diff --git a/README.md b/README.md index 38ab7bce0a48d56257c908ac7e7919eeed2d2c60..82d6e55e787aaabc809bd5430f40a6f9ed22c908 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 358a2b45f659bd83e3c1ca7c7b220c2f68fd516b..969d7051a56295bdd25406f624a18993dbfc4a76 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") @@ -18,24 +25,134 @@ public class SpringContextUtils implements BeanFactoryAware { listableBeanFactory = (DefaultListableBeanFactory) beanFactory; } + /** + * 注册单例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); - String beanName = beanName(type.getName()); + // 自动装配Bean依赖 + listableBeanFactory.autowireBean(obj); + // 注册单例Bean listableBeanFactory.registerSingleton(beanName, obj); } - public static void destroy(String className) { - String beanName = beanName(className); + /** + * 注册控制器 + * + * @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 beanName 控制器的bean名称 + */ + 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的名称 + */ 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); } 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 ae061158d77a65b702c2208775ea841778425bb9..8d4c50b326d67bc16d45f5711b156d7cea381eec 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 68b667e93cde5e6c284b8622c77cb72280570cae..1050cc4b026087900f0685dbf5f91b7693cc9bec 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 746ca0cccea2b5b9917bd4b7471f3542fd70533b..2eb43a206b74ec8917bece2e32fbec96c9f0f0c1 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; }