[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[备忘]spring里@Lazy(true)懒加载的bean,@Scheduled注解的手动调度实现

上一篇:[备忘]unity里,大跳小跳的实现
下一篇:[备忘]Filed上注解,不能自定义一个组合注解?

添加日期:2024/8/8 18:10:08 快速返回   返回列表 阅读403次
我的需求:
定时调度类几十个,都是@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置空,再手动添加?



 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved