愚蠢的代码?某程序员强行编写了一段开发阶段无法调试的代码,大佬们快来救救他!

本文主要是介绍愚蠢的代码?某程序员强行编写了一段开发阶段无法调试的代码,大佬们快来救救他!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、开场白
    • 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

不幸的是以上几种方案,均违背了以下前提:

  1. 代码修改变动太大,对原始代码侵入太强
  2. 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
我们需要完成如下优化:

  1. 执行 java -jar jetty-runner-extra.jar 搜索jar内存在的war运行,否则给出提示 war不存在,程序终止。
  2. 执行 java -jar jetty-runner-extra.jar --addwar 交互运行,列出当前目录(包含子目录)war供选择添加,添加后生成新的jar,jar执行逻辑同1。
  3. 执行 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突然发现这个代码在开发阶段竟然是无法调试的。

各位大佬,快来帮帮他,提供思路来解决他的难处!!
把这段代码变得聪明起来!!解决问题有帮助的网友,版主会点名感谢!!
俗话说得好: 军功章上有你的一半也有我的一半

这篇关于愚蠢的代码?某程序员强行编写了一段开发阶段无法调试的代码,大佬们快来救救他!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/134

相关文章

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

python多进程实现数据共享的示例代码

《python多进程实现数据共享的示例代码》本文介绍了Python中多进程实现数据共享的方法,包括使用multiprocessing模块和manager模块这两种方法,具有一定的参考价值,感兴趣的可以... 目录背景进程、进程创建进程间通信 进程间共享数据共享list实践背景 安卓ui自动化框架,使用的是

SpringBoot生成和操作PDF的代码详解

《SpringBoot生成和操作PDF的代码详解》本文主要介绍了在SpringBoot项目下,通过代码和操作步骤,详细的介绍了如何操作PDF,希望可以帮助到准备通过JAVA操作PDF的你,项目框架用的... 目录本文简介PDF文件简介代码实现PDF操作基于PDF模板生成,并下载完全基于代码生成,并保存合并P

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st