本文主要是介绍愚蠢的代码?某程序员强行编写了一段开发阶段无法调试的代码,大佬们快来救救他!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 一、开场白
- 1. 程序员打赌的故事
- 2. 目标: 尽量在不修改代码的情况下将springmvc框架以独立jar方式运行
- 二、出师不利
- 方案一、Spring Web工程转Spring Boot
- 方案二、引入内置服务器jetty
- 其余备用方案
- 三、柳暗花明
- 遇见jetty-runner
- 测试验证
- 准备工作:
- 部署工作
- 四、再接再厉
- 一、新建maven工程pom.xml
- 二、编写核心逻辑
- 三、测试
- 五、遗留难题
- 难难难!!! 小C突然发现这个代码在开发阶段竟然是无法调试的。
一、开场白
1. 程序员打赌的故事
小A、小B、小C都在一家初创公司工作,小A是系统运维,小B和小C都是后台开发,他们都是能力卓越的IT工程狮。
某日加班后,三人聚在一起闲聊。小A向其余2人抱怨说,咱们公司开发小组维护的那个历史比较悠久的B项目,在测试服务上部署太麻烦了,每次更新都需要先停Tomcat服务器,再删掉旧的war和目录,然后上传war包,最后再重启服务。你们看看能不能改造下,最好能象springboot工程一样,上传一个jar,一个命令启停就搞定了,能这样就谢天谢地了!
小B说,据我所知,那个B项目是springmvc框架开发的,历史悠久,想把他转换为springboot框架Jar运行,基本上不太可能,除非大动。
小C说,我看有一定可行性,只是需要时间做技术预研。
于是,3人约定将这件事情作为一个技术攻坚工作,谁能够先成功解决,另外2人请吃饭。而且可以上报本季度的效率提升之星(获得免费2天的调休时间以及物质奖励)
2. 目标: 尽量在不修改代码的情况下将springmvc框架以独立jar方式运行
二、出师不利
经过一段时间的技术预研,小B、小C他们提出了以下2个主要方案和备用方案
方案一、Spring Web工程转Spring Boot
步骤1:删除web.xml
步骤2:pom.xml导入springboot
步骤3:添加springboot 启动代码,保留springmvc工程xml配置文件,用ImportResource注解引入
参考案例: springmvc-dbutils-redis、 springmvc-dbutils-to-boot
方案二、引入内置服务器jetty
步骤1:pom.xml导入jetty相关组件,一般包含jetty-webapp、jetty-jsp、jetty-server等。
步骤2:编写启动类,设置jetty启动的各项参数。
参考案例:keta-custom
其余备用方案
自己实现一套web逻辑,可以参考的项目主要有:h2-database、Jenkins
不幸的是以上几种方案,均违背了以下前提:
- 代码修改变动太大,对原始代码侵入太强
- java web程序一般都是war,改造成boot工程未必就是Jar(其实war如果能独立运行的也是可以接受的)
事情似乎陷入了死胡同。。。
三、柳暗花明
遇见jetty-runner
大家继续寻找方案,这天小C,在查找资料时,忽然在jetty官网看到这样一句话:
Jetty Runner ,This chapter explains how to use the jetty-runner to run your webapps without needing an installation of Jetty.
Deploying a Simple Context Let’s assume we have a very simple webapp that does not need any resources from its environment, nor any configuration apart from the defaults. Starting it is as simple as performing the following:
java -jar jetty-runner.jar simple.war
官网文档:https://eclipse.dev/jetty/documentation/jetty-9/index.html#runner
测试验证
准备工作:
从maven中央仓库下载jetty-runner jar
因为本地开发环境是jdk8,所以下载了支持jdk版本为1.8的9.3或者9.4系列版本。
部署工作
小C急忙将之前开发的一个springmvc工程打成的dbtool_simple.war找出来,与jetty-runner.jar放在同一级目录
并敲下这个命令:java -jar jetty-runner-9.4.52.v20230823.jar dbtool_simple.war
后台打印出一段日志后,dbtool_simple.war竟然成功启动了
小C激动的都快喊出来了。
四、再接再厉
小C冷静下来之后,看着jar、war陷入了沉思,现在jar、war其实是分离的,其实可以将war打包到jar里面运行,经过一段思考之后,小C提出了如下优化方案:
暂时将此项目命名为jetty-runner-extra
,这样项目打出的jar为jetty-runner-extra.jar
我们需要完成如下优化:
- 执行
java -jar jetty-runner-extra.jar
搜索jar内存在的war运行,否则给出提示war不存在
,程序终止。 - 执行
java -jar jetty-runner-extra.jar --addwar
交互运行,列出当前目录(包含子目录)war供选择添加,添加后生成新的jar,jar执行逻辑同1。 - 执行
java -jar jetty-runner-extra.jar --addwar war/simple.war
如指定的war存在,直接添加,添加后生成新的jar,jar执行逻辑同1。否则给出提示war不存在
,程序终止。
说干就干!
一、新建maven工程pom.xml
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.fly</groupId><artifactId>jetty-runner-extra</artifactId><version>1.0.0</version><name>jetty-runner-extra</name><url>http://maven.apache.org</url><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><log4j.version>2.12.1</log4j.version><java.version>1.8</java.version><skipTests>true</skipTests></properties><dependencies><dependency><groupId>org.eclipse.jetty</groupId><artifactId>jetty-runner</artifactId><version>9.4.52.v20230823</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.5</version></dependency><!-- slf4j + log4j2 begin --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>${log4j.version}</version></dependency><!-- log4j end --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.4.0</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><minimizeJar>false</minimizeJar><filters><filter><artifact>*:*</artifact></filter></filters><transformers><!-- 往MANIFEST文件中写入Main-Class是可执行包的必要条件。ManifestResourceTransformer可以轻松实现。 --><transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>com.fly.JettyExtraRunner</mainClass></transformer><!-- AppendingTransformer 用来处理多个jar包中存在重名的配置文件的合并,尤其是spring --><transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"><resource>META-INF/services/javax.servlet.ServletContainerInitializer</resource></transformer></transformers></configuration></execution></executions></plugin></plugins></build>
</project>
这里我们引入了jetty-runner、commons-io、log4j2、lombok还有maven-shade-plugin插件,插件主要实现可执行jar的配置。
二、编写核心逻辑
JettyExtraRunner.java
package com.fly;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Scanner;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.eclipse.jetty.runner.Runner;import lombok.extern.slf4j.Slf4j;@Slf4j
@SuppressWarnings("deprecation")
public class JettyExtraRunner
{private static URL url = JettyExtraRunner.class.getProtectionDomain().getCodeSource().getLocation();/*** 遍历文件或Jar寻找war运行* * @param args* @throws IOException*/public static void main(String[] args)throws IOException{log.info("类加载路径: {}", url.getPath());boolean isJar = url.getPath().endsWith(".jar");if (isJar){processInJar(args);}}/*** Jar遍历-寻找war-拷贝-运行* * @param args* @throws IOException*/private static void processInJar(String[] args)throws IOException{// 调用方式1: java -jar jetty-runner-extra.jar 搜索jar内存在的war运行,否则给出提示 war不存在,程序终止if (args.length == 0){try (JarFile jarFile = new JarFile(url.getFile())){int num = 0;Enumeration<JarEntry> entrys = jarFile.entries();while (entrys.hasMoreElements()){JarEntry jar = entrys.nextElement();String name = jar.getName();if (name.endsWith(".war")){num++;log.info("即将加载运行:{}", name);try (InputStream is = JettyExtraRunner.class.getResourceAsStream("/" + name)){File file = new File(name);FileUtils.copyInputStreamToFile(is, file);Runner.main(new String[] {file.getCanonicalPath()});return;}}}if (num == 0){log.error("未发现war文件,程序终止");}}return;}// 调用方式2: java -jar jetty-runner-extra.jar --addwar 交互运行,列出当前目录(包含子目录)war供选择添加if (args.length == 1 && "--addwar".equals(args[0])){Collection<File> files = FileUtils.listFiles(new File(url.getPath()).getParentFile(), new String[] {"war"}, true);if (files.isEmpty()){log.error("未发现war文件,无法添加");return;}File selected;if (files.size() == 1){selected = files.toArray(new File[0])[0];}else{// 列出->选择try (Scanner sc = new Scanner(System.in)){int input;do{int index = 1;for (File file : files){log.info("序号{}: {}", index++, file.getCanonicalPath());}log.info("请输入序号1-{}选择war文件", files.size());input = sc.nextInt();} while (input < 1 || input > files.size());selected = files.toArray(new File[0])[input - 1];log.info("你选择了war文件:{} ", selected.getCanonicalPath());}}addWar(selected);return;}// 调用方式3: java -jar jetty-runner-extra.jar --addwar war/simple.war 如指定的war存在,直接添加,否则给出提示 war不存在,程序终止if (args.length == 2 && "--addwar".equals(args[0])){String path = args[1];File war = new File(path);if (war.exists()){log.info("文件:{}", war.getCanonicalPath());addWar(war);}else{log.error("{} 不存在,程序终止", path);}return;}}/*** 添加war到新jar中* * @param war* @see [类、类#方法、类#成员]*/private static void addWar(File war){try{File srcJar = new File(url.getPath());String newJar = srcJar.getCanonicalPath().replace(".jar", DateFormatUtils.format(System.currentTimeMillis(), "_HHmmssSSS") + ".jar");addWarToJar(war, srcJar, newJar);}catch (IOException e){log.error(e.getMessage(), e);}}/*** 将war添加到srcJar并重命名为newJar* * @param war* @param srcJar* @param newJar* @throws IOException*/private static void addWarToJar(File war, File srcJar, String newJar)throws IOException{log.info("即将添加war文件:{} 到Jar中...", war.getCanonicalPath());try (JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(newJar)); JarFile jarFile = new JarFile(srcJar)){// 遍历jar文件数据写入新jarEnumeration<JarEntry> entrys = jarFile.entries();while (entrys.hasMoreElements()){JarEntry jarEntry = entrys.nextElement();if (jarEntry != null){jarOutputStream.putNextEntry(jarEntry);try (InputStream entryInputStream = jarFile.getInputStream(jarEntry)){IOUtils.copy(entryInputStream, jarOutputStream);}}}// 追加war写入数据JarEntry warEntry = new JarEntry("war/" + war.getName());jarOutputStream.putNextEntry(warEntry);try (InputStream entryInputStream = new FileInputStream(war)){IOUtils.copy(entryInputStream, jarOutputStream);}}}
}
三、测试
我们在项目的根目录执行mvn clean package
便生成了 jetty-runner-extra.jar
目录结构如下:
我们来实际运行下:
五、遗留难题
难难难!!! 小C突然发现这个代码在开发阶段竟然是无法调试的。
各位大佬,快来帮帮他,提供思路来解决他的难处!!
把这段代码变得聪明起来!!解决问题有帮助的网友,版主会点名感谢!!
俗话说得好: 军功章上有你的一半也有我的一半
这篇关于愚蠢的代码?某程序员强行编写了一段开发阶段无法调试的代码,大佬们快来救救他!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!