我的需求: 定时调度类几十个,都是@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置空,再手动添加?
 
 
  
 |