项目启动后开启定时任务方法-->InitializingBean

本文介绍如何使用Spring框架的InitializingBean接口实现项目启动后的定时任务。通过创建一个实现了InitializingBean的类,并在配置文件中进行注册,可以设定定时任务的具体执行时间和周期。

通过springframework自带的InitializingBean同样可以达到项目启动后开启定时任务效果,具体实现如下。

1.写一个继承InitializingBean接口的类

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

import javax.annotation.Resource;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import com.xxx.service.XxxService;
import com.xxx.util.StringUtil;

@Service
public class TaskListener implements InitializingBean {

	@Resource(name = "xxxService")
	private XxxService service;

	@Override
	public void afterPropertiesSet() throws Exception {
		
		Calendar date = Calendar.getInstance();
		//设置时间为 xx-xx-xx 00:00:00
        date.set(date.get(Calendar.YEAR),
        		date.get(Calendar.MONTH), date.get(Calendar.DATE), 14, 52, 0);
        //如果第一次执行定时任务的时间 小于当前的时间    
        //此时要在 第一次执行定时任务的时间加一天,以便此任务在下个时间点执行。如果不加一天,任务会立即执行。
        if (date.before(new Date())) {    
        	date.set(date.get(Calendar.YEAR),
            		date.get(Calendar.MONTH), date.get(Calendar.DATE) + 1, 14, 52, 0);    
        }
        StringUtil.date2String(date.getTime(), "yyyy-MM-dd HH:mm:ss");
        
		Timer timer = new Timer();
		XxxTask task = new Xxxask(service);
		timer.scheduleAtFixedRate(task, date.getTime(), 24 * 60 * 60 * 1000);}}
2.在配置文件中配置该类

<bean class="com.xxx.task.TaskListener" />
这样就行了。

原理:

Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:
1)通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
2)通过 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;
3)在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。
结论:Bean在实例化的过程中
Constructor > @PostConstruct > InitializingBean > init-method


<think>我们正在讨论的是Spring MVC项目中,如何在项目完全启动后执行一个方法。用户之前尝试过@PostConstruct或InitializingBean,但可能因为执行时机过早(在应用上下文完全初始化之前)而没有成功。因此,我们需要确保方法在应用上下文完全初始化之后执行。 从引用[1]中,我们得知可以通过实现ApplicationListener<ContextRefreshedEvent>接口,并在onApplicationEvent方法中编写逻辑,同时通过检查event.getApplicationContext().getParent() == null来避免重复执行(因为Spring可能有父子上下文,可能会触发两次事件)。这是Spring框架提供的一种标准方式。 另外,引用[3]提到了一种使用HttpServlet的init方法的方式,但这种方式依赖于Servlet的生命周期,并且通常需要配置在web.xml中,不如Spring的事件监听机制灵活和现代。 因此,我们推荐使用监听ContextRefreshedEvent事件的方式。同时,如果项目是Spring Boot,那么使用ApplicationRunner或CommandLineRunner会更方便,但用户明确问的是Spring MVC项目(非Spring Boot),所以我们主要推荐事件监听方式。 在回答中,我们需要强调避免使用@PostConstruct和InitializingBean的原因(执行时机过早),以及ServletContextListener需要配置web.xml,不够方便。 步骤: 1. 创建一个类实现ApplicationListener<ContextRefreshedEvent>接口,或者使用@EventListener注解(Spring 4.2+)。 2. 在事件处理方法中,检查父上下文是否为null(避免重复执行)。 3. 在该方法中调用需要执行的业务逻辑。 同时,我们可以提供代码示例,并说明注意事项。 此外,用户可能还会关心如何防止多次执行(父子上下文问题),以及如果初始化方法耗时很长,如何异步执行。 最后,按照要求生成相关问题。 </think>### Spring MVC项目完全启动后执行方法的实现方案 在Spring MVC项目中,若需要在**应用完全启动后**执行初始化方法(如加载缓存、启动后台任务等),推荐使用Spring的事件监听机制。以下是经过验证的可靠实现方式: --- #### 方案1:监听`ContextRefreshedEvent`事件(推荐) **原理**:Spring在完成所有Bean初始化、依赖注入和应用上下文准备后,会发布`ContextRefreshedEvent`事件[^1]。这是最安全的时机。 **代码实现**: ```java import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; @Component public class StartupListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 确保只执行一次(避免父子上下文重复触发) if (event.getApplicationContext().getParent() == null) { executeAfterStartup(); } } private void executeAfterStartup() { System.out.println("✅ 应用完全启动!执行自定义逻辑..."); // 此处添加业务代码(如初始化缓存、启动定时任务等) } } ``` **简化注解版(Spring 4.2+)**: ```java import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @Component public class StartupService { @EventListener public void onContextRefreshed(ContextRefreshedEvent event) { if (event.getApplicationContext().getParent() == null) { // 业务逻辑 } } } ``` **优势**: - 精确的执行时机:在所有Bean初始化完成后、请求处理前执行[^2] - 无需额外配置(通过`@Component`自动注册) - 天然支持依赖注入(可直接`@Autowired`其他Bean) --- #### 方案2:使用Servlet生命周期(备选) **适用场景**:需要在Servlet容器级别初始化(如启动全局定时任务)[^3]。 **实现步骤**: 1. 创建继承`HttpServlet`的类 2. 在`init()`方法中编写逻辑 3. 在`web.xml`中注册Servlet: ```xml <servlet> <servlet-name>StartupServlet</servlet-name> <servlet-class>com.example.StartupServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> ``` **代码示例**: ```java public class StartupServlet extends HttpServlet { @Override public void init() { // 应用启动后执行(Servlet容器级别) System.out.println("Servlet初始化完成"); // 启动定时任务等 } } ``` --- ### ️ 避免使用的方案及原因 | 方案 | 问题原因 | |---------------------|--------------------------------------------------------------------------| | **`@PostConstruct`** | 在Bean初始化阶段执行,此时应用上下文可能未完全就绪(如数据库连接未建立) | | **`InitializingBean`** | 同`@PostConstruct`,执行时机过早 | | **`ServletContextListener`** | 需要在`web.xml`手动配置,不符合现代Spring开发习惯 | --- ### 关键注意事项 1. **重复执行问题**: 通过`event.getApplicationContext().getParent() == null`过滤父子上下文导致的多次触发。 2. **依赖注入安全**: 监听器方法中可直接使用`@Autowired`注入的Bean,Spring会确保依赖已初始化。 3. **耗时操作处理**: 若初始化逻辑耗时较长,需异步执行防止阻塞启动: ```java @Async public void executeAfterStartup() { /* 业务逻辑 */ } ``` (需在配置类添加`@EnableAsync`) 4. **执行顺序保证**: 多个监听器的执行顺序可通过`@Order`注解控制: ```java @EventListener @Order(1) // 数字越小优先级越高 public void handler1(ContextRefreshedEvent event) {...} ``` --- ### 执行流程验证 ```mermaid graph TD A[启动Tomcat] --> B[加载web.xml] B --> C[初始化DispatcherServlet] C --> D[创建Spring上下文] D --> E[初始化所有Bean] E --> F[发布ContextRefreshedEvent] F --> G[执行自定义方法] G --> H[接收用户请求] ``` 通过此方案,自定义方法会在阶段`F→G`执行,确保应用完全就绪[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值