spring @import注解

阅读数:174 评论数:0

跳转到新版页面

分类

python/Java

正文

一、概述

1、java注解

java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致。

2、spring 的@Import注解

@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));
    }

}

 




相关推荐

(1)Spring MVC是一个基于DispatcherServlet的MVC框架,DispatcherServlet是继承自HttpServlet的。Spring的IOC和AOP主要就用了java的

如果一个类交张spring管理,比如 &lt;bean id="foo1" class="demo.scope.foo" /&gt; 不指明scope就是单例。</p

@PathParam的声明允许你在URI路径中去映射你的方法将使用的参数。 @Path("/library") pu

方式1:通过@PostConstruct和@PreDestroy方法。 从Java EE5开始,Servlet增加了两个影响Servlet生命周期的注解。 方式2:通

一、概述 1、spring容器 spring中有三种bean容器:BeanFactory、ApplicationContext、WebApplicationContext。 (1)BeanFactor

有时我们在做开发的时候需要记录每个任务执行时间,或者记录一段代码执行时间,最简单的方法就是打印当前时间与执行完时间的差值,然后这样如果执行大量测试的话就很麻烦,并且不直观,如果想对执行的时间做进

一、request uri部分 @PathVariable 获取路径参数,形如url/{id} 二、request header部分 @RequestHeade

一、概述 springboot中有两种方式使用kafka时,直接使用kafka-client连接kafka服务;另一种是使用spring-kafka框架来连接kafka。 1、版本兼容 使用时要注意版

当然可以自己写redis的工具类,或者使用第三方开源jar包或代码,这里使用spring boot的集成类。 一、pom依赖 <dependency> <gro

websocket协议基于tcp的网络协议,它实现浏览器与器全双工通信。 spring boot2 +websocket 1、添加依赖 <pre clas