JAVA代码的热部署,动态语言Groovy使用的三种方式

您所在的位置:网站首页 java静态语言 JAVA代码的热部署,动态语言Groovy使用的三种方式

JAVA代码的热部署,动态语言Groovy使用的三种方式

2024-07-16 16:38| 来源: 网络整理| 查看: 265

JAVA代码的热部署,动态语言Groovy使用的三种方式一、概述

Groovy是构建在JVM上的一个轻量级却强大的动态语言, 它结合了Python、Ruby和Smalltalk的许多强大的特性.

Groovy就是用Java写的 , Groovy语法与Java语法类似, Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码, 相对于Java, 它在编写代码的灵活性上有非常明显的提升,Groovy 可以使用其他 Java 语言编写的库.

Groovy通常是被用来扩展现有的代码,就是说,当你需要某个实现类动态生成的时候,就可以使用Groovy来完成,比如:

动态类实现从文件生成,改动后能立即检测到。动态类实现从数据库中生成,改动后能立即检测到。动态类作为Spring的bean被管理起来,改动后能立即检测到。

这次,我要讲的就是这三种方式。

二、准备工作

本篇的使用场景是:假设有一个规则接口,它的实现可以是本地的JAVA代码实现,也可以是groovy文件实现,也可以通过数据库存储的Groovy脚本实现,也可以是Spring管理的bean。然后这多种不同的规则实现,放到一个列表中,统一执行。

在xxl-job中,所有的任务都要实现IJobHandler接口,它的web Glue方式就是基于Groovy实现的。

2.1 规则接口IRule

IRule定义了这个规则接口。

IRule:

代码语言:javascript复制package cn.pomit.skill.groovy.rule; public interface IRule { static final int NORMAL_TYPE = 0; static final int GROOVY_FILE_TYPE = 1; static final int GROOVY_DB_TYPE = 2; static final int GROOVY_SPRING_TYPE = 3; int getType(); void printInfo(); }2.2 普通的JAVA实现

如果是普通的Java实现,就很简单。

NormalJavaRule:

代码语言:javascript复制package cn.pomit.skill.groovy.rule.normal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.pomit.skill.groovy.rule.IRule; public class NormalJavaRule implements IRule { private Logger log = LoggerFactory.getLogger(this.getClass()); @Override public int getType() { return NORMAL_TYPE; } @Override public void printInfo() { log.info("这是正常的JAVA代码"); printInfoHigh(); } //加这段是为了测试改动后输出是否有变化,在ide里,只靠上面的打印看不出来效果 public void printInfoHigh() { log.info("这是正常的JAVA代码的代码"); } }三、非Spring环境Groovy文件方式3.1 Groovy文件

定义一个GroovyFileRule的Groovy文件,执行自己的规则。

GroovyFileRule:

代码语言:javascript复制package cn.pomit.skill.groovy.rule.file; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.pomit.skill.groovy.rule.IRule; class GroovyFileRule implements IRule { private Logger log = LoggerFactory.getLogger(this.getClass()); @Override public int getType() { return GROOVY_FILE_TYPE; } @Override public void printInfo() { log.info("这是一段来自Groovy文件的代码"); printInfoHigh(); } public void printInfoHigh() { log.info("这是一段来自Groovy文件的代码的代码"); } }3.2 读取并生成实例

我这里定义了一个GroovyFactory,用来实现将Groovy文件生成IRule的实现.

GroovyFactory :

代码语言:javascript复制package cn.pomit.skill.groovy.rule; import java.net.URL; import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyCodeSource; public class GroovyFactory { private static GroovyFactory groovyFactory = new GroovyFactory(); private GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); public static GroovyFactory getInstance() { return groovyFactory; } /** * 根据groovy文件路径生成IRule的实现 * @param packagePath * @return * @throws Exception */ public IRule getIRuleFromPackage(String filePath) throws Exception { URL url = this.getClass().getClassLoader().getResource(filePath); Class clazz = groovyClassLoader.parseClass(new GroovyCodeSource(url)); if (clazz != null) { Object instance = clazz.newInstance(); if (instance != null) { if (instance instanceof IRule) { return (IRule) instance; } } } throw new IllegalArgumentException("读取groovy文件异常"); } }3.3 使用这个实现代码语言:javascript复制 //调用普通的JAVA实现做对比 List rules = new ArrayList(); NormalJavaRule normalJavaRule = new NormalJavaRule(); rules.add(normalJavaRule); //直接读取Groovy文件生成IRule实现 IRule groovyFile = GroovyFactory.getInstance() .getIRuleFromPackage("cn/pomit/skill/groovy/rule/file/GroovyFileRule.groovy"); rules.add(groovyFile);

执行并调用list里面元素的printInfo方法,结论:

代码语言:javascript复制2020-01-02 12:16:25,738 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.normal.NormalJavaRule] 这是正常的JAVA代码 2020-01-02 12:16:25,739 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.normal.NormalJavaRule] 这是正常的JAVA代码的代码 2020-01-02 12:16:25,744 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyFileRule] 这是一段来自Groovy文件的代码 2020-01-02 12:16:25,745 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyFileRule] 这是一段来自Groovy文件的代码的代码

改动JAVA普通的实现,打印的东西不会改动(如果只是改变打印内容,ide可能会做优化,可以直接改动逻辑,会更明显)。

改动Groovy文件,打印的东西马上就做改动了。

四、数据库Groovy脚本方式4.1 Groovy脚本

定义一个GroovyDbRule 的脚本,执行自己的规则。GroovyDbRule 脚本存储在数据库中,

GroovyDbRule脚本如下:

代码语言:javascript复制package cn.pomit.skill.groovy.rule.file; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.pomit.skill.groovy.rule.IRule; class GroovyDbRule implements IRule { private Logger log = LoggerFactory.getLogger(this.getClass()); @Override public int getType() { return GROOVY_DB_TYPE; } @Override public void printInfo() { log.info("这是一段来自数据库的Groovy脚本"); printInfoHigh(); } public void printInfoHigh() { log.info("这是一段来自数据库的Groovy脚本的代码"); } }

建表语句及插入以上脚本:

代码语言:javascript复制CREATE TABLE `pomit_rule` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `rule` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, `create_time` timestamp(0) DEFAULT CURRENT_TIMESTAMP, `update_time` timestamp(0) DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0), `visible` int(11) DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of pomit_rule -- ---------------------------- INSERT INTO `pomit_rule` VALUES (5, 'GroovyDbRule', 'package cn.pomit.skill.groovy.rule.file;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\nimport cn.pomit.skill.groovy.rule.IRule;\r\n\r\nclass GroovyDbRule implements IRule {\r\n private Logger log = LoggerFactory.getLogger(this.getClass());\r\n \r\n @Override\r\n public int getType() {\r\n return GROOVY_DB_TYPE;\r\n }\r\n\r\n @Override\r\n public void printInfo() {\r\n log.info(\"这是一段来自数据库的Groovy脚本\");\r\n printInfoHigh();\r\n }\r\n \r\n public void printInfoHigh() {\r\n log.info(\"这是一段来自数据库的Groovy脚本的代码\");\r\n }\r\n}\r\n', '2020-01-02 10:36:01', '2020-01-02 10:36:01', NULL);4.2 读取并生成实例

在GroovyFactory,加入从字符串生成IRule实现的方法.

在上面3.2中的GroovyFactory 中,加入下面的代码:

代码语言:javascript复制/** * 根据脚本内容生成IRule的实现 * @param code * @return * @throws Exception */ public IRule getIRuleFromCode(String code) throws Exception { Class clazz = groovyClassLoader.parseClass(code); if (clazz != null) { Object instance = clazz.newInstance(); if (instance != null) { if (instance instanceof IRule) { return (IRule) instance; } } } throw new IllegalArgumentException("读取groovy脚本异常"); }4.3 使用这个实现

这里使用到了数据库,所以要使用ORM框架,我使用了Mybatis。RulesDao是Mybatis对4.1中表pomit_rule操作的一个Mapper。

代码语言:javascript复制 //调用普通的JAVA实现做对比 List rules = new ArrayList(); NormalJavaRule normalJavaRule = new NormalJavaRule(); rules.add(normalJavaRule); //直接读取Groovy文件生成IRule实现 IRule groovyFile = GroovyFactory.getInstance() .getIRuleFromPackage("cn/pomit/skill/groovy/rule/file/GroovyFileRule.groovy"); rules.add(groovyFile); //从Db的Groovy脚本中生成IRule实现 Rules ruleGroovy = rulesDao.getByName("GroovyDbRule"); if (ruleGroovy != null) { IRule groovyDb = GroovyFactory.getInstance().getIRuleFromCode(ruleGroovy.getRule()); rules.add(groovyDb); }

执行并调用list里面元素的printInfo方法,结论:

代码语言:javascript复制2020-01-02 12:16:25,738 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.normal.NormalJavaRule] 这是正常的JAVA代码 2020-01-02 12:16:25,739 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.normal.NormalJavaRule] 这是正常的JAVA代码的代码 2020-01-02 12:16:25,744 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyFileRule] 这是一段来自Groovy文件的代码 2020-01-02 12:16:25,745 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyFileRule] 这是一段来自Groovy文件的代码的代码 2020-01-02 12:16:25,745 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyDbRule] 这是一段来自数据库的Groovy脚本 2020-01-02 12:16:25,745 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyDbRule] 这是一段来自数据库的Groovy脚本的代码

改动JAVA普通的实现,打印的东西不会改动(如果只是改变打印内容,ide可能会做优化,可以直接改动逻辑,会更明显)。

改动Groovy文件,打印的东西马上就做改动了。

改动数据库中的Groovy脚本,打印的东西马上就做改动了。

五、Spring中使用Groovy的方式5.1 Groovy文件

定义一个SpringGroovyRule 文件,执行自己的规则。这里拿它来测试Spring是如何将Groovy文件作为Bean来使用,不再手动读取Groovy文件。

SpringGroovyRule如下:

代码语言:javascript复制package cn.pomit.skill.groovy.rule.spring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.pomit.skill.groovy.rule.IRule; import org.springframework.stereotype.Service; public class SpringGroovyRule implements IRule { private Logger log = LoggerFactory.getLogger(this.getClass()); @Override public int getType() { return GROOVY_SPRING_TYPE; } @Override public void printInfo() { log.info("这是一段Spring的Groovy代码"); printInfoHigh(); } public void printInfoHigh() { log.info("这是一段Spring的Groovy代码的代码"); } }5.2 读取并生成实例

建立Spring的配置文件(SpringBoot也要这样玩,因为Groovy文件没有JAVA配置的方式生成Bean):spring-groovy.xml。

代码语言:javascript复制 5.3 使用这个实现

如果是在SpringBoot下,需要引入上面的xml文件:@ImportResource(locations={"classpath:spring-groovy.xml"})

注入这个bean:

代码语言:javascript复制 @Resource(name = "springGroovyRule") IRule springGroovyRule;

可以将它放到之前定义的List rules中,并调用里面元素的printInfo方法,执行完成如下:

代码语言:javascript复制2020-01-02 12:16:25,738 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.normal.NormalJavaRule] 这是正常的JAVA代码 2020-01-02 12:16:25,739 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.normal.NormalJavaRule] 这是正常的JAVA代码的代码 2020-01-02 12:16:25,744 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyFileRule] 这是一段来自Groovy文件的代码 2020-01-02 12:16:25,745 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyFileRule] 这是一段来自Groovy文件的代码的代码 2020-01-02 12:16:25,745 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyDbRule] 这是一段来自数据库的Groovy脚本 2020-01-02 12:16:25,745 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.file.GroovyDbRule] 这是一段来自数据库的Groovy脚本的代码 2020-01-02 12:16:25,747 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.spring.SpringGroovyRule] 这是一段Spring的Groovy代码 2020-01-02 12:16:25,748 [http-nio-8086-exec-1][IP:|USER:][INFO cn.pomit.skill.groovy.rule.spring.SpringGroovyRule] 这是一段Spring的Groovy代码的代码六、过程中用到的其他文件6.1 完整的GroovyFactory代码语言:javascript复制package cn.pomit.skill.groovy.rule; import java.net.URL; import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyCodeSource; public class GroovyFactory { private static GroovyFactory groovyFactory = new GroovyFactory(); private GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); public static GroovyFactory getInstance() { return groovyFactory; } /** * 根据groovy文件路径生成IRule的实现 * @param packagePath * @return * @throws Exception */ public IRule getIRuleFromPackage(String filePath) throws Exception { URL url = this.getClass().getClassLoader().getResource(filePath); Class clazz = groovyClassLoader.parseClass(new GroovyCodeSource(url)); if (clazz != null) { Object instance = clazz.newInstance(); if (instance != null) { if (instance instanceof IRule) { return (IRule) instance; } } } throw new IllegalArgumentException("读取groovy文件异常"); } /** * 根据脚本内容生成IRule的实现 * @param code * @return * @throws Exception */ public IRule getIRuleFromCode(String code) throws Exception { Class clazz = groovyClassLoader.parseClass(code); if (clazz != null) { Object instance = clazz.newInstance(); if (instance != null) { if (instance instanceof IRule) { return (IRule) instance; } } } throw new IllegalArgumentException("读取groovy脚本异常"); } }6.2 完整的RuleFactory代码语言:javascript复制package cn.pomit.skill.groovy.rule; import java.util.ArrayList; import java.util.List; import cn.pomit.skill.groovy.rule.db.Rules; import cn.pomit.skill.groovy.rule.db.RulesDao; import cn.pomit.skill.groovy.rule.normal.NormalJavaRule; public class RuleFactory { private static volatile RuleFactory singleton = null; private RuleFactory() { } public static RuleFactory getInstance() { // 第一次校验singleton是否为空 if (singleton == null) { synchronized (RuleFactory.class) { // 第二次校验singleton是否为空 if (singleton == null) { singleton = new RuleFactory(); } } } return singleton; } private RulesDao rulesDao; private RuleFactory(RulesDao rulesDao) { this.rulesDao = rulesDao; } public static RuleFactory getInstance(RulesDao rulesDao) { // 第一次校验singleton是否为空 if (singleton == null) { synchronized (RuleFactory.class) { // 第二次校验singleton是否为空 if (singleton == null) { singleton = new RuleFactory(rulesDao); } } } return singleton; } public List getRuleList() throws Exception { //调用普通的JAVA实现做对比 List rules = new ArrayList(); NormalJavaRule normalJavaRule = new NormalJavaRule(); rules.add(normalJavaRule); //直接读取Groovy文件生成IRule实现 IRule groovyFile = GroovyFactory.getInstance() .getIRuleFromPackage("cn/pomit/skill/groovy/rule/file/GroovyFileRule.groovy"); rules.add(groovyFile); //从Db的Groovy脚本中生成IRule实现 Rules ruleGroovy = rulesDao.getByName("GroovyDbRule"); if (ruleGroovy != null) { IRule groovyDb = GroovyFactory.getInstance().getIRuleFromCode(ruleGroovy.getRule()); rules.add(groovyDb); } return rules; } }6.3 数据库访问相关实体

Rules:

代码语言:javascript复制package cn.pomit.skill.groovy.rule.db; import java.util.Date; public class Rules { private Integer id; private String rule; private String name; private Date create_time; private Date update_time; private Integer visible; public String getRule() { return rule; } public void setRule(String rule) { this.rule = rule; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getVisible() { return visible; } public void setVisible(Integer visible) { this.visible = visible; } public Date getUpdate_time() { return update_time; } public void setUpdate_time(Date update_time) { this.update_time = update_time; } public Date getCreate_time() { return create_time; } public void setCreate_time(Date create_time) { this.create_time = create_time; } }

RulesDao:

代码语言:javascript复制package cn.pomit.skill.groovy.rule.db; import java.util.List; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; @Mapper public interface RulesDao { @Select("SELECT * FROM pomit_rule where id = #{id}") Rules getById(@Param("id") Integer id); @Select("SELECT * FROM pomit_rule where name = #{name}") Rules getByName(@Param("name") String name); @Insert("INSERT INTO pomit_rule(name,rule) VALUE(#{name},#{rule})") Integer setRule(@Param("name") String name,@Param("rule") String rule); @Select("SELECT * FROM pomit_rule order by create_time DESC") List getRuleList(); @Update("UPDATE pomit_rule SET visible=0 WHERE id = #{id}") Integer deleteRule(@Param("id") Integer id); @Update("UPDATE pomit_rule SET rule= #{rule} WHERE id = #{id}") Integer updateRule(@Param("id") Integer id,@Param("rule") String rule); }6.4 SpringBoot相关实体

ApiApplication:

代码语言:javascript复制package cn.pomit.skill.groovy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ImportResource; @SpringBootApplication @ImportResource(locations={"classpath:spring-groovy.xml"}) public class ApiApplication { public static void main(String[] args) { SpringApplication.run(ApiApplication.class, args); } }6.5 测试web

WebRest:

代码语言:javascript复制package cn.pomit.skill.groovy.web; import java.util.List; import javax.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.pomit.skill.groovy.rule.IRule; import cn.pomit.skill.groovy.rule.RuleFactory; import cn.pomit.skill.groovy.rule.db.RulesDao; @RestController @RequestMapping("/") public class WebRest { @Autowired RulesDao rulesDao; @Resource(name = "springGroovyRule") IRule springGroovyRule; @RequestMapping(value = "/test") public String test() throws Exception { List list = RuleFactory.getInstance(rulesDao).getRuleList(); list.add(springGroovyRule); for (IRule item : list) { item.printInfo(); } return "0000"; } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3