美文网首页框架建设收集Springboot
spring + groovy 实现动态代码注入执行

spring + groovy 实现动态代码注入执行

作者: jackcooper | 来源:发表于2020-06-19 15:25 被阅读0次

    在工作流中各个模板引擎都需要要执行一个动态业务,这些动态业务有多种实现方式,最常用的就是用户自己写一段脚本文件,然后工作流引擎执行到这里的时候,运行这个脚本文件。

    1、添加groovy依赖pom.xml

    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy</artifactId>
        <version>2.5.1</version>
    </dependency>
    

    2、Spring上线问工具类

    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    public class SpringContextUtils implements ApplicationContextAware {
    
        static ApplicationContext context;
    
        @Override
        public void setApplicationContext(ApplicationContext context)
                throws BeansException {
            SpringContextUtils.context = context;
        }
    
        public static ApplicationContext getContext() {
            return context;
        }
    
        public static void autowireBean(Object bean) {
            context.getAutowireCapableBeanFactory().autowireBean(bean);
        }
    
        public static <T> T getBean(Class<T> clazz) {
            return context.getBean(clazz);
        }
    }
    

    3、写个控制器运行groovy脚本

    import com.ruizton.util.SpringContextUtils;
    import groovy.lang.GroovyClassLoader;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.lang.reflect.Method;
    
    @RestController
    public class GroovyController {
    
        @RequestMapping("/runScript")
        public Object runScript(String script) throws Exception {
            if (script != null) {
             // 这里其实就是groovy的api动态的加载生成一个Class,然后反射生成对象,然后执行run方法,最后返回结果
             // 最精华的地方就是SpringContextUtils.autowireBean,可以实现自动注入bean,
                Class clazz = new GroovyClassLoader().parseClass(script);
                Method run = clazz.getMethod("run");
                Object o = clazz.newInstance();
                SpringContextUtils.autowireBean(o);
                Object ret = run.invoke(o);
                return ret;
            } else {
                return "no script";
            }
        }
    }
    

    4、在线写groovy脚本调用3中接口并返回运行结果Foo.groovy

    import org.springframework.beans.factory.annotation.Autowired
    
    class Foo {
    
        @Autowired
        UserService userService;
    
        Object run() {
            // do something
            def user = userService.findById(666);
            if (user != null) {
                return user .name
            }
            return null
        }
    
    }
    

    不写类直接写代码块也是可以的
    test.groovy

    def sum = 1 * 2 * 3
    return "result= " + sum
    

    上面代码也会生成一个Class对象,里面的代码默认在run方法下面,所以控制器哪里都是调用的run方法.


    动态注册Bean,Controller到Spring容器

    结合上面的SpringContextUtil使用

    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ConfigurableApplicationContext;
    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.Method;
    
    /**
     * 动态注册对象到容器
     */
    @Slf4j
    public class DynamicLoadUtils {
        /**
         * 动态添加controller到spring容器
         * @param controllerBeanName
         * @throws Exception
         */
        public static void registerController(String controllerBeanName) throws Exception {
            final RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
                    SpringContextUtil.applicationContext.getBean("requestMappingHandlerMapping");
    
            if (requestMappingHandlerMapping != null) {
                String handler = controllerBeanName;
                Object controller = SpringContextUtil.applicationContext.getBean(handler);
                if (controller == null) {
                    return;
                }
                unregisterController(controllerBeanName);
                //注册Controller
                Method method = requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().
                        getDeclaredMethod("detectHandlerMethods", Object.class);
                method.setAccessible(true);
                method.invoke(requestMappingHandlerMapping, handler);
                log.info("==>动态注入controller:{}",controllerBeanName);
            }
        }
    
        /**
         * 动态删除controller
         * @param controllerBeanName
         */
        public static void unregisterController(String controllerBeanName) {
            final RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
                    SpringContextUtil.applicationContext.getBean("requestMappingHandlerMapping");
            if (requestMappingHandlerMapping != null) {
                String handler = controllerBeanName;
                Object controller = SpringContextUtil.applicationContext.getBean(handler);
                if (controller == null) {
                    return;
                }
                final Class<?> targetClass = controller.getClass();
                ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
                    @Override
                    public void doWith(Method method) {
                        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                        try {
                            Method createMappingMethod = RequestMappingHandlerMapping.class.
                                    getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
                            createMappingMethod.setAccessible(true);
                            RequestMappingInfo requestMappingInfo = (RequestMappingInfo)
                                    createMappingMethod.invoke(requestMappingHandlerMapping, specificMethod, targetClass);
                            if (requestMappingInfo != null) {
                                requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }, ReflectionUtils.USER_DECLARED_METHODS);
            }
        }
    
        /**
         * 向spring容器中添加bean
         * @param className
         * @param serviceName
         * @param app
         */
        public static void addBean(String className, String serviceName, ApplicationContext app) {
            try {
                Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
                BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
                registerBean(serviceName, beanDefinitionBuilder.getRawBeanDefinition(), app);
            } catch (ClassNotFoundException e) {
                System.out.println(className + ",主动注册失败.");
            }
        }
    
        /**
         * 向spring容器中添加bean
         * @param clazz
         * @param serviceName
         * @param app
         */
        public static void addBean(Class clazz, String serviceName, ApplicationContext app) {
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            registerBean(serviceName, beanDefinitionBuilder.getRawBeanDefinition(), app);
        }
    
        /**
         * 向spring容器注册bean核心代码
         * @param beanName
         * @param beanDefinition
         * @param context
         */
        private static void registerBean(String beanName, BeanDefinition beanDefinition, ApplicationContext context) {
            ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) context;
            BeanDefinitionRegistry beanDefinitonRegistry = (BeanDefinitionRegistry) configurableApplicationContext
                    .getBeanFactory();
            beanDefinitonRegistry.registerBeanDefinition(beanName, beanDefinition);
            log.info("==> 动态注册bean:{}",beanName);
        }
    
    }
    

    参考文档:
    https://yq.aliyun.com/articles/665345
    https://blog.csdn.net/qq_38206090/article/details/103101323?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-13.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-13.nonecase

    相关文章

      网友评论

        本文标题:spring + groovy 实现动态代码注入执行

        本文链接:https://www.haomeiwen.com/subject/yjnvxktx.html