我的需求: 定时调度类几十个,都是@Lazy(false) +@Scheduled注解写的。 启动时,这些bean都要初始化,很浪费时间。
所以想改成@Lazy(true),但是改成懒加载的副作用就是,定时调度不再生效。 所以需要手动调度一下。
最终的代码如下:
package cn.com.xxxx.service.impl;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.SingletonBeanRegistry; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Lazy; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor; import org.springframework.stereotype.Service;
import java.lang.reflect.Method; import java.util.Set;
@Service public class ScheduledBeanInitializer implements ApplicationListener<ContextRefreshedEvent> {
private static final Logger logger = LoggerFactory.getLogger("xxxInfo");
@Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
//spring context才执行,springMvc context不执行 final ApplicationContext context = contextRefreshedEvent.getApplicationContext(); if (context.getParent() != null) { return; }
new Thread() { @Override public void run() {
//休息10秒 try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }
logger.info("ScheduledBeanInitializer开始执行........"); ScheduledAnnotationBeanPostProcessor postProcessor = context.getBean(ScheduledAnnotationBeanPostProcessor.class); try { //查找包含@Lazy注解的类 ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); provider.addIncludeFilter(new AnnotationTypeFilter(Lazy.class));
//遍历类 Set<BeanDefinition> beanDefinitionSet = provider.findCandidateComponents("cn.xxx"); logger.info("查找cn.xxx下有@Lazy注解的类:" + beanDefinitionSet.size() + "个。"); for (BeanDefinition beanDefinition : beanDefinitionSet) { try { // 检查类的方法上是否有@Scheduled注解 Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName()); if (isScheduledPresent(beanClass)) {
//@Lazy(true)的 Lazy lazyAnnotation = beanClass.getAnnotation(Lazy.class); if (lazyAnnotation != null && lazyAnnotation.value()) {
//初始化Bean try { AutowireCapableBeanFactory autowireCapableBeanFactory = context.getAutowireCapableBeanFactory(); if (autowireCapableBeanFactory instanceof SingletonBeanRegistry) { //没有实例存在,才创建 boolean isLoaded = ((SingletonBeanRegistry) autowireCapableBeanFactory).containsSingleton(beanDefinition.getBeanClassName()); if (!isLoaded) { Object beanInstance = context.getBean(beanClass); logger.info("已初始化Bean:" + beanInstance.toString());
//注册定时任务 postProcessor.postProcessAfterInitialization(beanInstance, beanClass.getName()); } } } catch (Exception e) { logger.error("初始化Bean时出错:", e); } } }
} catch (Exception e) { logger.error("检测Bean定义时出错:", e); } } } catch (Exception e) { logger.error("ScheduledBeanInitializer执行出错", e); } //添加为调度 postProcessor.afterSingletonsInstantiated(); logger.info("ScheduledBeanInitializer执行结束........"); } }.start(); logger.info("ScheduledBeanInitializer 直接返回,新线程负责创建Bean"); }
/** * 类的Method上是否有@Scheduled注解 * * @param beanClass 类 * @return */ private boolean isScheduledPresent(Class<?> beanClass) { // 获取类中所有声明的方法 Method[] methods = beanClass.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Scheduled.class)) { return true; } } return false; } }
(1) BeanPostProcessor就是创建Bean后执行的处理器。 ScheduledAnnotationBeanPostProcessor就是一个。
(2) 它里面有一个注册器, private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
创建Bean后,它扫描类里面的@Scheduled注解,转成调度Task,放到registrar里。 最后会执行scheduleTasks()方法,添加调度。
(3) 使用@Lazy(true)时,启动时spring没有创建这个bean,所以就没执行ScheduledAnnotationBeanPostProcessor, 也就没添加调度。
(4) 手动使用context.getBean(beanClass);会自动创建Bean, 但是也没有执行ScheduledAnnotationBeanPostProcessor,还是没有调度
所以需要自己调一下, postProcessor.postProcessAfterInitialization(beanInstance, beanClass.getName());
上面这句只是add到registrar里了,还是没有调度 最后需要postProcessor.afterSingletonsInstantiated();里面就是调用registrar的scheduleTasks()方法,添加调度。
(5)至此就实现了,启动时不调度,需要时再添加调度。
(6)如果@Lazy(true)的bean被其他地方引用了,那么可能还会自动创建这个bean
所以又判断了一下,bean是否已经存在,不存在才创建一个,添加调度。 这个判断也找了好久,很费劲。
context.containsBeanDefinition()这种方法是不行的, bean没有创建实例,但是beanDifinition是存在的。
(7)有个问题: 如果@Lazy(true)的bean启动时已经创建的话,定时调度已经添加了。 比如已经有111,222,333三个调度了, 我这里又add了444,555,666三个task, 最后执行了 postProcessor.afterSingletonsInstantiated(); 将111,222,333,444,555,666添加为调度 相当于111,222,333重复了。
ScheduledTaskRegistrar类提供了setCronTasksList等几个方法, 那么,先将List置空,再手动添加?
|