唐山星俊科技有限公司

唐山星俊科技有限公司

2、动态加载由于此项目使用spring框架

发布日期:2024-06-25 09:46    点击次数:64

2、动态加载由于此项目使用spring框架

一、概述1、背景

目前数据治理服务中有众多治理任务,当其中任一治理任务有改动需要升级或新增一个治理任务时,都需要将数据治理服务重启,会影响其他治理任务的正常运行。

2、目标能够动态启动、停止任一治理任务能够动态升级、添加治理任务启动、停止治理任务或升级、添加治理任务不能影响其他任务3、方案为了支持业务代码尽量的解耦,把部分业务功能通过动态加载的方式加载到主程序中,以满足可插拔式的加载、组合式的部署。配合xxl-job任务调度框架,将数据治理任务做成xxl-job任务的方式注册到xxl-job中,方便统一管理。二、动态加载1、自定义类加载器

URLClassLoader 是一种特殊的类加载器,可以从指定的 URL 中加载类和资源。它的主要作用是动态加载外部的 JAR 包或者类文件,从而实现动态扩展应用程序的功。为了便于管理动态加载的jar包,自定义类加载器继承URLClassloader。

潍坊华天柴油机制造有限公司 53); font-size: 16px; letter-spacing: 0.8px; text-align: left; word-spacing: 0.8px; background-color: rgb(255,
儋州市海麟燕商贸有限公司 255,
于田县达南聚合物有限公司 255); border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">/** * 自定义类加载器 * * @author lijianyu * @date 2023/04/03 17:54 **/public class MyClassLoader extends URLClassLoader {    private Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<>();    public Map<String, Class<?>> getLoadedClasses() {        return loadedClasses;    }    public MyClassLoader(URL[] urls, ClassLoader parent) {        super(urls, parent);    }    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {        // 从已加载的类集合中获取指定名称的类        Class<?> clazz = loadedClasses.get(name);        if (clazz != null) {            return clazz;        }        try {            // 调用父类的findClass方法加载指定名称的类            clazz = super.findClass(name);            // 将加载的类添加到已加载的类集合中            loadedClasses.put(name, clazz);            return clazz;        } catch (ClassNotFoundException e) {            e.printStackTrace();            return null;        }    }    public void unload() {        try {            for (Map.Entry<String, Class<?>> entry : loadedClasses.entrySet()) {                // 从已加载的类集合中移除该类                String className = entry.getKey();                loadedClasses.remove(className);                try{                    // 调用该类的destory方法,回收资源                    Class<?> clazz = entry.getValue();                    Method destory = clazz.getDeclaredMethod("destory");                    destory.invoke(clazz);                } catch (Exception e ) {                    // 表明该类没有destory方法                }            }            // 从其父类加载器的加载器层次结构中移除该类加载器            close();        } catch (Exception e) {            e.printStackTrace();        }    }}
自定义类加载器中,为了方便类的卸载,定义一个map保存已加载的类信息。key为这个类的ClassName,value为这个类的类信息。同时定义了类加载器的卸载方法,卸载方法中,将已加载的类的集合中移除该类。由于此类可能使用系统资源或调用线程,为了避免资源未回收引起的内存溢出,通过反射调用这个类中的destroy方法,回收资源。最后调用close方法。2、动态加载

产品介绍 53); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif; font-size: 16px; text-align: left; text-indent: 0em; text-wrap: wrap; word-spacing: 0.8px; background-color: rgb(255, 255, 255); line-height: 1.75; letter-spacing: 0em;">由于此项目使用spring框架,以及xxl-job任务的机制调用动态加载的代码,因此要完成以下内容

将动态加载的jar包读到内存中将有spring注解的类,通过注解扫描的方式,扫描并手动添加到spring容器中。将@XxlJob注解的方法,通过注解扫描的方式,手动添加到xxljob执行器中。
/** * @author lijianyu * @date 2023/04/29 13:18 **/@Componentpublic class DynamicLoad {    private static Logger logger = LoggerFactory.getLogger(DynamicLoad.class);    @Autowired    private ApplicationContext applicationContext;    private Map<String, MyClassLoader> myClassLoaderCenter = new ConcurrentHashMap<>();    @Value("${dynamicLoad.path}")    private String path;    /**     * 动态加载指定路径下指定jar包     * @param path     * @param fileName     * @param isRegistXxlJob  是否需要注册xxljob执行器,项目首次启动不需要注册执行器     * @return map<jobHander, Cron> 创建xxljob任务时需要的参数配置     */    public void loadJar(String path, String fileName, Boolean isRegistXxlJob) throws ClassNotFoundException, InstantiationException, IllegalAccessException {        File file = new File(path +"/" + fileName);        Map<String, String> jobPar = new HashMap<>();        // 获取beanFactory        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();        // 获取当前项目的执行器        try {            // URLClassloader加载jar包规范必须这么写            URL url = new URL("jar:file:" + file.getAbsolutePath() + "!/");            URLConnection urlConnection = url.openConnection();            JarURLConnection jarURLConnection = (JarURLConnection)urlConnection;            // 获取jar文件            JarFile jarFile = jarURLConnection.getJarFile();            Enumeration<JarEntry> entries = jarFile.entries();            // 创建自定义类加载器,并加到map中方便管理            MyClassLoader myClassloader = new MyClassLoader(new URL[] { url }, ClassLoader.getSystemClassLoader());            myClassLoaderCenter.put(fileName, myClassloader);            Set<Class> initBeanClass = new HashSet<>(jarFile.size());            // 遍历文件            while (entries.hasMoreElements()) {                JarEntry jarEntry = entries.nextElement();                if (jarEntry.getName().endsWith(".class")) {                    // 1. 加载类到jvm中                    // 获取类的全路径名                    String className = jarEntry.getName().replace('/', '.').substring(0, jarEntry.getName().length() - 6);                    // 1.1进行反射获取                    myClassloader.loadClass(className);                }            }            Map<String, Class<?>> loadedClasses = myClassloader.getLoadedClasses();            XxlJobSpringExecutor xxlJobExecutor = new XxlJobSpringExecutor();            for(Map.Entry<String, Class<?>> entry : loadedClasses.entrySet()){                String className = entry.getKey();                Class<?> clazz = entry.getValue();                // 2. 将有@spring注解的类交给spring管理                // 2.1 判断是否注入spring                Boolean flag = SpringAnnotationUtils.hasSpringAnnotation(clazz);                if(flag){                    // 2.2交给spring管理                    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);                    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();                    // 此处beanName使用全路径名是为了防止beanName重复                    String packageName = className.substring(0, className.lastIndexOf(".") + 1);                    String beanName = className.substring(className.lastIndexOf(".") + 1);                    beanName = packageName + beanName.substring(0, 1).toLowerCase() + beanName.substring(1);                    // 2.3注册到spring的beanFactory中                    beanFactory.registerBeanDefinition(beanName, beanDefinition);                    // 2.4允许注入和反向注入                    beanFactory.autowireBean(clazz);                    beanFactory.initializeBean(clazz, beanName);                    /*if(Arrays.stream(clazz.getInterfaces()).collect(Collectors.toSet()).contains(InitializingBean.class)){                        initBeanClass.add(clazz);                    }*/                    initBeanClass.add(clazz);                }                // 3. 带有XxlJob注解的方法注册任务                // 3.1 过滤方法                Map<Method, XxlJob> annotatedMethods = null;                try {                    annotatedMethods = MethodIntrospector.selectMethods(clazz,                            new MethodIntrospector.MetadataLookup<XxlJob>() {                                @Override                                public XxlJob inspect(Method method) {                                    return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);                                }                            });                } catch (Throwable ex) {                }                // 3.2 生成并注册方法的JobHander                for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {                    Method executeMethod = methodXxlJobEntry.getKey();                    // 获取jobHander和Cron                    XxlJobCron xxlJobCron = executeMethod.getAnnotation(XxlJobCron.class);                    if(xxlJobCron == null){                        throw new CustomException("500", executeMethod.getName() + "(),没有添加@XxlJobCron注解配置定时策略");                    }                    if (!CronExpression.isValidExpression(xxlJobCron.value())) {                        throw new CustomException("500", executeMethod.getName() + "(),@XxlJobCron参数内容错误");                    }                    XxlJob xxlJob = methodXxlJobEntry.getValue();                    jobPar.put(xxlJob.value(), xxlJobCron.value());                    if (isRegistXxlJob) {                        executeMethod.setAccessible(true);                        // regist                        Method initMethod = null;                        Method destroyMethod = null;                        xxlJobExecutor.registJobHandler(xxlJob.value(), new CustomerMethodJobHandler(clazz, executeMethod, initMethod, destroyMethod));                    }                }            }            // spring bean实际注册            initBeanClass.forEach(beanFactory::getBean);        } catch (IOException e) {            logger.error("读取{} 文件异常", fileName);            e.printStackTrace();            throw new RuntimeException("读取jar文件异常: " + fileName);        }    }}

以下是判断该类是否有spring注解的工具类企业文化

apublic class SpringAnnotationUtils {    private static Logger logger = LoggerFactory.getLogger(SpringAnnotationUtils.class);    /**     * 判断一个类是否有 Spring 核心注解     *     * @param clazz 要检查的类     * @return true 如果该类上添加了相应的 Spring 注解;否则返回 false     */    public static boolean hasSpringAnnotation(Class<?> clazz) {        if (clazz == null) {            return false;        }        //是否是接口        if (clazz.isInterface()) {            return false;        }        //是否是抽象类        if (Modifier.isAbstract(clazz.getModifiers())) {            return false;        }        try {            if (clazz.getAnnotation(Component.class) != null 




Powered by 唐山星俊科技有限公司 @2013-2022 RSS地图 HTML地图

Copyright 站群 © 2013-2024 SSWL 版权所有