@import代码如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
@Import注解主要用于导入某些特殊的Bean,这些特殊的Bean和Bean Definitaion 有关。主要用于导入@Configuration 类,ImportSelector和ImportBeanDefinitionRegistrar接口的实现类 ,当然如果你有需要的话普通@component类同样也是可以导入的。
所提供的功能和Spring XML中的<import/>元素是一样的。
XML和其他非Bean definition源需要使用@ImportResource导入
导入@Configuration配置类
在Spring boot 中配置都一般都是自动导入的,所以我们不需要使用@Import,但是如果如果你自动扫包路径为:com.spring.example.app,而你想导入的配置类在com.spring.example.config下 ,那么该配置类就需要使用@Import导入,尤其是第三方jar包的配置类都需要借助@Import来导入
案列演示
目录结构
image.png
@Import(OutConfiguration.class)
@SpringBootApplication
@Log4j2
public class ImportExampleSpringBootApplication {
public static void main(String[] args) {
ConfigurableApplicationContext content = SpringApplication.run(ImportExampleSpringBootApplication.class, args);
Student student = (Student) content.getBean("student");
log.info("the name of the student is [{}]",student.getName());
}
}
public class Student {
protected String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@Configuration
public class OutConfiguration {
@Bean
public Student student(){
return new Student("inner");
}
}
OutConfiguration和ImportExampleSpringBootApplication在同级的不同目录下如果我们需要用到OutConfiguration配置类
那么我们要用 @import导入OutConfiguration类
代码输出:
[ main] com.spring.example.app.ImportExample : the name of the student is [inner]
ImportSelector导入配置类
@Component
@Log4j2
public class MyImportSelector implements ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*
* @param importingClassMetadata
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
importingClassMetadata.getAnnotationTypes().forEach(config->{
log.info(config);
});
return null;
}
}
如上所示:如果只是用@Component注释ImportSelector的实现类
MyImportSelector 该类的selectImports方法是不会被调用的
我们需要使用@import导入自动配置文件选择器
之前使用的是@Import(OutConfiguration.class)导入配置类
那么现在我们使用MyImportSelector 来导入OutConfiguration.class配置类
@Import({ MyImportSelector.class})
@SpringBootApplication
@Log4j2
public class ImportExampleSpringBootApplication {
public static void main(String[] args) {
ConfigurableApplicationContext content = SpringApplication.run(ImportExampleSpringBootApplication.class, args);
Student student = (Student) content.getBean("student");
log.info("the name of the student is [{}]",student.getName());
}
}
public class MyImportSelector implements ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*
* @param importingClassMetadata
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
importingClassMetadata.getAnnotationTypes().forEach(System.out::println);
return new String[]{OutConfiguration.class.getName()};
}
}
代码输出:
[ main] com.spring.example.app.ImportExample : the name of the student is [inner]
MyImportSelector中我们简单的返回了OutConfiguration所以最终效果和之前的案例效果是完全一样的
导入ImportBeanDefinitionRegistrar的实现类
ImportBeanDefinitionRegistrar 代码原文翻译
@Import({ MyImportBeanDefinitionRegistrar.class})
@SpringBootApplication
@Log4j2
public class ImportExampleSpringBootApplication {
public static void main(String[] args) {
ConfigurableApplicationContext content = SpringApplication.run(ImportExampleSpringBootApplication.class, args);
Student student = (Student) content.getBean("student");
log.info("the name of the student is [{}]",student.getName());
}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
*
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(Student.class.getName());
ConstructorArgumentValues arg = new ConstructorArgumentValues();
arg.addIndexedArgumentValue(0,"inner");
((GenericBeanDefinition) beanDefinition).setConstructorArgumentValues(arg);
registry.registerBeanDefinition("student",beanDefinition);
}
}
ImportBeanDefinitionRegistrar 注册器主要是在Spring boot 启动
阶段动态的向容器中注册Bean Definition 像Mybatis中的Mapper
就是通过实现BeanDefinitionRegistrar接口来自动注册到容器中的
代码如下
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
/**
* A {@link ImportBeanDefinitionRegistrar} to allow annotation configuration of
* MyBatis mapper scanning. Using an @Enable annotation allows beans to be
* registered via @Component configuration, whereas implementing
* {@code BeanDefinitionRegistryPostProcessor} will work for XML configuration.
*
* @author Michael Lanyon
* @author Eduardo Macarron
* @author Putthiphong Boonphong
*
* @see MapperFactoryBean
* @see ClassPathMapperScanner
* @since 1.2.0
*/
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
/**
* {@inheritDoc}
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(mapperScanAttrs, registry);
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
Optional.ofNullable(resourceLoader).ifPresent(scanner::setResourceLoader);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value"))
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("basePackages"))
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(
Arrays.stream(annoAttrs.getClassArray("basePackageClasses"))
.map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/**
* A {@link MapperScannerRegistrar} for {@link MapperScans}.
* @since 2.0.0
*/
static class RepeatingRegistrar extends MapperScannerRegistrar {
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScansAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
if (mapperScansAttrs != null) {
Arrays.stream(mapperScansAttrs.getAnnotationArray("value"))
.forEach(mapperScanAttrs -> registerBeanDefinitions(mapperScanAttrs, registry));
}
}
}
}
网友评论