@Import整合第三方框架原理

@Import整合第三方框架原理

一、概述

Spring 与 MyBatis 注解方式整合有个重要的技术点就是 @Import,第三方框架与 Spring 整合 xml 方式很多是凭借自定义标签完成的,而第三方框架与 Spring 整合注解方式很多是靠 @Import 注解完成的。

@Import 可以导入如下三种类:

  • 普通的配置类
  • 实现 ImportSelector 接口的类
  • 实现 ImportBeanDefinitionRegistrar 接口的类

二、实验

普通的配置类的方式前面已经演示过了,可以看这篇文章:https://blog.csdn.net/2301_80749359/article/details/158066852?fromshare=blogdetail&sharetype=blogdetail&sharerId=158066852&sharerefer=PC&sharesource=2301_80749359&sharefrom=from_link,在这里就就不再演示了,直接从第二种开始。

2.1实现 ImportSelector 接口的类

1.创建类实现ImportSelector 接口(代码里面有注释了就不再详细解释了):

这里以注册OtherBean2为例

java 复制代码
package com.itheima.imports;

import com.itheima.beans.OtherBean2;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Map;

public class MyImportSelector2 implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
//        返回的数组是要注册到Spring的那些Bean的全限制定名
        return new String[]{OtherBean2.class.getName()};
    }
}

2.在配置类中导入上面的类:

java 复制代码
@Import(MyImportSelector2.class)

测试:

java 复制代码
package com.itheima;

import com.itheima.beans.OtherBean2;
import com.itheima.config.SpringConfig2;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl3;
import com.itheima.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.sql.DataSource;

public class ApplicationContextTest3 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContextTest2 = new AnnotationConfigApplicationContext(SpringConfig2.class);
       Object object2 = applicationContextTest2.getBean(OtherBean2.class.getName());
        System.out.println(object2);

    }

}

结果:

同时这里要进行拓展的是AnnotationMetadata,AnnotationMetadata叫做注解媒体数组,封装的是@Import类上的其他注解的信息,例如:

java 复制代码
package com.itheima.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.beans.OtherBean3;
import com.itheima.imports.MyImportSelector2;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

import javax.sql.DataSource;
@Import(MyImportSelector2.class)
//这个注解代表:这是一个核心配置类;这个类交给了Spring容器管理(就是相当于替换Spring配置文件)
@Configuration
//<context:component-scan base-package="com.itheima"/>:配置注解扫描,只有一个时直接写,有多个写成数组
@ComponentScan("com.itheima")
//<context:property-placeholder location="classpath:jdbc.properties"/>:引入配置文件
@PropertySource("classpath:jdbc.properties")
// <import resource="":导入其他配置类
//@Import(OtherBean3.class)
//下面是接口扫描
// <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
//<!--        指定要扫描的包-->
//        <property name="basePackage" value="com.itheima.mapper"></property>
//    </bean>
@MapperScan("com.itheima.mapper")
public class SpringConfig2 {
    @Value("${jdbc.driver}")
    String driverClassName;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUsername( username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        return dataSource;
    }
    @Bean
//    在这里是通过类型进行注入的
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
//        SqlSessionFactoryBean sqlSessionFactoryBean = sqlSessionFactoryBean(dataSource);
        SqlSessionFactoryBean sqlSessionFactoryBean =new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
}

这里SpringConfig2类上使用了@Import注解,那么AnnotationMetadata存储的是SpringConfig2类上的其他注解的信息,加入我们来获取@ComponentScan注解的元信息:

java 复制代码
package com.itheima.imports;

import com.itheima.beans.OtherBean2;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Map;

public class MyImportSelector2 implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
//        AnnotationMetadata叫做注解媒体数组,封装的是@Import上的其他注解的信息
//        定义一个Map来获取@Import注解上的其他注解的信息
        Map<String,Object>anootationAtrributes = annotationMetadata.getAnnotationAttributes(ComponentScan.class.getName());
//        return new String[0];
        anootationAtrributes.forEach((AtrributeName,AtrributeValue)->{
            System.out.println(AtrributeName+"=="+AtrributeValue);
        });
//        String[] basePackages = (String[]) anootationAtrributes.get("basePackages");
//        System.out.println(basePackages[0]);
//        返回的数组是要注册到Spring的那些Bean的全限制定
        return new String[]{OtherBean2.class.getName()};
    }
}

结果:

那么这样用什么用呢?当我们不想配置xml来注册Bean时可以使用这种方式了注册。具体在实际中的应用就是整合第三方框架,因为别人的框架都是配好的,我们只能通过这种形式了注册Bean,要么就是像上面一样自己写好一个实现ImportSelector 接口的类,使用@Import来进行导入,要么就像@MapperScan注解一样将@Import封装在某个注解里面。

java 复制代码
Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
    String[] value() default {};

2.2实现 ImportBeanDefinitionRegistrar 接口的类

1.定义类实现ImportBeanDefinitionRegistrar接口:

java 复制代码
package com.itheima.imports;

import com.itheima.beans.OtherBean2;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
//        直接在这里定义BeanDefinition,然后再注册
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName(OtherBean2.class.getName());
        registry.registerBeanDefinition("otherBean2",beanDefinition);
    }
}

底层是使用BeanDefinitionRegistry的registerBeanDefinition方法直接注册BeanDefinition进入BeanDefinitionMap当中。

2.在核心配置类中导入实现 ImportBeanDefinitionRegistrar 接口的类:

java 复制代码
package com.itheima.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.beans.OtherBean3;
import com.itheima.imports.MyImportSelector2;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

import javax.sql.DataSource;
@Import(MyImportBeanDefinitionRegistrar2.class)
//这个注解代表:这是一个核心配置类;这个类交给了Spring容器管理(就是相当于替换Spring配置文件)
@Configuration
//<context:component-scan base-package="com.itheima"/>:配置注解扫描,只有一个时直接写,有多个写成数组
@ComponentScan("com.itheima")
//<context:property-placeholder location="classpath:jdbc.properties"/>:引入配置文件
@PropertySource("classpath:jdbc.properties")
// <import resource="":导入其他配置类
//@Import(OtherBean3.class)
//下面是接口扫描
// <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
//<!--        指定要扫描的包-->
//        <property name="basePackage" value="com.itheima.mapper"></property>
//    </bean>
@MapperScan("com.itheima.mapper")
public class SpringConfig2 {
    @Value("${jdbc.driver}")
    String driverClassName;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUsername( username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        return dataSource;
    }
    @Bean
//    在这里是通过类型进行注入的
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
//        SqlSessionFactoryBean sqlSessionFactoryBean = sqlSessionFactoryBean(dataSource);
        SqlSessionFactoryBean sqlSessionFactoryBean =new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
}

测试:

java 复制代码
package com.itheima;

import com.itheima.beans.OtherBean2;
import com.itheima.config.SpringConfig2;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl3;
import com.itheima.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.sql.DataSource;

public class ApplicationContextTest3 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContextTest2 = new AnnotationConfigApplicationContext(SpringConfig2.class);
       Object object2 = applicationContextTest2.getBean("otherBean2");
        System.out.println(object2);

    }

}

当然也可以模仿将@MapperScan注解,将@Import封装于一个注解当中:

java 复制代码
package com.itheima.anno;

import com.itheima.imports.MyImportBeanDefinitionRegistrar2;
import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Import(MyImportBeanDefinitionRegistrar2.class)
public @interface MyMapperScan2 {
}

核心配置类中代码将"@Import(MyImportBeanDefinitionRegistrar2.class)"改为"@MyMapperScan2"即可,均能够运行:

相关推荐
沐知全栈开发1 小时前
Python File 方法详解
开发语言
坚持就完事了2 小时前
Java实现数据结构中的链表
java·数据结构·链表
写代码的小球2 小时前
C++ 标准库 <numbers>
开发语言·c++·算法
拳里剑气2 小时前
C++:哈希
开发语言·数据结构·c++·算法·哈希算法·学习方法
玩具猴_wjh2 小时前
JWT优化方案
java·服务器·数据库
坚持就完事了2 小时前
Java各种命名规则
java·开发语言
白露与泡影2 小时前
2026年Java面试题精选(涵盖所有Java核心面试知识点),立刻收藏
java·开发语言
瓦特what?2 小时前
冒 泡 排 序
开发语言·数据结构·c++
wjs20242 小时前
TypeScript 变量声明
开发语言