Spring 与自定义注解、外部配置化的结合使用

一、Java注解的简单介绍

注解,也叫Annotation、标注,是 Java 5 带来的新特性。

可使用范围

类、字段、方法、参数、构造函数、包等,具体可参阅枚举类 java.lang.annotation.ElementType

生命周期(摘自 刘大飞的博客 )

RetentionPolicy.SOURCE 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃

RetentionPolicy.CLASS 注解被保留到class文件,但 jvm 加载class文件时候被遗弃,这是默认的生命周期

RetentionPolicy.RUNTIME 注解不仅被保存到class文件中,jvm 加载class文件之后,仍然存在

使用方式

可以使用反射获取注解的内容,具体如何使用请自己百度,可参考这篇Java注解完全解析,这里不是重点,不多做介绍

二、Spring的 @Import注解

@Import 注解是Spring用来注入 Spring Bean 的一种方式,可以用来修饰别的注解,也可以直接在Springboot配置类上使用。

它只有一个value属性需要设置,来看一下源码

public @interface Import {

Class>[] value();

}

这里的 value属性只接受三种类型的Class:

被@Configuration 修饰的配置类

接口org.springframework.context.annotation.ImportBeanDefinitionRegistrar的实现类

接口org.springframework.context.annotation.ImportSelector的实现类

下面针对三种类型的Class分别做简单介绍,中间穿插自定义注解与外部配置的结合使用方式。

三、被@Configuration 修饰的配置类

像 Springboot 中的配置类一样正常使用,需要注意的是,如果该类的包路径已在Springboot启动类上配置的扫描路径下,则不需要再重新使用@Import导入了,因为@Import的目的是注入bean,但是Springboot启动类自动扫描已经可以注入你想通过@Import导入的bean了。

这种Class可以进行如下拓展

继承各种Aware接口, 获取对应的信息(如果不清楚Aware接口在Spring当中的作用,请自行百度),如,继承EnviromentAware,可以拿到Spring的环境配置信息,进而从中拿到 @Value所需要的值,如 environment.getProperty("user.username")

使用@Autowire、@Resource、@Value注入各种所需Spring 资源

像普通Spring Bean 一样使用该类

更多使用方式,请自行百度。

四、接口org.springframework.context.annotation.ImportBeanDefinitionRegistrar的实现类

当@Import修饰自定义注解时候,通常会导入这种类。

来看一下接口定义

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 信息。

来看如下案例,我们通过一个注解,启动RocketMq的消息发送器:

@SpringBootApplication

@EnableMqProducer(group="xxx")

public class App {

public static void main(String[] args) {

SpringApplication.run(App.class);

}

}

这是一个服务项目的启动类,这个服务开启了RocketMq的一个发送器,并且分到xxx组里。

来下一下@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.ImportBeanDefinitionRegistrar的实现类,第二个是被@Configuration 修饰的配置类

我们看第一个类,这个类注入了一个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 values = new HashMap<>();

//这里有的同学可能不清楚为什么key是这个

//这里的key就是DefaultMQProducer的字段名

values.put("producerGroup", group);

//省略部分代码

//注册到Spring中

BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, DefaultMQProducer.class.getName(), DefaultMQProducer.class, values);

}

到这里,我们已经注入了一个DefaultMQProducer的实例到Spring容器中,但是这个实例,还不完整,比如,还没有启动,nameServer地址还没有配置,可外部配置的属性还没有覆盖实例已有的值(nameServer地址建议外部配置)。好消息是,我们已经可以通过注入来使用这个实例了。

上面遗留的问题,就是第二个类接下来要做的事。

来看第二个配置类

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

}

到这里,通过自定义注解和外部配置的结合,一个完整的消息发送器就可以使用了,但方式有取巧之嫌,因为在消息发送器启动之前,不知道还有没有别的类使用了这个实例,这是不安全的。

五、接口org.springframework.context.annotation.ImportSelector的实现类

首先看一下接口

public interface ImportSelector {

/**

* importingClassMetadata 注解元信息,可获取自定义注解的属性集合

* 根据自定义注解的属性,或者没有属性,返回要注入Spring的Class全限定类名集合

如:XXX.class.getName(),Spring会自动注入XXX的一个实例

*/

String[] selectImports(AnnotationMetadata importingClassMetadata);

@Nullable

default Predicate getExclusionFilter() {

return null;

}

}

这个接口的实现类如果没有进行@Aware拓展,功能比较单一,因为我们无法参与Spring Bean 的构建过程,只是告诉Spring 要注入的Bean的名字。不再详述。

六、总结

通过接口和配置类的灵活结合,可以实现自定义注解的配置化设计,归根到底是Spring Bean的灵活构建,如果你有更好更优雅的方式,欢迎留言指教。

java 外部覆盖内部配置,Spring 与自定义注解、外部配置化的结合使用相关推荐

  1. spring使用自定义注解_用Spring组成自定义注释

    spring使用自定义注解 Java批注在2004年随Java 5一起引入,是一种将元数据添加到Java源代码中的方法. 如今,许多主要框架(如Spring或Hibernate)都严重依赖注释. 在本 ...

  2. spring AOP自定义注解方式实现日志管理

    转:spring AOP自定义注解方式实现日志管理 今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接 ...

  3. Spring 自定义注解,配置简单日志注解

    java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致. 下面会讲解Spring中自定义注解的简单流程,其中会涉及到spring框架中的AOP(面向切面编程)相关概念. ...

  4. spring中自定义注解(annotation)与AOP中获取注解___使用aspectj的@Around注解实现用户操作和操作结果日志

    spring中自定义注解(annotation)与AOP中获取注解 一.自定义注解(annotation) 自定义注解的作用:在反射中获取注解,以取得注解修饰的类.方法或属性的相关解释. packag ...

  5. spring 扫描自定义注解

    目录 一:定义自定义注解 二:使用自定义注解 3.1 定义使用注解的测试文件路径 3.2继承BeanPostProcessor增加spring扫描配置 四:测试 目录结构: 一:定义自定义注解 pac ...

  6. 使用Java反射(Reflect)、自定义注解(Customer Annotation)生成简单SQL语句

    使用Java反射(Reflect).自定义注解(Customer Annotation)生成简单SQL语句 这次给大家介绍一下在Java开发过程中 使用自定义注解开发: 主要知识点:          ...

  7. spring aop 自定义注解配合swagger注解保存操作日志到mysql数据库含(源码)

    spring aop 自定义注解保存操作日志到mysql数据库 一.思路 二.自定义注解 三.编写操作日志 四.编写操作日志切面\增强 五.使用 六.`注意` 一.思路 利用spring aop 对方 ...

  8. Spring定时任务@Scheduled注解使用配置方式(cron表达式、fixedRate和fixedDelay)

    Spring定时任务@Scheduled注解使用配置方式(cron表达式.fixedRate和fixedDelay) 序言: 个人推荐一个很方便的在线Cron生成器(网页版):https://qqe2 ...

  9. Java之——Spring AOP自定义注解实现日志管理

    转载请注明出处:https://blog.csdn.net/l1028386804/article/details/80295737 1.定义日志类SystemLog package io.mykit ...

最新文章

  1. 斗图必备!AI实战各种沙雕表情包分门别类~
  2. leetcode算法题--Can I Win
  3. Nginx - request_time和upstream_response_time的区别
  4. 实现一个压缩Remoting传输数据的Sink:CompressionSink
  5. 飞畅 Profibus总线光纤中继器产品介绍
  6. pom添加mysql依赖tomcat崩溃_Spring Boot + Mybatis + Spring MVC环境配置(一) :Spring Boot初始化,依赖添加...
  7. [Leedcode][JAVA][面试题 08.11][硬币][动态规划]
  8. Base64转PDF、PDF转IMG(使用pdfbox插件)
  9. 关于Android工程师转成vue的三两事儿(4)--webpack
  10. [android开发IDE]adt-bundle-windows-x86的一个bug:无法解析.rs文件--------rs_core.rsh file not found...
  11. 平板集热器的集热量和热效率计算的Python程序
  12. app账号退不出去_2021个人所得税退税时间是什么时候?还可以退吗?
  13. 前端快速切图:PS切图常用快捷键、精准测量尺寸
  14. PostgreSQL数据库备份还原全攻略
  15. 联想IdeapadU410重装系统win10
  16. Codeforces 686D. Kay and Snowflake
  17. 云服务器有什么用途?新手如何使用云服务器
  18. win10开始菜单打不开,使用startmenu.diagcab进行修复
  19. Python通过机器学习实现对个人信用评估
  20. 【论文笔记】强化学习论文阅读-Model-Based RL 9篇

热门文章

  1. 9. 混合模型和EM(2)
  2. python电路模型编程_14、python开发之路-并发编程之I/O模型
  3. Linux循环链表删除节点,删除循环单链表开头元素
  4. java检测安卓程序退出_java-我的Android应用程序的gameLoop线程在退出...
  5. 5.3 核函数-机器学习笔记-斯坦福吴恩达教授
  6. 【PC工具】更新常用USB转串口芯片MAC驱动,CH340G驱动安装有可能遇到的问题及解决办法...
  7. ScrollView child layout ([alignItems]) 错误
  8. 深入理解volatile
  9. 网站压力测试工具webbench简介、安装、使用
  10. 转Java 理论与实践: 正确使用 Volatile 变量