接[IDEA maven多模块 使用jetty热部署]上文,尝试了IDEA自带的jetty插件(project structure的artifacts里的配置不定期丢失),maven的jetty插件(不能热部署,会导致整体重启),全都不能做到完美的多模块热部署,而eclipse的jetty插件这么好用,那就把移植过来吧,以下为具体解决方案…
序
尝试了IDEA自带的jetty插件(project structure的artifacts里的配置不定期丢失),maven的jetty插件(不能热部署,会导致整体重启),全都不能做到完美的多模块热部署,而eclipse的jetty插件这么好用,那就把移植过来吧,以下为具体解决方案…
总体思路
- 找到eclipse jetty插件的入口函数
- 确定如何传参
- 获取或配置各类参数
- 启动
找入口函数
首先,在github上搜了一圈,找到了eclipse jetty插件的repo eclipse-jetty-plugin,项目主页翻了一遍,没发现什么有效信息 那就只好看源码了,git clone下拉了源码,下一步就是找到入口函数在哪。
先想到的思路,是从启动打印的那个logo入手,很快就搜到在类 net.sourceforge.eclipsejetty.starter.jetty9.Jetty9LauncherMain下的printLogo()函数内,被继承的抽象类内的launch()函数调用,lanuch被main调用,正好往上两行就是main函数了
/**
* Calls the {@link #launch(String[])} method
*
* @param args the arguments
* @throws Exception on occasion
*/
public static void main(String[] args) throws Exception
{
new Jetty9LauncherMain().launch(args);
}
/**
* {@inheritDoc}
*
* @see net.sourceforge.eclipsejetty.starter.common.AbstractJettyLauncherMain#printLogo(java.io.PrintStream)
*/
@Override
protected void printLogo(PrintStream out)
{
out.println(" ____ ___ __ __ __ ___");
out.println(" / __/___/ (_)__ ___ ___ __ / /__ / /_/ /___ __ / _ \\");
out.println(" / _// __/ / / _ \\(_-</ -_) / // / -_) __/ __/ // / \\_, /");
out.println("/___/\\__/_/_/ .__/___/\\__/ \\___/\\__/\\__/\\__/\\_, / /___/");
out.println(" /_/ /___/");
}
传参分析
接下来看传参了,可以看到lanuch主要干的事就是从”jetty.launcher.configuration”配置中取到一个配置文件路径,加载文件内容
protected void launch(String[] args) throws Exception {
long millis = System.currentTimeMillis();
boolean showInfo = System.getProperty("jetty.launcher.hideLaunchInfo") == null;
boolean consoleEnabled = System.getProperty("jetty.launcher.disableConsole") == null;
if (showInfo) {
this.printLogo(System.out);
}
String configurationFileDef = System.getProperty("jetty.launcher.configuration");
if (configurationFileDef == null) {
throw new IOException(String.format("-D%s missing", "jetty.launcher.configuration"));
} else {
File[] configurationFiles = getConfigurationFiles(configurationFileDef);
ServerAdapter adapter = this.createAdapter(configurationFiles, showInfo);
this.configure(System.out, adapter, configurationFiles, showInfo);
if (showInfo) {
adapter.info(System.out);
}
this.initConsole(consoleEnabled, adapter);
adapter.start();
if (showInfo) {
this.printStartupTime(System.out, millis, consoleEnabled);
}
}
}
配置文件分析
这个配置就在eclipse项目的最外面,找了一个,抽出一个模板,如下代码,中间需要替换的部分已经替换成了freemaker的变量(渲染直接用字符串替换也可以),主要的几个参数如下: * port 监听端口 * webpath web主目录 * webcontext context路径 * defaultsDescriptor 默认web.xml路径(可不配) * extraClasspath 其他需要加载的classpath路径(重点!jar包,多模块的编译结果路径全配在这)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Configure class="org.eclipse.jetty.server.Server" id="Server">
<!-- Thread Pool -->
<Get name="ThreadPool">
<Set name="minThreads">1</Set>
<Set name="idleTimeout">60000</Set>
<Set name="detailedDump">false</Set>
</Get>
<!-- HTTP Config -->
<New class="org.eclipse.jetty.server.HttpConfiguration" id="httpConfig">
<Set name="secureScheme">https</Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Set name="sendServerVersion">true</Set>
<Set name="sendDateHeader">false</Set>
<Set name="headerCacheSize">512</Set>
</New>
<!-- HTTP Connector -->
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server">
<Ref refid="Server"/>
</Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config">
<Ref refid="httpConfig"/>
</Arg>
</New>
</Item>
</Array>
</Arg>
<Set name="port">${port}</Set>
<Set name="idleTimeout">30000</Set>
</New>
</Arg>
</Call>
<!-- Handler -->
<Set name="handler">
<New class="org.eclipse.jetty.webapp.WebAppContext">
<Arg type="java.lang.String">${webpath}</Arg>
<Arg type="java.lang.String">${webcontext}</Arg>
<Set name="defaultsDescriptor">${defaultsDescriptor}</Set>
<Set name="extraClasspath">${extraClasspath}</Set>
</New>
</Set>
<!-- Annotations -->
<Call class="org.eclipse.jetty.webapp.Configuration$ClassList" name="setServerDefault">
<Arg>
<Ref refid="Server"/>
</Arg>
<Call name="addBefore">
<Arg name="addBefore" type="java.lang.String">org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Arg>
<Arg>
<Array type="java.lang.String">
<Item>
org.eclipse.jetty.annotations.AnnotationConfiguration
</Item>
</Array>
</Arg>
</Call>
</Call>
<!-- JNDI -->
<Call class="org.eclipse.jetty.webapp.Configuration$ClassList" name="setServerDefault">
<Arg>
<Ref refid="Server"/>
</Arg>
<Call name="addAfter">
<Arg name="afterClass" type="java.lang.String">org.eclipse.jetty.webapp.FragmentConfiguration</Arg>
<Arg>
<Array type="java.lang.String">
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
</Array>
</Arg>
</Call>
</Call>
<!-- Extra Options -->
<Set name="stopAtShutdown">true</Set>
<Set name="stopTimeout">1000</Set>
<Set name="dumpAfterStart">false</Set>
<Set name="dumpBeforeStop">false</Set>
</Configure>
生成配置文件
接下来就是怎么生成这个文件了,目前有两种方式: 第一种,在eclipse里,把项目导入,配好插件,生成xml;在IDEA里新建一个项目,jetty.launcher.configuration设置好eclipse生成的配置文件路径即可。
这种方式需要eclipse和idea都打开,配置不好的电脑容易卡,不过eclipse打开的唯一目的就是为了生成这个文件,可以把初始化的内存配置小点。好处就是方便,无需其他任何操作。
另一种,就是写代码生成了,这其中最麻烦的是获取运行需要的各种classpath;classpath分两种,分别是 * 各模块的 target/classes 路径 * 第三方jar包的实际路径 各模块的路径还是比较好获取,遍历一遍项目各模块目录,拼接即可。 但直接遍历无法处理的是,开发过程中,可能部分模块暂时不需要,也就是没法完全按pom.xml的配置精确指定要加载的模块,不过大多数时候,全部加载进路径能应付大多数情况。
第三方jar包的路径就比较不好获取了 第一种方案,要精确获取各类jar包,那必然要解析pom.xml,想到的一个思路是调用maven的接口去获取,而目前暂时没有多余时间去研究 第二种方案,借助IDE的配置文件,在IDEA的配置文件夹.idea里挨个看下即可发现jar包的实际路径在以下目录内.idea\libraries\xxx.xml,代码中要做的就是解析这些xml整合各个模块的依赖,最终生成classpath 第三种方案,那就是直接打出war包,把lib目录下的jar包全部拷出,去除项目源码生成的jar包,就是第三方的所有jar包,遍历解压后的目录,加入classpath即可,操作起来最简单,但问题也最大,那就是更新完第三方包后,必须重新进行以上操作,如果忘记了,很容易出现各种诡异bug。
由于第三方包变动不是很大,目前暂时采用第三种方案
完成
配置文件搞定后,剩下的就是在业务项目下建个模块,整合上面写好的代码,生成配置文件,调用eclipse插件,传入配置文件路径,点击debug运行项目,热部署正常,终于可以愉快的调试代码了。