spring1.0(一)Spring 容器启动完成后,执行初始化加载工作

文章目录
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
--------------------- 
 

猜你喜欢

转载自blog.csdn.net/insis_mo/article/details/87888494
今日推荐