文章目录
1、@PostConstruct 注解
2、spring 的指定init-method属性指定方法
3、实现 InitializingBean 接口,重写afterPropertiesSet()
4、实现ApplicationListener接口,重写onApplicationEvent()
5、各种接口综合使用
6、使用 定时器 quartz
6.1、quartz的定时器
6.2、Spring task任务调度 (推荐)
7、执行顺序
springmvc 容器启动后,要执行一些工作。
1、@PostConstruct 注解
@PostConstruct 是在对象构造完成,并且属性赋值完成后,调用 init-method 之前执行的。
如果你在 applicationContext.xml 中配置default-lazy-init="true" 延迟初始化,意味着容器刚启动,如果这个bean没有被调用,是不会初始化的,自然也就不会调用@PostConstruct 的方法。
注意:
1,把 @PostConstruct 放到要执行实始化的方法上面。
2,Service(如AppDicService) 必须注入到 Spring容器中才行。
package com.aop8.springmvc2.initload;
import javax.annotation.PostConstruct;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import com.aop8.springmvc2.service.UserService;
@Component
public class AppDicService{ @Autowired
private UserService xxxService;
/**
* Spring 启动完成后,会自动调用此方法
*/
@PostConstruct
public void initAppDicCache(){
//执行加载操作,
// 比如 xxxService.getList()
System.out.println("@PostConstruct");
xxxService.print();
}
}
2、spring 的指定init-method属性指定方法
Spring在初始化bean时,在对象构造完成后,会调用 init-method 和 destroy-method属性中指定的方法。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >
<--
scope 的值一定要是 singleton
inti-method 是指要执行的方法
-->
<bean id="startRun" class="com.aop8.springmvc2.initload.StartRun" scope="singleton" init-method="testInit" />
</beans>
package com.aop8.springmvc2.initload;public class StartRun {
public void testInit(){
System.out.println("开始执行 init-method() ");
}
}
3、实现 InitializingBean 接口,重写afterPropertiesSet()
spring很多组建的初始化都放在afterPropertiesSet 里。
我们在做一些中间件想和spring一起启动,可以放在这里启动。
package com.aop8.springmvc2.initload;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;@Component
public class ArraignedLogService implements InitializingBean { @Override
public void afterPropertiesSet() throws Exception {
System.out.println("通过实现接口 initializingBean,调用afterPropertiesSet()方法 来执行的"); }
}
4、实现ApplicationListener接口,重写onApplicationEvent()
使用场景
在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载、初始化缓存、特定任务的注册等等。这个时候我们就可以使用Spring提供的ApplicationListener来进行操作。
遇到的问题
在spring中可以通过ApplicationListener来实现相关的功能,加载完成后触发contextrefreshedevent事件(上下文件刷新事件)。
但是这个时候,会存在一个问题,在web 项目中(spring mvc),系统会存在两个容器,一个是root application context,另一个就是我们自己的 projectName-servlet context(作为 root application context 的子容器)。
这种情况下,就会造成 onApplicationEvent 方法被执行两次。
为了避免上面提到的问题,我们可以只在 root application context 初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理,修改后代码。
解决方法:
if(event.getApplicationContext().getParent() == null){//root application context 没有parent
//TODO 这里写下将要初始化的内容
}
if(event.getApplicationContext().getDisplayName().equals("Root WebApplicationContext")){}
示例:
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import com.xx.service.DemoService;
@Component
public class InitBeanTest implements ApplicationListener<ContextRefreshedEvent> {
@Resource
DemoService demoService;
//构造函数
public InitBeanTest() {
System.err.println("----> InitBeanTest: constructor: "+demoService);
}
//实现 ApplicationListener 接口,必须重写的方法
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("ApplicationListener:onApplicationEvent");
// demoService.testMethod();
if(event.getApplicationContext().getParent() == null){//root application context 没有parent
//TODO 这里写下将要初始化的内容
}
if(event.getApplicationContext().getDisplayName().equals("Root WebApplicationContext")){
}
}
}
执行结果:
----> InitBeanTest: constructor: null
----> ApplicationListener: onApplicationEvent
----> ApplicationListener: onApplicationEvent
分析:
onApplicationEvent 出现了两次,为什么?因为bean注入了DemoService,spring容器会被刷新。
换言之onApplicationEvent会被频繁执行,需要使用它监听,需要考虑性能问题。
@Service 也可以改成使用 @Component 等 spring 注解。
如果不想使用@Service 注解,也可以在 applicationContext.xml 容器中配置bean 。
5、各种接口综合使用
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import com.xx.service.DemoService;
@Component
public class InitBeanTest implements InitializingBean,ApplicationListener<ContextRefreshedEvent> {
@Resource
DemoService demoService;
//构造函数
public InitBeanTest() {
System.err.println("----> InitBeanTest: constructor: "+demoService);
}
@PostConstruct
public void postConstruct() {
System.err.println("----> postConstruct: "+demoService);
}
//实现 InitializingBean 接口,必须重写的方法
@Override
public void afterPropertiesSet() throws Exception {
System.err.println("----> InitializingBean : afterPropertiesSet: "+demoService);
}
//实现 ApplicationListener 接口,必须重写的方法
@Override
public void onApplicationEvent(ContextRefreshedEvent arg0) {
System.err.println("----> ApplicationListener: onApplicationEvent");
}
}
执行结果:
----> InitBeanTest: constructor: null
----> postConstruct: com.yiniu.kdp.service.impl.DemoServiceImpl@40fe544
----> InitializingBean : afterPropertiesSet: com.aa.DemoServiceImpl@40fe544
----> ApplicationListener: onApplicationEvent
----> ApplicationListener: onApplicationEvent
分析:
1、构造函数是每个类最先执行的,这个时候,bean属性还没有被注入
2、postConstruct优先于afterPropertiesSet执行,这时属性竟然也被注入了,有点意外
3、spring很多组建的初始化都放在afterPropertiesSet做。我们在做一些中间件想和spring一起启动,可以放在这里启动。
4、onApplicationEvent属于应用层的时间,最后被执行,很容易理解。注意,它出现了两次,为什么?因为bean注入了DemoService,spring容器会被刷新。
换言之onApplicationEvent会被频繁执行,需要使用它监听,需要考虑性能问题。
@Service 也可以改成使用 @Component 等 spring 注解。
如果不想使用@Service 注解,也可以在 applicationContext.xml 容器中配置bean 。
6、使用 定时器 quartz
6.1、quartz的定时器
InitLoadDataQuartzJob 类省略。
下面直接看配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" >
<bean id="initLoadData" class="xx.xx.xx.InitLoadDataQuartzJob"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="initLoadDataDetail" />
<property name="targetMethod" value="loadMethod" />
<property name="concurrent" value="false" />
</bean>
<!-- 项目启动后任务就执行一次 -->
<bean id="initLoadDataDetailTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="initLoadDataDetail"/>
<property name="startDelay" value="500"/>
<property name="repeatInterval" value="0"/>
<property name="repeatCount" value="0"/>
<!--
<property name="cronExpression" value="0 0,30 8-17 * * ?"/>
-->
</bean>
<bean id="startQuertz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="initLoadDataDetailTrigger"/>
</list>
</property>
</bean>
</beans>
6.2、Spring task任务调度 (推荐)
task是Spring自带的一个设定时间自动任务调度。
Service类:
/**
* 定时计算
*/
@Component("profitScheduler")
public class ProfitScheduler { @Autowired
private XXXService xxxService;
@Scheduled
public void execute() {
logger.info("start 执行定时任务");
try {
xxxService.testMethod();
} catch (Exception e) {
logger.error("执行异常。{}",e);
e.printStackTrace();
}
logger.info("end 执行定时任务");
}
}
1、@Component 将类注入到Spring容器中;
2、在要执行的方法上加 @Scheduled 。要执行的方法与下面的xml配置的method值是对应的;
3、方法名是不是固定的。只要@Scheduled 注解的方法名与xml配置的一致即可。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
<!--
每2分钟执行一次:
0 0/2 * * * ?
-->
<!-- 定时任务 -->
<task:scheduled-tasks scheduler="scheduler" >
<task:scheduled ref="profitScheduler" method="execute" cron="0 0/2 * * * ?" />
</task:scheduled-tasks>
<task:scheduler id="scheduler" pool-size="5" />
</beans>
ref="profitScheduler" 引用的是 ProfitScheduler 类中的 @Component 中的值profitScheduler 。
7、执行顺序
上面的例子,就是按照 执行顺序 列举出来的。
@PostConstruct > init-method > InitializingBean ( afterPropertiesSet ) > ServletContextAware
---------------------