20220625_項目熱部署學習筆記
1概述
本文主要是結(jié)合代碼,學習一下spring中Bean的動態(tài)加載,具體實現(xiàn)看代碼,主要分以下幾種情況:
- 本地項目中的類加載到當前項目的spring 容器。
- 外部磁盤中的單個clazz文件基于自定義加載器的方式加載到當前項目spring 容器中。
- 外部磁盤中的單個clazz文件基于URLClassLoader加載器的方式加載到當前項目spring 容器中
- 外部磁盤jar中的clazz文件基于注解的方式加載到當前項目spring 容器中趟妥。
- 外部磁盤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)
2SpringBoot動態(tài)注入Bean
3SpringBoot動態(tài)注入bean (系統(tǒng)注入漏洞原理)
https://blog.csdn.net/weixin_45943597/article/details/124176226
4Java中動態(tài)加載字節(jié)碼的方法 (持續(xù)補充)Good
5java Introspector(內(nèi)省) 的使用場景以及為什么使用
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)造方法斥难。