spring @import注解
阅读数:232 评论数:0
跳转到新版页面分类
python/Java
正文
一、概述
1、java注解
java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致。
@Import注解是Spring用来注入Spring Bean的一种方式,可以用来修饰别的注解,也可以直接在Springboot配置类上使用。
它只有一个value属性需要设置。
public @interface Import {
Class<?>[] value();
}
这里的value属性只接受三种类型的Class:
被@Configuration修改的配置类 |
ImportBeanDefintionRegistar接口的实现类 |
ImportSelector的实现类 |
二、@Import+被@Configuration修饰的配置类
像Springboot中的配置类一样正常使用,需要注意的是,如果该类的包路径已在Springboot启动类上配置的扫描路径上,则不需要重新使用@Import导入了,因为@Import的目的是注入bean,但是Springboot启动类自动扫描已经可以注入你想通过@Import导入的bean了。
这种Class可以进行如下拓展:
(1)继承各种Aware接口,获取对应的信息,如继承EnvironmentAware,可以拿到Spring的环境配置信息,进而从中拿到@Value所需要的值,如environment.getProperty("user.username")
(2)使用@Autowire、@Resource、@Value注入各种所需Spring资源。
(3)像普通Spring Bean一样使用该类。
三、@Import+接口ImportBeanDefintionRegistar的实现类
当@Import修饰自定义注解时候,通常会导入这种类。
1、ImportBeanDefinitionRegistratr接口定义:
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
/**
* importingClassMetadata 被@Import修饰的自定义注解的元信息,可以获得属性集合
* registry Spring bean注册中心
**/
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
通过这种方式,我们可以根据自定义注解配置的属性值来注入Spring Bean信息。
2、案例
我们通过一个注解,启动RocketMq的消息发送器。
@SpringBootApplication
@EnableMqProducer(group="xxx")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
这是一个服务项目的启动类,这个服务开启了RocketMq的一个发送器,并且分到xxx组里。
(1)@Import修饰@EnableMqProducer注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({XXXRegistrar.class,XXXConfig.class})
public @interface EnableMqProducer {
String group() default "DEFAULT_PRODUCER_GROUP";
String instanceName() default "defaultProducer";
boolean retryAnotherBrokerWhenNotStoreOK() default true;
}
这里使用@Import导入了两个配置类,第一个是接口org.springframework.context.annotation.ImportBeanDefintionRegistrar的实现类,第二个是被@Configuration修饰的配置类。
(2)ImportBeanDefinitionRegistrar实现类
这个类注入了一个DefaultMQProducer到Spring容器中,使业务方可以直接通过@Autowired注入使用。
public class XXXRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableMqProducer.class.getName()));
registerBeanDefinitions(attributes, registry);
}
private void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {
//获取配置
String group = attributes.getString("group");
//省略部分代码...
//添加要注入的类的字段值
Map<String, Object> values = new HashMap<>();
//这里有的同学可能不清楚为什么key是这个
//这里的key就是DefaultMQProducer的字段名
values.put("producerGroup", group);
//省略部分代码
//注册到Spring中
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, DefaultMQProducer.class.getName(), DefaultMQProducer.class, values);
}
到这里,我们已经注入了一个DefaultMQProducer的实例到Spring容器中,但是这个实例,还不完整,比如,还没有启动,nameServer地址还没有配置,外部配置的属性没有覆盖实例已有的值(nameServer地址建议外部配置)。
(3)配置类
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@EnableConfigurationProperties(XxxProperties.class) //Spring提供的配置自动映射功能,配置后可直接注入
public class XXXConfig {
@Resource //直接注入
private XxxProperties XxxProperties;
@Autowired //注入上一步生成的实例
private DefaultMQProducer producer;
@PostConstruct
public void init() {
//省略部分代码
//获取外部配置的值
String nameServer = XxxProperties.getNameServer();
//修改实例
producer.setNamesrvAddr(nameServer);
//启动实例
try {
this.producer.start();
} catch (MQClientException e) {
throw new RocketMqException("mq消息发送实例启动失败", e);
}
}
@PreDestroy
public void destroy() {
producer.shutdown();
}
四、接口ImportSelector的实现类
1、接口定义
public interface ImportSelector {
/**
* importingClassMetadata 注解元信息,可获取自定义注解的属性集合
* 根据自定义注解的属性,或者没有属性,返回要注入Spring的Class全限定类名集合
如:XXX.class.getName(),Spring会自动注入XXX的一个实例
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
当配置类实现了 ImportSelector 接口的时候,就会调用 selectImports 方法的实现,获取一批类的全限定名,最终这些类就会被注册到Spring容器中。
2、使用示例
public class UserImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
System.out.println("调用 UserImportSelector 的 selectImports 方法获取一批类限定名");
return new String[]{"com.sanyou.spring.extension.User"};
}
}
// @Import 注解导入 UserImportSelector
@Import(UserImportSelector.class)
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//将 Application 注册到容器中
applicationContext.register(Application.class);
applicationContext.refresh();
System.out.println("获取到的Bean为" + applicationContext.getBean(User.class));
}
}