使用Spring启动时运行自定义业务
在spring应用启动时运行自定义业务的场景很常见,但应用不当也可能会导致一些问题。
基于spring控制反转(inverse of control)功能用户几乎不用干预bean实例化过程,对于自定义业务则需要控制部分流程及容器,因此值得须特别关注。
1. spring启动时运行自定义业务
我们不能简单包括自定义业务在bean的构造函数或在实例化任何对象之后调用方法,这些过程不由我们控制。请看示例:
@component public class invalidinitexamplebean { @autowired private environment env; public invalidinitexamplebean() { env.getactiveprofiles(); } }
这里尝试在构造函数中访问自动装配的属性。当调用构造函数时,spring bean仍没有全部初始化,因此导致nullpointerexceptions异常。下面介绍几种方式解决此问题。
1.1 @postconstruct 注解
@postconstruct注解用于方法上,实现bean初始化后立刻执行一次。需要注意的是,即使没有对象注入,spring也会执行注解方法。
@component public class postconstructexamplebean { private static final logger log = logger.getlogger(postconstructexamplebean.class); @autowired private environment environment; @postconstruct public void init() { log.info(arrays.aslist(environment.getdefaultprofiles())); } }
上面示例可以实现environment environment被安全注入,然后调用注解方法且不会出现空指针异常。
1.2 initializingbean 接口
initializingbean接口实现功能与上节类似。但需要实现接口并重写afterpropertiesset方法。
下面重写前节的示例:
@component public class initializingbeanexamplebean implements initializingbean { private static final logger log = logger.getlogger(initializingbeanexamplebean.class); @autowired private environment environment; @override public void afterpropertiesset() throws exception { log.info(arrays.aslist(environment.getdefaultprofiles())); } }
1.3 applicationlistener 监听器
该方法可用于在spring上下文初始化之后执行自定义业务。因此不针对特定bean,而是等待所有bean初始化之后。应用时需要实现applicationlistener接口:
@component public class startupapplicationlistenerexample implements applicationlistener<contextrefreshedevent> { private static final logger log = logger.getlogger(startupapplicationlistenerexample.class); public static int counter; @override public void onapplicationevent(contextrefreshedevent event) { log.info("increment counter"); counter++; } }
同样可以引入@eventlistener注解实现:
@component public class eventlistenerexamplebean { private static final logger log = logger.getlogger(eventlistenerexamplebean.class); public static int counter; @eventlistener public void onapplicationevent(contextrefreshedevent event) { log.info("increment counter"); counter++; } }
上面示例使用contextrefreshedevent,具体选择哪种事件根据你的业务需要。
1.4 @bean的初始化方法
该注解的initmethod属性可用于在bean初始化之后执行方法,示例:
public class initmethodexamplebean { private static final logger log = logger.getlogger(initmethodexamplebean.class); @autowired private environment environment; public void init() { log.info(arrays.aslist(environment.getdefaultprofiles())); } }
既不要实现接口,也不要特定注解。通过注解定义bean:
@bean(initmethod="init") public initmethodexamplebean initmethodexamplebean() { return new initmethodexamplebean(); }
对应xml配置:
<bean id="initmethodexamplebean" class="com.baeldung.startup.initmethodexamplebean" init-method="init"> </bean>
1.5 构造函数注入
如果使用构造器注入属性,可以简单地在构造函数中包括业务:
@component public class logicinconstructorexamplebean { private static final logger log = logger.getlogger(logicinconstructorexamplebean.class); private final environment environment; @autowired public logicinconstructorexamplebean(environment environment) { this.environment = environment; log.info(arrays.aslist(environment.getdefaultprofiles())); } }
1.6 spring boot commandlinerunner接口
spring boot 提供了commandlinerunner接口,重写run方法,可以在应用启动时spring应用上下文实例化之后调用。
@component public class commandlineappstartuprunner implements commandlinerunner { private static final logger log = loggerfactory.getlogger(commandlineappstartuprunner.class); public static int counter; @override public void run(string...args) throws exception { log.info("increment counter"); counter++; } }
commandlinerunner bean在相同上下文中可以定义多个,通过使用ordered 接口或@ordere注解确定顺序。
1.7 spring boot applicationrunner
与commandlinerunner类似,spring boot 也提供了applicationrunner接口,重写run方法可以实现应用启动时执行自定义业务。另外其回调方法没有使用string参数,而是使用applicationarguments类的实例。
applicationarguments有方法可以获取可选参数及普通参数的值,参数前有–的表示可选参数。
@component public class appstartuprunner implements applicationrunner { private static final logger log = loggerfactory.getlogger(appstartuprunner.class); public static int counter; @override public void run(applicationarguments args) throws exception { log.info("application started with option names : {}", args.getoptionnames()); log.info("increment counter"); counter++; } }
2. 执行顺序
多种方法对bean同时进行控制,对应执行顺序如下:
- 构造函数
- @postconstruct注解方法
- initializingbean的afterpropertiesset()
- @bean或xml中标注的初始化方法
读者可以自行测试进行验证。
3. 总结
本文介绍多种方式实现在spring启动时实现自定义业务。通过对比不同方式实现加深对spring的理解,掌握更多控制bean实例化过程的方式。以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
推荐阅读
-
详解使用Spring AOP和自定义注解进行参数检查
-
[Spring Boot]使用自定义注解统一请求返回值
-
spring boot使用自定义的线程池执行Async任务
-
Spring-Boot使用嵌入式容器,那怎么配置自定义Filter呢
-
Spring Boot使用嵌入式容器,那怎么配置自定义Filter呢
-
使用Spring启动时运行自定义业务
-
Spring Boot 中使用自定义注解,AOP 切面打印出入参日志及Dubbo链路追踪透传traceId
-
SpringBoot使用logback自定义配置时遇到的坑 --- 在 /tmp目录下自动生成spring.log文件
-
Spring Boot 自定义 Shiro 过滤器,无法使用 @Autowired 解决方法
-
使用springmvc运行流程分析,手写spring框架尝试