2022-06-25_項目熱部署學習筆記

20220625_項目熱部署學習筆記

1概述

本文主要是結(jié)合代碼,學習一下spring中Bean的動態(tài)加載,具體實現(xiàn)看代碼,主要分以下幾種情況:

  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;
    }

    /**
     * 觸發(fā)被 loadClass-->findClass-->loadClass
     *
     * @param name com.kikop.model.CurrentAnimal
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        String filePath; // 磁盤路徑
        if (isKeepPacketName) {
            // 前提:包名要保留,因為是根據(jù) 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將字節(jié)數(shù)組轉(zhuǎn)換成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 容器中已經(jīng)存在", 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 容器中已經(jīng)存在", beanName));
                return;
            }
        } catch (Exception ex) {
            // ignore
        }

        String strLocation = reqParam.get("localtion");
        // 使用file協(xié)議在本地尋找指定.class文件,file:///Users/fa1c0n/codeprojects/IdeaProjects/misc-classes/src/main/java/
        // 使用http協(xié)議到遠程地址尋找指定.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包后 動態(tài)注冊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(中間態(tài))-->URLClassLoader(最簡單)
    // SecureClassLoader 默認: this(checkCreateClassLoader(), null, getSystemClassLoader());

    /**
     * 熱加載Calculator接口的實現(xiàn) 反射方式
     *
     * @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 獲取類的字節(jié)碼
 * @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>
     * 返回類的字節(jié)碼
     *
     * @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暫存字節(jié)流
        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包中類的字節(jié)碼
     *
     * @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:要加斜杠轉(zhuǎn)義,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包中類的字節(jié)碼
     *
     * @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();

    }
}

總結(jié)

1.1JDKintrospector

當我們把這個類交給 spring 的時候,問題就出現(xiàn)了: spring 是一個已有的框架机错, 它并不知道 User 這個類爬范,也并不知道它有哪些方法、哪些屬性弱匪。

public void introspector(String clazz, Map<String, Object> properties) throws Exception {
    //反射創(chuàng)建實例
    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動態(tài)上傳jar包熱部署實戰(zhàn)

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

2SpringBoot動態(tài)注入Bean

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

3SpringBoot動態(tài)注入bean (系統(tǒng)注入漏洞原理)

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

4Java中動態(tài)加載字節(jié)碼的方法 (持續(xù)補充)Good

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

5java Introspector(內(nèi)省) 的使用場景以及為什么使用

http://www.reibang.com/p/418122d84e6e

6[深入理解Java:內(nèi)省(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 構(gòu)造方法斥难。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市帘饶,隨后出現(xiàn)的幾起案子哑诊,更是在濱河造成了極大的恐慌,老刑警劉巖及刻,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镀裤,死亡現(xiàn)場離奇詭異竞阐,居然都是意外死亡,警方通過查閱死者的電腦和手機暑劝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門骆莹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人担猛,你說我怎么就攤上這事幕垦。” “怎么了傅联?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵先改,是天一觀的道長。 經(jīng)常有香客問我蒸走,道長仇奶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任载碌,我火速辦了婚禮猜嘱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嫁艇。我一直安慰自己,他們只是感情好弦撩,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布步咪。 她就那樣靜靜地躺著,像睡著了一般益楼。 火紅的嫁衣襯著肌膚如雪猾漫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天感凤,我揣著相機與錄音悯周,去河邊找鬼。 笑死陪竿,一個胖子當著我的面吹牛禽翼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播族跛,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼闰挡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了礁哄?” 一聲冷哼從身側(cè)響起长酗,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桐绒,沒想到半個月后夺脾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體之拨,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年咧叭,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚀乔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡佳簸,死狀恐怖乙墙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情生均,我是刑警寧澤听想,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站马胧,受9級特大地震影響汉买,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜佩脊,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一蛙粘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧威彰,春花似錦出牧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽郭膛。三九已至皆串,卻和暖如春映皆,著一層夾襖步出監(jiān)牢的瞬間幕与,已是汗流浹背士袄。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工泞当, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留才沧,地道東北人成翩。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓氮惯,卻偏偏與公主長得像叮雳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子筐骇,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容