-
背景
-
@Import 作用
-
作用对象
-
直接注入
-
注入 @Configuration 类
-
导入实现 ImportBeanDefinitionRegistrar接口的类
-
导入实现ImportSelector 接口的类
背景
最近在写Spring Boot 相关的sdk,有这么一个需求,就是在某个Bean存在的时候才注入或者开启某个配置,最简单的例子就是我们需要项目配置了Redission才启动分布式锁相关的AOP切面配置,那么如何实现呢,最简单的方式使用使用@ConditionalOnBean
注解,但是发现@ConditionalOnBean
注解失效了,原因是@ConditionalOnBean
依赖的Bean后加载了,所以就打算使用@Import
注解,在此之前就简单研究了下@Import
注解的用法
@Import 作用
将指定的类实例注入之Spring IOC 容器中
作用对象
-
直接注入 -
实现 ImportBeanDefinitionRegistrar 接口 注入 -
实现 ImportSelector 注入
直接注入
public class A {
public A() {
System.out.println("init A");
}
}
public class B {
public B() {
System.out.println("init B");
}
}
@Component
@Import({A.class,B.class})
public class TestImport {
}
这样就直接注入了,和这样写的方式一样
@Component
public class A {
public A() {
System.out.println("init A");
}
}
@Component
public class B {
public B() {
System.out.println("init B");
}
}
一般不会使用这种方式
注入 @Configuration 类
@Configuration
public class ConfigurationTest {
@Bean
public A a() {
return new A();
}
@Bean
public B b() {
return new B();
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ConfigurationTest.class)
public @interface EnableImport {
}
这种方式主要用于写三方sdk的时候开启某个功能,然后导入一些配置的Bean自动加载,比如EnableFeignClients
注解
导入实现 ImportBeanDefinitionRegistrar接口的类
我们还是以注册BeanA.class
B.class
为例
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 注册A
BeanDefinitionBuilder beanDef_A = BeanDefinitionBuilder.rootBeanDefinition(A.class);
registry.registerBeanDefinition("A", beanDef_A.getBeanDefinition());
// 注册B
BeanDefinitionBuilder beanDef_B = BeanDefinitionBuilder.rootBeanDefinition(B.class);
registry.registerBeanDefinition("B", beanDef_B.getBeanDefinition());
}
}
@Import(MyImportBeanDefinitionRegistrar.class)
@Configuration
public class TestImportBeanDefinitionRegistrar {
}
这样就注册了A
B
或者基于注解的方式导入行
-
EnableTest
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface EnableTest {
}
-
Application
@SpringBootApplication
@EnableTest
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
我们可以看看Spring Cloud
Feign的自动装配就是用这种方式注入的
我们要开启Feign
的自动装配只需要在启动类添加@EnableFeignClients
我们看看@EnableFeignClients
源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}
可以看到导入了配置类FeignClientsRegistrar.class
,而FeignClientsRegistrar.class
就实现了ImportBeanDefinitionRegistrar
接口
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
}
}
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
}
导入实现ImportSelector 接口的类
与上面类似
-
TestImportSelector.java 实现 ImportSelector
接口
public class TestImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 获取 注解上 EnableTest 的Class 信息 并注册
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableTest.class.getName());
assert annotationAttributes != null;
Class<?>[] clazz = (Class<?>[])annotationAttributes.get("clazz");
return Arrays.stream(clazz).map(Class::getName).toArray(String[]::new);
}
}
注解导入
-
EnableTest.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(TestImportSelector.class)
public @interface EnableTest {
Class[] clazz() default {};
}
然后在启动类添加注解
@SpringBootApplication
@EnableTest(clazz = {A.class, B.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这样就注册了A.class B.class
原文始发于微信公众号(小奏技术):想写Spring Boot SDK?先深入学习下@Import 注解吧
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30160.html