美文网首页框架建设收集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