Groovy 动态加载业务

48 阅读3分钟

依赖

<dependency> 
     <groupId>org.codehaus.groovy</groupId> 
     <artifactId>groovy-all</artifactId> 
     <version>2.4.12</version> 
</dependency> 

1.利用Spring Boot的CommandLineRunner注册SpringBean、GroovyBean

  • 初始化加载项目中RuleFilter的Spring Bean
  • 直接使用@Autowired注解配合List即可获取所有RuleFilter的子类
  • 初始化Groovy动态扫描的监控间隔,目录配置
  • 这里配置是每隔10秒扫描目录xxx,新增或修改的文件用于编译加载
@Component 
public class GroovyRunner implements CommandLineRunner { 
    @Autowired 
    List<RuleFilter> ruleFilterList; 
    @Override 
    public void run(String... args) throws Exception { 
        // 初始化加载项目中RuleFilter的Springbean 
        RuleFilterLoader.getInstance().initSpringRuleFilter(ruleFilterList); 
        try { 
            // 每隔多少秒,扫描目录下的groovy文件 
            RuleFilterFileManager.init(5"C:\\rule\\groovy"); 
        } catch (Exception e) { 
            e.printStackTrace(); 
            throw new RuntimeException(); 
        } 
    } 
} 

项目内不变的规则以java实现继承RuleFilter

@Component 
public class JavaRule extends RuleFilter { 
    /** 
     * 具体规则执行 
     * @param msg 
     */ 
    @Override 
    public void run(String msg) { 
        System.out.println(" === Java 实现的业务规则 order = 1 , msg = " + msg + " === "); 
    } 
    /** 
     * 该规则是否被执行 
     * @return 
     */ 
    @Override 
    public boolean shouldRun() { 
        return true; 
    } 
 
    /** 
     * 该规则执行的顺序 
     * @return 
     */ 
    @Override 
    public int runOrder() { 
        return 1; 
    } 
} 

项目内经常变动的以Groovy来实现

public class GroovyRule extends RuleFilter { 
    @Override 
    public void run(String msg) { 
 
        System.out.println(" === Groovy 实现的业务规则 order = " + runOrder() + ", msg = " + msg + " === "); 
    } 
    @Override 
    public boolean shouldRun() { 
        return true; 
    } 
    @Override 
    public int runOrder() { 
        return 2; 
    } 
} 

使用RuleFilterProcessor来加载RuleFiler

@RestController 
@RequestMapping("/groovy") 
public class GroovyController { 
    @Autowired 
    private RuleFilterProcessor ruleFilterProcessor; 
 
    @GetMapping() 
    @ApiOperation("测试groovy的动态加载") 
    public void transaction(@RequestParam String msg) { 
        ruleFilterProcessor.runRuleFilters(msg); 
    } 
} 

启动并验证

浏览器访问:http://localhost:8080/groovy?msg=laker%20666

=== Java 实现的业务规则 order = 1 , msg = laker 666 ===  
=== Groovy 实现的业务规则 order = 2 , msg = laker 666 ===  

新增一个Groovy后

public class Groovy2Rule extends RuleFilter { 
    @Override 
    public void run(String msg) { 
        System.out.println(" === Groovy 实现的业务规则 order = " + runOrder() + ", msg = " + msg + " === "); 
        List<RuleFilter> ruleFilters = RuleFilterLoader.getInstance().getFilters(); 
        for (RuleFilter ruleFilter : ruleFilters) { 
            System.out.println(ruleFilter.getClass().getName()); 
        } 
    } 
    @Override 
    public boolean shouldRun() { 
        return true; 
    } 
 
    @Override 
    public int runOrder() { 
        return 3; 
    } 
} 

不用重启服务,浏览器访问:http://localhost:8080/groovy?msg=laker%20666

 === Groovy 实现的业务规则 order = 0 , msg = laker 666 ===  
 === Java 实现的业务规则 order = 1 , msg = laker 666 ===  
 === Groovy 实现的业务规则 order = 3, msg = laker 666 ===  
com.laker.map.moudle.groovy.javarule.GroovyRule 
com.laker.map.moudle.groovy.javarule.JavaRule 
com.laker.map.moudle.groovy.Groovy2Rule 

核心的模块

  • RuleFilter :规则过滤器抽象类,用于扩展实现业务规则,供Java和Groovy继承
  • RuleFilterLoader :规则过滤器加载器,用于加载基于Spring的RuleFilter实现类和动态编译指定文件基于Groovy的RuleFilter实现类
  • RuleFilterFileManager : 一个独立线程轮询监听指定目录文件的变化配合RuleFilterLoader ( 规则过滤器加载器)使用
  • RuleFilterProcessor: 业务规则处理器核心入口

RuleFilter.java

public abstract class RuleFilter implements IRule, Comparable<RuleFilter> { 
 
    abstract public int runOrder(); 
 
    @Override 
    public int compareTo(RuleFilter ruleFilter) { 
        return Integer.compare(this.runOrder(), ruleFilter.runOrder()); 
    } 
    ... 
} 

RuleFilterLoader.java

public class RuleFilterLoader { 
    public boolean putFilter(File file) throws Exception { 
        String sName = file.getAbsolutePath() + file.getName(); 
        if (filterClassLastModified.get(sName) != null && (file.lastModified() != filterClassLastModified.get(sName))) { 
            LOG.debug("reloading filter " + sName); 
            filterRegistry.remove(sName); 
        } 
        RuleFilter filter = filterRegistry.get(sName); 
        if (filter == null) { 
            Class clazz = compile(file); 
            if (!Modifier.isAbstract(clazz.getModifiers())) { 
                filter = (RuleFilter) clazz.newInstance(); 
                filterRegistry.put(file.getAbsolutePath() + file.getName(), filter); 
                ruleFilters.clear(); 
                filterClassLastModified.put(sName, file.lastModified()); 
                return true; 
            } 
        } 
        return false; 
    } 
    public List<RuleFilter> getFilters() { 
        if (CollUtil.isNotEmpty(ruleFilters)) { 
            return ruleFilters; 
        } 
        ruleFilters.addAll(springRuleFilterList); 
        ruleFilters.addAll(this.filterRegistry.values()); 
        Collections.sort(ruleFilters); 
        return ruleFilters; 
    } 
    private Class compile(File file) throws IOException { 
        GroovyClassLoader loader = getGroovyClassLoader(); 
        Class groovyClass = loader.parseClass(file); 
        return groovyClass; 
    } 
    GroovyClassLoader getGroovyClassLoader() { 
        return new GroovyClassLoader(); 
    } 
    ... 
} 

RuleFilterFileManager.java

public class RuleFilterFileManager { 
    public static void init(int pollingIntervalSeconds, String... directories)  { 
        if (INSTANCE == null) INSTANCE = new RuleFilterFileManager(); 
 
        INSTANCE.aDirectories = directories; 
        INSTANCE.pollingIntervalSeconds = pollingIntervalSeconds; 
        INSTANCE.manageFiles(); 
        INSTANCE.startPoller(); 
    } 
    void startPoller() { 
        poller = new Thread("GroovyRuleFilterFileManagerPoller") { 
            @Override 
            public void run() { 
                while (bRunning) { 
                    try { 
                        sleep(pollingIntervalSeconds * 1000); 
                        manageFiles(); 
                    } catch (Exception e) { 
                        e.printStackTrace(); 
                    } 
                } 
            } 
        }; 
        poller.setDaemon(true); 
        poller.start(); 
    } 
   void processGroovyFiles(List<File> aFiles) throws Exception, InstantiationException, IllegalAccessException { 
 
        for (File file : aFiles) { 
            RuleFilterLoader.getInstance().putFilter(file); 
        } 
    } 
    void manageFiles() throws Exception, IllegalAccessException, InstantiationException { 
        List<File> aFiles = getFiles(); 
        processGroovyFiles(aFiles); 
    } 
    ... 
} 

RuleFilterProcessor.java

@Component 
public class RuleFilterProcessor { 
    public void runRuleFilters(String msg) { 
        List<RuleFilter> list = RuleFilterLoader.getInstance().getFilters(); 
        if (list != null) { 
            list.forEach(ruleFilter -> { 
                if (ruleFilter.shouldRun()) { 
                    ruleFilter.run(msg); 
                } 
            }); 
        } 
    } 
}