From ac4c5ad4bf326eb1edc91e631162ea75eff5cc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=8D=9A?= Date: Fri, 23 Feb 2024 12:37:07 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B3=A8=E5=86=8C=E5=8D=95=E4=BE=8BBean?= =?UTF-8?q?=E5=89=8D=E8=87=AA=E5=8A=A8=E8=A3=85=E9=85=8DBean=E5=B1=9E?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wubo/loader/util/SpringContextUtils.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/cn/wubo/loader/util/SpringContextUtils.java b/src/main/java/cn/wubo/loader/util/SpringContextUtils.java index 358a2b4..66181ce 100644 --- a/src/main/java/cn/wubo/loader/util/SpringContextUtils.java +++ b/src/main/java/cn/wubo/loader/util/SpringContextUtils.java @@ -18,23 +18,45 @@ public class SpringContextUtils implements BeanFactoryAware { listableBeanFactory = (DefaultListableBeanFactory) beanFactory; } + /** + * 注册单例Bean + * @param type Bean的类型 + */ public static void registerSingleton(Class type) { + // 创建Bean实例 T obj = listableBeanFactory.createBean(type); + // 获取Bean名称 String beanName = beanName(type.getName()); + // 自动装配Bean属性 + listableBeanFactory.autowireBean(obj); + // 注册单例Bean listableBeanFactory.registerSingleton(beanName, obj); } + + /** + * 销毁指定类名的单例对象。 + * + * @param className 要销毁的类名 + */ public static void destroy(String className) { String beanName = beanName(className); listableBeanFactory.destroySingleton(beanName); } + + /** + * 生成bean的名称 + * @param className 类名 + * @return bean的名称 + */ public static String beanName(String className) { String[] path = className.split("\\."); String beanName = path[path.length - 1]; return Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1); } + @SuppressWarnings("unchecked") public static T getBean(String name) { return (T) listableBeanFactory.getBean(name); -- Gitee From bcb16a818847a5e2b29678815fc5b5b5bc6a5160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=8D=9A?= Date: Fri, 23 Feb 2024 13:53:52 +0800 Subject: [PATCH 2/3] =?UTF-8?q?1.=20=E6=B3=A8=E5=86=8Cbean=E5=92=8Ccontrol?= =?UTF-8?q?ler=E6=97=B6=E8=87=AA=E5=8A=A8=E8=A3=85=E9=85=8D=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E6=B3=A8=E5=85=A5=E7=9A=84bean=202.=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=B3=A8=E5=86=8C=E3=80=81=E5=8D=B8=E8=BD=BDbean?= =?UTF-8?q?=E7=9A=84=E6=96=B9=E6=B3=95=203.=20=E4=BF=AE=E6=94=B9=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 47 +------- .../wubo/loader/util/SpringContextUtils.java | 112 ++++++++++++++++-- .../loader/util/bean_loader/DynamicBean.java | 21 ++-- .../util/class_loader/DynamicClassLoader.java | 6 + .../controller_loader/DynamicController.java | 49 ++------ 5 files changed, 133 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 38ab7bc..82d6e55 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.gitee.wb04307201 loader-util - 1.1.2 + 1.1.3 ``` @@ -136,52 +136,7 @@ Accept: application/json Hello,world! ``` -#### 如需在controller种调用其他bean,请使用MethodUtils.invokeBean("demoService", "testMethod", name) -```java - @GetMapping(value = "/loadControllerAndBean") - public String loadControllerAndBean() { - String fullClassName1 = "cn.wubo.loaderutiltest.DemoService"; - String javaSourceCode1 = """ - package cn.wubo.loaderutiltest; - - import org.springframework.stereotype.Service; - - @Service - public class DemoService { - - public String testMethod(String name) { - return String.format("Hello,%s!", name); - } - } - """; - - String beanName = DynamicBean.init(DynamicClass.init(javaSourceCode1, fullClassName1)).load(); - - String fullClassName2 = "cn.wubo.loaderutiltest.DemoController"; - String javaSourceCode2 = """ - package cn.wubo.loaderutiltest; - -import cn.wubo.loader.util.MethodUtils; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(value = "test") -public class DemoController { - - @GetMapping(value = "hello") - public String testMethod(@RequestParam(value = "name") String name) { - return MethodUtils.invokeBean("demoService", "testMethod", name); - } -} - """; - - return DynamicController.init(DynamicClass.init(javaSourceCode2, fullClassName2)).load(); - } -``` ## 6. proxy 动态代理切面 ```java @GetMapping(value = "/testAspect") diff --git a/src/main/java/cn/wubo/loader/util/SpringContextUtils.java b/src/main/java/cn/wubo/loader/util/SpringContextUtils.java index 66181ce..b2739cc 100644 --- a/src/main/java/cn/wubo/loader/util/SpringContextUtils.java +++ b/src/main/java/cn/wubo/loader/util/SpringContextUtils.java @@ -1,11 +1,18 @@ package cn.wubo.loader.util; +import cn.wubo.loader.util.exception.LoaderRuntimeException; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.stereotype.Component; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Map; @Component(value = "loaderUtilSpringContextUtils") @@ -20,33 +27,123 @@ public class SpringContextUtils implements BeanFactoryAware { /** * 注册单例Bean + * * @param type Bean的类型 */ public static void registerSingleton(Class type) { + registerSingleton(beanName(type.getName()), type); + } + + /** + * 注册单例Bean + * + * @param beanName Bean的名称 + * @param type Bean的类型 + */ + public static void registerSingleton(String beanName, Class type) { // 创建Bean实例 T obj = listableBeanFactory.createBean(type); - // 获取Bean名称 - String beanName = beanName(type.getName()); - // 自动装配Bean属性 + // 自动装配Bean依赖 listableBeanFactory.autowireBean(obj); // 注册单例Bean listableBeanFactory.registerSingleton(beanName, obj); } + /** + * 注册控制器 + * + * @param beanName 控制器的bean名称 + * @param type 控制器的类型 + */ + public static void registerController(String beanName, Class type) { + registerSingleton(beanName, type); + // 获取RequestMappingHandlerMapping对象 + RequestMappingHandlerMapping requestMappingHandlerMapping = getBean("requestMappingHandlerMapping"); + try { + // 获取detectHandlerMethods方法 + Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().getDeclaredMethod("detectHandlerMethods", Object.class); + // 设置detectHandlerMethods方法可访问 + method.setAccessible(true); + // 调用detectHandlerMethods方法,将控制器对象作为参数传入 + method.invoke(requestMappingHandlerMapping, beanName); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // 抛出异常 + throw new LoaderRuntimeException(e.getMessage(), e); + } + } /** - * 销毁指定类名的单例对象。 + * 注销控制器 * - * @param className 要销毁的类名 + * @param beanName 控制器的bean名称 */ - public static void destroy(String className) { - String beanName = beanName(className); + public static void unregisterController(String beanName) { + // 获取控制器对象 + Object obj = getBean(beanName); + // 获取控制器的类型 + Class type = obj.getClass(); + // 获取RequestMappingHandlerMapping对象 + RequestMappingHandlerMapping requestMappingHandlerMapping = getBean("requestMappingHandlerMapping"); + // 遍历控制器的方法 + ReflectionUtils.doWithMethods(type, method -> { + // 获取最具体的实现方法 + Method mostSpecificMethod = ClassUtils.getMostSpecificMethod(method, type); + try { + // 获取RequestMappingInfo实例 + Method declaredMethod = requestMappingHandlerMapping.getClass().getDeclaredMethod("getMappingForMethod", Method.class, Class.class); + declaredMethod.setAccessible(true); + RequestMappingInfo requestMappingInfo = (RequestMappingInfo) declaredMethod.invoke(requestMappingHandlerMapping, mostSpecificMethod, type); + // 如果RequestMappingInfo不为空,则注销对应的映射 + if (requestMappingInfo != null) requestMappingHandlerMapping.unregisterMapping(requestMappingInfo); + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { + throw new LoaderRuntimeException(e.getMessage(), e); + } + }); + // 销毁控制器的bean listableBeanFactory.destroySingleton(beanName); } + /** + * 判断是否包含指定类型的bean + * + * @param type 要判断的bean类型 + * @return 如果包含指定类型的bean则返回true,否则返回false + */ + public static Boolean containsBean(Class type) { + return listableBeanFactory.containsBean(beanName(type.getName())); + } + + /** + * 判断给定的bean名称是否存在于listableBeanFactory中 + * + * @param beanName 要判断的bean名称 + * @return 如果bean名称存在于listableBeanFactory中,则返回true;否则返回false + */ + public static Boolean containsBean(String beanName) { + return listableBeanFactory.containsBean(beanName); + } + + /** + * 销毁指定名称的单例对象。 + * + * @param beanName 要销毁的单例对象的名称 + */ + public static void destroy(String beanName) { + listableBeanFactory.destroySingleton(beanName); + } + + /** + * 销毁指定类型的单例对象。 + * + * @param type 指定的类型 + */ + public static void destroy(Class type) { + listableBeanFactory.destroySingleton(beanName(type.getName())); + } /** * 生成bean的名称 + * * @param className 类名 * @return bean的名称 */ @@ -56,7 +153,6 @@ public class SpringContextUtils implements BeanFactoryAware { return Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1); } - @SuppressWarnings("unchecked") public static T getBean(String name) { return (T) listableBeanFactory.getBean(name); diff --git a/src/main/java/cn/wubo/loader/util/bean_loader/DynamicBean.java b/src/main/java/cn/wubo/loader/util/bean_loader/DynamicBean.java index ae06115..8d4c50b 100644 --- a/src/main/java/cn/wubo/loader/util/bean_loader/DynamicBean.java +++ b/src/main/java/cn/wubo/loader/util/bean_loader/DynamicBean.java @@ -1,7 +1,7 @@ package cn.wubo.loader.util.bean_loader; -import cn.wubo.loader.util.class_loader.DynamicClass; import cn.wubo.loader.util.SpringContextUtils; +import cn.wubo.loader.util.class_loader.DynamicClass; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -24,20 +24,23 @@ public class DynamicBean { return new DynamicBean(dynamicClass); } - /** - * 加载动态生成的类并将其注册为单例Bean, - * 销毁同名的Bean对象,返回新加载类的Bean名称 - * @return 新加载类的Bean名称 + * 加载动态类并注册到Spring容器中 + * + * @return 注册的bean名称 */ public String load() { + // 获取动态类的bean名称 String beanName = SpringContextUtils.beanName(dynamicClass.getFullClassName()); - // 销毁Bean - SpringContextUtils.destroy(beanName); - // 每次都是new新的ClassLoader对象 + // 如果Spring容器中已存在该bean,则销毁该bean + if (Boolean.TRUE.equals(SpringContextUtils.containsBean(beanName))) SpringContextUtils.destroy(beanName); + // 加载动态类并获取其Class对象 Class type = dynamicClass.compiler().load(); - SpringContextUtils.registerSingleton(type); + // 将该Class对象注册为Spring容器中的单例bean + SpringContextUtils.registerSingleton(beanName, type); + // 返回注册的bean名称 return beanName; } + } diff --git a/src/main/java/cn/wubo/loader/util/class_loader/DynamicClassLoader.java b/src/main/java/cn/wubo/loader/util/class_loader/DynamicClassLoader.java index 68b667e..1050cc4 100644 --- a/src/main/java/cn/wubo/loader/util/class_loader/DynamicClassLoader.java +++ b/src/main/java/cn/wubo/loader/util/class_loader/DynamicClassLoader.java @@ -20,6 +20,12 @@ public class DynamicClassLoader extends SecureClassLoader { super(parent); } + /** + * 添加类信息 + * + * @param fullClassName 类的完全限定名 + * @param classData 类的数据 + */ public void addClass(String fullClassName, byte[] classData) { classBytes.put(fullClassName, classData); } diff --git a/src/main/java/cn/wubo/loader/util/controller_loader/DynamicController.java b/src/main/java/cn/wubo/loader/util/controller_loader/DynamicController.java index 746ca0c..2eb43a2 100644 --- a/src/main/java/cn/wubo/loader/util/controller_loader/DynamicController.java +++ b/src/main/java/cn/wubo/loader/util/controller_loader/DynamicController.java @@ -2,15 +2,7 @@ package cn.wubo.loader.util.controller_loader; import cn.wubo.loader.util.SpringContextUtils; import cn.wubo.loader.util.class_loader.DynamicClass; -import cn.wubo.loader.util.exception.LoaderRuntimeException; import lombok.extern.slf4j.Slf4j; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; @Slf4j public class DynamicController { @@ -27,41 +19,20 @@ public class DynamicController { } /** - * 加载方法 - * @return bean名称 + * 加载动态类并注册到Spring容器中 + * + * @return 注册的bean名称 */ public String load() { - // 获取bean名称 + // 获取动态类的bean名称 String beanName = SpringContextUtils.beanName(dynamicClass.getFullClassName()); - // 获取RequestMappingHandlerMapping实例 - RequestMappingHandlerMapping requestMappingHandlerMapping = SpringContextUtils.getBean("requestMappingHandlerMapping"); - // 加载类 + // 如果Spring容器中已存在该bean,则注销该bean + if (Boolean.TRUE.equals(SpringContextUtils.containsBean(beanName))) SpringContextUtils.unregisterController(beanName); + // 加载动态类并获取其Class对象 Class type = dynamicClass.compiler().load(); - // 遍历类的方法 - ReflectionUtils.doWithMethods(type, method -> { - // 获取最具体的实现方法 - Method mostSpecificMethod = ClassUtils.getMostSpecificMethod(method, type); - try { - // 获取RequestMappingInfo实例 - Method declaredMethod = requestMappingHandlerMapping.getClass().getDeclaredMethod("getMappingForMethod", Method.class, Class.class); - declaredMethod.setAccessible(true); - RequestMappingInfo requestMappingInfo = (RequestMappingInfo) declaredMethod.invoke(requestMappingHandlerMapping, mostSpecificMethod, type); - // 如果RequestMappingInfo不为空,则注销对应的映射 - if (requestMappingInfo != null) requestMappingHandlerMapping.unregisterMapping(requestMappingInfo); - } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { - throw new LoaderRuntimeException(e.getMessage(), e); - } - }); - // 销毁bean - SpringContextUtils.destroy(beanName); - try { - // 检测HandlerMethods - Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().getDeclaredMethod("detectHandlerMethods", Object.class); - method.setAccessible(true); - method.invoke(requestMappingHandlerMapping, type.getConstructor().newInstance()); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { - throw new LoaderRuntimeException(e.getMessage(), e); - } + // 将该Class对象注册为Spring容器中的控制器 + SpringContextUtils.registerController(beanName, type); + // 返回注册的bean名称 return beanName; } -- Gitee From a94da195d9406223b5f545dc733ce9de3e6c5fc3 Mon Sep 17 00:00:00 2001 From: wubo Date: Fri, 23 Feb 2024 20:35:22 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=88=A0=E9=99=A4@SuppressWarnings("unchec?= =?UTF-8?q?ked")?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/cn/wubo/loader/util/SpringContextUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/cn/wubo/loader/util/SpringContextUtils.java b/src/main/java/cn/wubo/loader/util/SpringContextUtils.java index b2739cc..969d705 100644 --- a/src/main/java/cn/wubo/loader/util/SpringContextUtils.java +++ b/src/main/java/cn/wubo/loader/util/SpringContextUtils.java @@ -153,7 +153,6 @@ public class SpringContextUtils implements BeanFactoryAware { return Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1); } - @SuppressWarnings("unchecked") public static T getBean(String name) { return (T) listableBeanFactory.getBean(name); } -- Gitee