美文网首页
2022-06-25_项目热部署学习笔记

2022-06-25_项目热部署学习笔记

作者: kikop | 来源:发表于2022-06-25 19:14 被阅读0次

    20220625_项目热部署学习笔记

    1概述

    本文主要是结合代码,学习一下spring中Bean的动态加载,具体实现看代码,主要分以下几种情况:

    1. 本地项目中的类加载到当前项目的spring 容器。
    2. 外部磁盘中的单个clazz文件基于自定义加载器的方式加载到当前项目spring 容器中。
    3. 外部磁盘中的单个clazz文件基于URLClassLoader加载器的方式加载到当前项目spring 容器中
    4. 外部磁盘jar中的clazz文件基于注解的方式加载到当前项目spring 容器中。
    5. 外部磁盘jar中的clazz文件基于反射的方式加载当前项目中。

    2代码示例

    2.1本地项目中的类加载到当前项目的spring 容器

    /**
     * @author kikop
     * @version 1.0
     * @project myspringhotdeploydemo
     * @file DynamicInjectManager
     * @desc 项目热部署示例学习
     * @date 2022/06/25
     * @time 8:30
     * @by IDE: IntelliJ IDEA
     */
    public class DynamicInjectManager {
        
    /**
         * 本地项目中的类加载到当前项目的spring 容器2
         * @param defaultListableBeanFactory
         */
        public static void dynamicInjectBeanByConstructor(DefaultListableBeanFactory defaultListableBeanFactory) {
    
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(CurrentAnimal.class);
    //        BeanDefinitionBuilder beanDefinitionBuilder2 = BeanDefinitionBuilder.genericBeanDefinition(CurrentAnimal.class);
    
            // 1.方式1,by constructorArgIndex
            beanDefinitionBuilder.addConstructorArgValue("北极熊")
                    .addConstructorArgValue("白色")
                    .addConstructorArgValue(3);
    
            String beanName = "myAnimal";
            defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
    
            Object bean = defaultListableBeanFactory.getBean(beanName);
            CurrentAnimal animal = (CurrentAnimal) bean;
            System.out.println("animal.getName():" + animal.getName());
        }
    
    
        /**
         * 本地项目中的类加载到当前项目的spring 容器1
         * @param defaultListableBeanFactory
         */
        public static void dynamicInjectBeanByProperty(DefaultListableBeanFactory defaultListableBeanFactory) {
    
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(CurrentAnimal.class);
    
            // 2.方式2
            beanDefinitionBuilder.addPropertyValue("name", "北极熊")
                    .addPropertyValue("color", "白色")
                    .addPropertyValue("age", 3);
            String beanName = "myAnimal";
            defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
    
            Object bean = defaultListableBeanFactory.getBean(beanName);
            CurrentAnimal animal = (CurrentAnimal) bean;
            System.out.println("animal.getName():" + animal.getName());
        }
    

    2.2外部磁盘中的单个clazz文件基于自定义加载器反射的方式加载到当前项目spring 容器中

    2.2.1MyClassLoader

    package com.kikop.cloader;
    
    import java.io.IOException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    /**
     * @author kikop
     * @version 1.0
     * @project myspringhotdeploydemo
     * @file ICalculator
     * @desc 项目热部署示例学习
     * @date 2022/06/25
     * @time 8:30
     * @by IDE: IntelliJ IDEA
     * @reference https://mp.weixin.qq.com/s/8JNecuJA9n07Ob7XzLPQTA
     */
    public class MyClassLoader extends ClassLoader {
    
        private String dirPath;
    
        //  是否保留类的完整包名路径,
        // true:保留时,类要放在原来的包路径下
        // false:类直接放在 dirPath下
        private boolean isKeepPacketName = true;
    
        /**
         * @param dirPath D:/mqexperiment/hotdeploy
         */
        public MyClassLoader(String dirPath, boolean isKeepPacketName) {
    
            if (!dirPath.endsWith("/") && !dirPath.endsWith("\\")) {
                dirPath += "/";
            }
            this.dirPath = dirPath;
            this.isKeepPacketName = isKeepPacketName;
        }
    
        /**
         * 触发被 loadClass-->findClass-->loadClass
         *
         * @param name com.kikop.model.CurrentAnimal
         * @return
         * @throws ClassNotFoundException
         */
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
    
            String filePath; // 磁盘路径
            if (isKeepPacketName) {
                // 前提:包名要保留,因为是根据 className进行分割的
                // file:///D:/mqexperiment/hotdeploy/com/kikop/model/CurrentAnimal.class
                filePath = dirPath + name.replace('.', '/') + ".class";
            } else {
                // file:///D:/mqexperiment/hotdeploy/CurrentAnimal.class
                filePath = dirPath + name.substring(name.lastIndexOf('.') + 1) + ".class";
            }
            byte[] b;
            Path path;
            try {
                String strIgnore = "Customizer";
                if (name.lastIndexOf(strIgnore) != -1) { // ignore for check beaninfo
                    return null;
                }
                path = Paths.get(new URI(filePath));
    
    //            b = MyClassLoaderUtil.getBytesByFilePath(filePath);
                b = Files.readAllBytes(path);
                // defineClass将字节数组转换成Class对象
                return defineClass(name, b, 0, b.length);
            } catch (IOException | URISyntaxException e) {
                e.printStackTrace();
                return null;
            }
        }
    
    }
    
    

    2.2.2dynamicInjectBeanByCustomCloader

    /**
     * @author kikop
     * @version 1.0
     * @project myspringhotdeploydemo
     * @file DynamicInjectManager
     * @desc 项目热部署示例学习
     * @date 2022/06/25
     * @time 8:30
     * @by IDE: IntelliJ IDEA
     */
    public class DynamicInjectManager {
        
    public static void dynamicInjectBeanByCustomCloader(DefaultListableBeanFactory defaultListableBeanFactory
                , Map<String, String> reqParam) throws ClassNotFoundException, MalformedURLException {
    
            // 1.解析
            String beanName = reqParam.get("beanName");
            try {
                if (null != defaultListableBeanFactory.getBean(beanName)) {
                    System.out.println(String.format("%s 容器中已经存在", beanName));
                    return;
                }
            } catch (Exception ex) {
                // ignore
            }
    
            String strLocation = reqParam.get("localtion");
            String strClazz = reqParam.get("clazz");
            String strPath = "file:///" + strLocation; // URL
            MyClassLoader myClassLoader = new MyClassLoader(strPath, false);
            Class<?> aClass = myClassLoader.loadClass(strClazz); // com.kikop.model.CurrentAnimal
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(aClass);
    
            // 2.组装
            for (int i = 0; i < aClass.getDeclaredFields().length; i++) {
                String fieldName = aClass.getDeclaredFields()[i].getName();
                if (null != reqParam.get(fieldName)) {
                    beanDefinitionBuilder.addPropertyValue(fieldName, reqParam.get(fieldName));
                } else {
                    beanDefinitionBuilder.addPropertyValue(fieldName, "default" + fieldName);
                }
            }
            defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
    
            // 3.输出字段
            // beanName的加载器:myClassLoader
            // type.getClassLoader():myClassLoader
            // 默认会变相加载该类的 XCustomizer,肯定没有啊
            Object animalBean = defaultListableBeanFactory.getBean(beanName);
            List<Field> fields = Arrays.asList(aClass.getDeclaredFields());
            fields.stream().forEach(field -> {
                System.out.println(field);
            });
        }
    

    2.3外部磁盘中的单个clazz文件基于URLClassLoader加载器的方式加载到当前项目spring 容器中

    /**
     * @author kikop
     * @version 1.0
     * @project myspringhotdeploydemo
     * @file DynamicInjectManager
     * @desc 项目热部署示例学习
     * @date 2022/06/25
     * @time 8:30
     * @by IDE: IntelliJ IDEA
     */
    public class DynamicInjectManager {
    
        public static void dynamicInjectBeanByReflect(DefaultListableBeanFactory defaultListableBeanFactory
                , Map<String, String> reqParam) throws ClassNotFoundException, MalformedURLException {
    
            // 1.解析
            String beanName = reqParam.get("beanName");
            try {
                if (null != defaultListableBeanFactory.getBean(beanName)) {
                    System.out.println(String.format("%s 容器中已经存在", beanName));
                    return;
                }
            } catch (Exception ex) {
                // ignore
            }
    
            String strLocation = reqParam.get("localtion");
            // 使用file协议在本地寻找指定.class文件,file:///Users/fa1c0n/codeprojects/IdeaProjects/misc-classes/src/main/java/
            // 使用http协议到远程地址寻找指定.class文件, http://127.0.0.1:8000/
            String strPath = "file:///" + strLocation; // URL
            String strClazz = reqParam.get("clazz");
    
            URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(strPath)});
            // 注意:
            // 类名 com.kikop.model.User
            Class<?> aClass = urlClassLoader.loadClass(strClazz);
    
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(aClass);
    
            // 2.组装
            for (int i = 0; i < aClass.getDeclaredFields().length; i++) {
                String fieldName = aClass.getDeclaredFields()[i].getName();
                if (null != reqParam.get(fieldName)) {
                    beanDefinitionBuilder.addPropertyValue(fieldName, reqParam.get(fieldName));
                } else {
                    beanDefinitionBuilder.addPropertyValue(fieldName, "default" + fieldName);
                }
            }
            defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
    
            // 3.输出字段
            Object animalBean = defaultListableBeanFactory.getBean(beanName);
            List<Field> fields = Arrays.asList(aClass.getDeclaredFields());
            fields.stream().forEach(field -> {
                System.out.println(field);
            });
        }
    

    2.4外部磁盘jar中的clazz文件基于注解的方式加载到当前项目spring 容器中

    package com.kikop.deploymethod;
    
    import com.kikop.calculator.ICalculator;
    import com.kikop.utils.MyJarUtils;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    import java.net.URL;
    import java.net.URLClassLoader;
    import java.util.Set;
    
    /**
     * @author kikop
     * @version 1.0
     * @project myspringhotdeploydemo
     * @file DeployByAnnotation
     * @desc 项目热部署示例学习
     * @date 2022/06/25
     * @time 8:30
     * @by IDE: IntelliJ IDEA
     * @reference https://mp.weixin.qq.com/s/8JNecuJA9n07Ob7XzLPQTA
     */
    @Component
    public class DeployByAnnotation implements ApplicationContextAware {
    
    
        private static ApplicationContext applicationContext;
    
    
        // jar:file:/
        private static String jarAddress = "D:\\mqexperiment\\hotdeploy\\mycalculatorbusinesscomponent-0.0.1-SNAPSHOT.jar";
        private static String jarPath = "file:/" + jarAddress;
    
        /**
         * 加入jar包后 动态注册bean到spring容器,包括bean的依赖
         */
        public static void hotDeployWithSpring() throws Exception {
            // com.kiko.calculator.impl.CalculatorImpl
            Set<String> classNameSet = MyJarUtils.readJarFile(jarAddress);
            URLClassLoader urlClassLoader = new URLClassLoader(
                    new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());
    
            for (String className : classNameSet) {
                Class clazz = urlClassLoader.loadClass(className);
    
                if (MyJarUtils.isSpringBeanClass(clazz)) { //  是需要的注解
                    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
    
                    ((BeanDefinitionRegistry) applicationContext).registerBeanDefinition(
                            MyJarUtils.transformName(className), beanDefinitionBuilder.getBeanDefinition());
                }
            }
            ICalculator calculator = applicationContext.getBean(ICalculator.class);
            System.out.println(calculator.calculate(4, 5));
        }
    
        /**
         * 删除jar包时 需要在spring容器删除注入
         */
        public static void delete() throws Exception {
            Set<String> classNameSet = MyJarUtils.readJarFile(jarAddress);
            URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());
            for (String className : classNameSet) {
                Class clazz = urlClassLoader.loadClass(className);
                if (MyJarUtils.isSpringBeanClass(clazz)) {
                    ((BeanDefinitionRegistry) applicationContext).removeBeanDefinition(MyJarUtils.transformName(className));
                }
            }
        }
    
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    
    

    2.5外部磁盘jar中的clazz文件基于反射的方式加载当前项目中

    package com.kikop.deploymethod;
    
    import com.kikop.calculator.ICalculator;
    
    import java.net.URL;
    import java.net.URLClassLoader;
    
    /**
     * @author kikop
     * @version 1.0
     * @project myspringhotdeploydemo
     * @file DeployByReflect
     * @desc 项目热部署示例学习
     * @date 2022/06/25
     * @time 8:30
     * @by IDE: IntelliJ IDEA
     * @reference https://mp.weixin.qq.com/s/8JNecuJA9n07Ob7XzLPQTA
     */
    public class DeployByReflect {
    
        // 位置目录,不能是文件
        private static String jarAddress = "D:\\mqexperiment\\hotdeploy\\mycalculatorbusinesscomponent-0.0.1-SNAPSHOT.jar";
        private static String jarPath = "file:/" + jarAddress;
    
    
        // ClassLoader(最底层)-->SecureClassLoader(中间态)-->URLClassLoader(最简单)
        // SecureClassLoader 默认: this(checkCreateClassLoader(), null, getSystemClassLoader());
    
        /**
         * 热加载Calculator接口的实现 反射方式
         *
         * @throws Exception
         */
        public static void hotDeployWithReflect() throws Exception {
    
            URLClassLoader urlClassLoader = new URLClassLoader(
                    new URL[]{new URL(jarPath)},
                    Thread.currentThread().getContextClassLoader());
            Class clazz = urlClassLoader.loadClass("com.kikop.calculator.impl.CalculatorImpl");
            ICalculator iCalculator = (ICalculator) clazz.newInstance();
            int result = iCalculator.add(18, 1);
            System.out.println(result);
        }
    
    }
    
    

    2.6工具类

    2.6.1MyJarUtils

    package com.kikop.utils;
    
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Repository;
    import org.springframework.stereotype.Service;
    
    import java.io.IOException;
    import java.lang.reflect.Modifier;
    import java.util.Enumeration;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
    
    /**
     * @author kikop
     * @version 1.0
     * @project myspringhotdeploydemo
     * @file MyJarUtils
     * @desc 项目热部署示例学习
     * @date 2022/06/25
     * @time 8:30
     * @by IDE: IntelliJ IDEA
     * @reference https://mp.weixin.qq.com/s/8JNecuJA9n07Ob7XzLPQTA
     */
    public class MyJarUtils {
    
        /**
         * 读取jar包中所有类文件
         */
        public static Set<String> readJarFile(String jarAddress) throws IOException {
    
            Set<String> classNameSet = new HashSet<>();
            JarFile jarFile = new JarFile(jarAddress);
            Enumeration<JarEntry> entries = jarFile.entries();// 遍历整个jar文件
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String name = jarEntry.getName();
                if (name.endsWith(".class")) { // 过滤 .class
                    // com/kikop/calculator/impl/XxxImpl.class
                    // com/kikop/calculator/impl/XxxImpl
                    // 最终的classNamecom.kikop.calculator.impl.XxxImpl
                    String className = name.replace(".class", "").replaceAll("/", ".");
                    classNameSet.add(className);
                }
            }
            return classNameSet;
        }
    
        /**
         * 方法描述 判断class对象是否带有spring的注解
         */
        public static boolean isSpringBeanClass(Class<?> cla) {
            if (cla == null) {
                return false;
            }
            //是否是接口
            if (cla.isInterface()) {
                return false;
            }
            // 是否是抽象类
            if (Modifier.isAbstract(cla.getModifiers())) {
                return false;
            }
            if (cla.getAnnotation(Component.class) != null) {
                return true;
            }
            if (cla.getAnnotation(Repository.class) != null) {
                return true;
            }
            if (cla.getAnnotation(Service.class) != null) {
                return true;
            }
            return false;
        }
    
        /**
         * 类名首字母小写 作为 spring容器beanMap的key
         */
        public static String transformName(String className) {
            String tmpstr = className.substring(className.lastIndexOf(".") + 1);
            return tmpstr.substring(0, 1).toLowerCase() + tmpstr.substring(1);
        }
    }
    
    

    2.6.2MyClassLoaderUtil

    package com.kikop.utils;
    
    
    import com.sun.xml.internal.ws.util.ByteArrayBuffer;
    
    import java.io.*;
    import java.net.URL;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
    
    /**
     * @author kikop
     * @version 1.0
     * @project myjdkclazzloader1demo
     * @file MyClassLoaderUtil
     * @desc 获取类的字节码
     * @date 2021/6/13
     * @time 18:00
     * @by IDE: IntelliJ IDEA
     */
    public class MyClassLoaderUtil {
    
        private static String convertFilePath(String ignoreValue, String filePath) {
    
            if (filePath.indexOf(ignoreValue) != -1) {
                filePath = filePath.substring(filePath.indexOf(ignoreValue) + ignoreValue.length());
            }
            return filePath;
        }
    
        /**
         * getBytesByFilePath
         * <p>
         * 返回类的字节码
         *
         * @param filePath D:/test/com.kikop.model.User.class
         * @return
         */
        public static byte[] getBytesByFilePath(String filePath) {
    
            filePath = convertFilePath("file:///", filePath);
            filePath = convertFilePath("file:/", filePath);
    
            byte[] resultBytes = null;
            InputStream inputStream = null;
    
            // 借助 byteArrayOutputStream暂存字节流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            try {
                File file = new File(filePath);
                if (!file.exists()) {
                    return resultBytes;
                }
                inputStream = new FileInputStream(file);
                int c = 0;
                while ((c = inputStream.read()) != -1) {
                    byteArrayOutputStream.write(c);
                }
                resultBytes = byteArrayOutputStream.toByteArray();
            } catch (FileNotFoundException ex) {
                ex.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    byteArrayOutputStream.close();
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return resultBytes;
        }
    
    
        /**
         * getBytesByJarFile
         * 返回 Jar包中类的字节码
         *
         * @param jarDirectory  D:\workdirectory\mqexperiment\clloader\
         * @param jarName       xx.jar
         * @param classFullName java.lang.String
         * @return
         * @throws IOException
         */
        public static byte[] getBytesByJarFile(String jarDirectory, String jarName, String classFullName) throws IOException {
    
            byte[] resultBytes = null;
    
            InputStream inputStream = null;
            ByteArrayOutputStream byteArrayOutputStream = null;
            try {
    
    
                // replaceAll:要加斜杠转义,replace:不需要
                // java.lang.String --> java/lang/String
                String tmpClassFullName = classFullName.replaceAll("\\.", "/").concat(".class");
    
                // com.kikop.AddressService-->com/kikop/service/AddressService
                // String tmpClassFullName2 = classFullName.replace(".", "/").concat(".class");
                String jarFullName = jarDirectory + "/" + jarName;
                JarFile jar = new JarFile(jarFullName);
                JarEntry entry = jar.getJarEntry(tmpClassFullName); // java/lang/String.class
    
                if (null == entry) { // 增加异常判断,文件不存在
                    System.out.println("tmpClassFullName:" + tmpClassFullName);
                    return null;
                }
    
                inputStream = jar.getInputStream(entry);
                byteArrayOutputStream = new ByteArrayOutputStream();
                int nextValue = inputStream.read();
                while (-1 != nextValue) {
                    byteArrayOutputStream.write(nextValue);
                    nextValue = inputStream.read();
                }
    //            byte[] buffer=new byte[2048];
    //            int len=0;
    //            while((len=in.read(buffer))!=-1){
    //                out.write(buffer,0,len);
    //            }
    
                resultBytes = byteArrayOutputStream.toByteArray();
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                try {
                    if (null != byteArrayOutputStream) {
                        byteArrayOutputStream.close();
                    }
                    if (null != inputStream) {
                        inputStream.close();
    
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return resultBytes;
        }
    
    
        /**
         * getBytesByJarFile 返回 Jar包中类的字节码
         *
         * @param jarDirectory  D:\workdirectory\mqexperiment\clloader\
         * @param jarName       xx.jar
         * @param classFullName java.lang.String
         * @return
         * @throws IOException
         */
        public static byte[] getBytesByJarFile2(String jarDirectory, String jarName, String classFullName) throws IOException {
    
            // com.kikop.AddressService-->com/kikop/service/AddressService.class
            String tmpClassFullName = classFullName.replace(".", "/").concat(".class");
            InputStream inputStream;
            ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer();
            byte[] resultBytes = null;
            int code;
            URL fileUrl;
            try {
    
                // jar:file:\
                // D:\workdirectory\mqexperiment\clloader\mymoduleva-1.0-SNAPSHOT.jar!/
                // com/kikop/service/AddressService.class
                String jarFullName = jarDirectory + "/" + jarName;
                String strSpec = "jar:file:\\" + jarFullName + "!/" + tmpClassFullName;
                fileUrl = new URL(strSpec);
                inputStream = fileUrl.openStream();
                while ((code = inputStream.read()) != -1) {
                    byteArrayBuffer.write(code);
                }
                resultBytes = byteArrayBuffer.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return resultBytes;
        }
    }
    

    2.7测试

    package com.kikop;
    
    import com.kikop.cloader.MyClassLoader;
    import com.kikop.deploymethod.DeployByAnnotation;
    import com.kikop.deploymethod.DeployByReflect;
    import com.kikop.deploymethod.DynamicInjectManager;
    import com.kikop.model.CurrentAnimal;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    import java.lang.reflect.Field;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLClassLoader;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author kikop
     * @version 1.0
     * @project myspringhotdeploydemo
     * @file MySpringHotDeployDemoApplication
     * @desc 项目热部署示例学习
     * @date 2022/06/25
     * @time 8:30
     * @by IDE: IntelliJ IDEA
     * @reference https://mp.weixin.qq.com/s/8JNecuJA9n07Ob7XzLPQTA
     */
    @SpringBootApplication
    public class MySpringHotDeployDemoApplication implements CommandLineRunner {
    
    
        private static DefaultListableBeanFactory defaultListableBeanFactory;
    
        public static void main(String[] args) throws ClassNotFoundException, MalformedURLException {
    
            ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(MySpringHotDeployDemoApplication.class, args);
    
            AutowireCapableBeanFactory autowireCapableBeanFactory = configurableApplicationContext.getAutowireCapableBeanFactory();
            defaultListableBeanFactory = (DefaultListableBeanFactory) autowireCapableBeanFactory;
    
            Map<String, String> reqParam = new HashMap<>();
    
            reqParam.put("name", "北极熊");
            reqParam.put("color", "白色");
            reqParam.put("age", "3");
            reqParam.put("localtion", "D:/mqexperiment/hotdeploy/");
            reqParam.put("clazz", "com.kikop.model.Animal");
            reqParam.put("beanName", "myAnimal");
            DynamicInjectManager.dynamicInjectBeanByCustomCloader(defaultListableBeanFactory, reqParam);
        }
    
    
        @Override
        public void run(String... args) throws Exception {
    
            // 1.基于JDK反射包热部署
            DeployByReflect.hotDeployWithReflect();
    
    
            // 2.基于Spring注解Jar包热部署
    //        DeployByAnnotation.hotDeployWithSpring();
    
        }
    }
    
    

    总结

    1.1JDKintrospector

    当我们把这个类交给 spring 的时候,问题就出现了: spring 是一个已有的框架, 它并不知道 User 这个类,也并不知道它有哪些方法、哪些属性。

    public void introspector(String clazz, Map<String, Object> properties) throws Exception {
        //反射创建实例
        Class target = Class.forName(clazz);
        Object bean = target.newInstance();
    
        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    
        for (PropertyDescriptor pd : pds) {
            Method setMethod = pd.getWriteMethod();
            String fieldName = pd.getName();
    
            if ("name".equalsIgnoreCase(fieldName)) {
                setMethod.invoke(bean, properties.get(fieldName));
            } else if ("age".equalsIgnoreCase(fieldName)){
                setMethod.invoke(bean, properties.get(fieldName));
            }
        }
    

    参考

    1动态上传jar包热部署实战

    https://mp.weixin.qq.com/s/8JNecuJA9n07Ob7XzLPQTA

    2SpringBoot动态注入Bean

    http://www.caotama.com/1398966.html

    3SpringBoot动态注入bean (系统注入漏洞原理)

    https://blog.csdn.net/weixin_45943597/article/details/124176226

    4Java中动态加载字节码的方法 (持续补充)Good

    https://blog.csdn.net/mole_exp/article/details/122768814

    5java Introspector(内省) 的使用场景以及为什么使用

    https://www.jianshu.com/p/418122d84e6e

    6[深入理解Java:内省(Introspector)

    https://www.cnblogs.com/peida/archive/2013/06/03/3090842.html)

    • public interface **Customizer**

    customizer 类提供一个用来自定义目标 Java Bean 的完全自定义 GUI。

    每个 customizer 都应该从 java.awt.Component 类继承,因此它们可以在 AWT 对话框或面板中被实例化。

    每个 customizer 都应该有一个 null 构造方法。

    相关文章

      网友评论

          本文标题:2022-06-25_项目热部署学习笔记

          本文链接:https://www.haomeiwen.com/subject/nfaivrtx.html