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

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 构造方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容