@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"即可,均能够运行:
