一招教你如何减少本地调试tomcat重启次数
当我们进行本地调试的时候,代码做了少量改动,却要重启tomcat。如果项目比较小还行,如果项目比较大这个时候重启tomcat的时间就比较长。下面我说的方法将会让你减少tomcat不必要的重启次数。
这次引入的技术为groovy。
在groovy中书写的代码无需重启tomcat,修改之后需需要重新从入口进入就行了
什么是gooovy
apache groovy是一种功能强大、可选的类型和动态语言,具有静态键入和静态编译功能,适用于java平台,旨在通过简洁、熟悉和易于学习的语法提高开发人员的工作效率。它与任何java程序顺利集成,并立即为您的应用程序提供强大的功能,包括脚本功能、特定域语言创作、运行时和编译时元编程以及功能编程。和java兼容性强,可以无缝衔接java代码,可以调用java所有的库。
多得不说,直接上代码
pom依赖
<dependency>
<groupid>org.codehaus.groovy</groupid>
<artifactid>groovy-jsr223</artifactid>
<version>3.0.6</version>
</dependency>
controller
@controller
@slf4j
public class scriptaction {
@autowired
private groovyeval groovyeval;
@requestmapping(value = "/script/test")
//入参:groovy脚本存放绝对路径、需要传递的参数
public object scripttest(
@param(value = "path", required = true) string path,
@json("@requestbody") @requestbody map<string,object> parammap
) {
try {
bufferedreader bufferedreader = new bufferedreader(new inputstreamreader(new fileinputstream(path), standardcharsets.utf_8));
string date;
stringbuilder stringbuilder = new stringbuilder();
while((date = bufferedreader.readline()) != null){
stringbuilder.append(date).append("\n");
}
bufferedreader.close();
//执行脚本获得结果,约定执行的脚本方法名字为solution
return groovyeval.evalscript(bufferedreader.tostring() , "solution" , new object[]{parammap});
} catch (filenotfoundexception e) {
e.printstacktrace();
} catch (ioexception e) {
e.printstacktrace();
}
return null;
}
}
service
import com.google.gson.gson;
import groovy.lang.groovyclassloader;
import groovy.lang.groovyobject;
import lombok.extern.slf4j.slf4j;
import org.springframework.beans.beansexception;
import org.springframework.context.applicationcontext;
import org.springframework.context.applicationcontextaware;
import org.springframework.stereotype.component;
import org.springframework.util.collectionutils;
import java.util.map;
import java.util.concurrent.timeunit;
@slf4j
@component
public class groovyeval implements applicationcontextaware {
private static groovyeval groovyeval;
private applicationcontext applicationcontext;
public static <t> t getbean(class<t> cls){
return groovyeval.applicationcontext.getbean(cls);
}
public object evalscript(string script, string methodname, object[] args){
object scriptobj = this.getscript(script);
try {
//脚本执行入口
//返回的数据类型在groovy脚本中自己定义即可,我这里返回的是map
map<string, object> resultmap = (map<string, object>)((groovyobject)scriptobj).invokemethod(methodname, args);
if (collectionutils.isempty(resultmap)){
return null;
}
return resultmap.get("data");
} catch (throwable e) {
log.error("script eval error !" , e);
}
return null;
}
private object getscript(string script){
//注意!!!本地调试可以不需要加入缓存机制,生产环境需要加入缓存
//加载脚本,每执行一次new一个groovycodesource
class<?> cls = new groovyclassloader().parseclass(script);
groovyobject groovyobject = null;
try {
log.info("load script!");
groovyobject = (groovyobject)cls.newinstance();
} catch (illegalaccessexception | instantiationexception e) {
log.error("load script error ! script : {}" , script , e);
}
return groovyobject;
}
@override
public void setapplicationcontext(applicationcontext applicationcontext) throws beansexception {
//静态化bean
this.applicationcontext = applicationcontext;
groovyeval = this;
}
}
groovy脚本
testgroovy.groovy
class testgroovy {
def map<string,object> solution(map<string,object> parammap){
map<string,object> resultmap = [:];
/** 获取上层传入的参数 */
object shopcodes = parammap.get("param");
//业务逻辑处理。。。。。。
resultmap.put("data", "resultdata");
return resultmap;
}
}
生产环境使用
因为groovy每执行一次脚本,都会生成一个脚本的class对象,这个class对象的名字由 “script” + system.currenttimemillis() +
math.abs(text.hashcode())组成,因此应用到生产环境需要加入缓存。推荐使用高性能缓存:caffeine,
官方介绍caffeine是基于jdk8的高性能本地缓存库,提供了几乎完美的命中率。它有点类似jdk中的concurrentmap,实际上,caffeine中的localcache接口就是实现了jdk中的concurrentmap接口,但两者并不完全一样。最根本的区别就是,concurrentmap保存所有添加的元素,除非显示删除之(比如调用remove方法)。而本地缓存一般会配置自动剔除策略,为了保护应用程序,限制内存占用情况,防止内存溢出。
有兴趣的可以自己去搜索一下,我感觉蛮好用的
@component
public class groovyeval implements applicationcontextaware {
private static final logger logger = loggerfactory.getlogger(groovyeval.class);
private static final object source = new object();
private static groovyeval groovyeval;
private applicationcontext applicationcontext;
@autowired
private alarmthresholdsettingsitemservice alarmthresholdsettingsitemservice;
public static <t> t getbean(class<t> cls){
return groovyeval.applicationcontext.getbean(cls);
}
private static final cache<object, object> caffeine = caffeine
.newbuilder()
.maximumsize(30000)
//三天不用直接 gc
.expireafteraccess(72 , timeunit.hours)
.build();
public map lookup(){
return caffeine.asmap();
}
public object evalscript(string script,string methodname,object[] args) {
object scriptobj = this.getscript(script);
if(scriptobj != null){
try{
//统一返回 map<string,object> { "data" : object }
map<string, object> resultmap = (map<string, object>) ((groovyobject) scriptobj).invokemethod(methodname, args);
if(collectionutils.isempty(resultmap)){
return null;
}
return resultmap.get("data");
}catch (throwable e){
logger.error("script eval error !" , e);
}
}
return null;
}
//脚本加入缓存
private object getscript(string script){
//唯一标记
string cachekey = digestutils.md5hex(script);
return caffeine.get(cachekey, new function<object, object>() {
@override
public object apply(object key) {
//避免变动导致并发问题
synchronized (source){
class<?> cls = new groovyclassloader().parseclass(script);
groovyobject gobj = null;
try {
logger.info("load script !");
gobj = (groovyobject) cls.newinstance();
} catch (instantiationexception | illegalaccessexception e) {
logger.error("load script error ! script : {}" , script , e);
}
return gobj;
}
}
});
}
@override
public void setapplicationcontext(applicationcontext applicationcontext) throws beansexception {
//静态化 bean
this.applicationcontext = applicationcontext;
groovyeval = this;
}
}
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注www.887551.com的更多内容!