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

news2024/11/25 6:47:38

文章目录

  • 一、开场白
    • 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文件数据写入新jar
            Enumeration<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.coloradmin.cn/o/1056822.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于web的画作展示系统/作品展示平台

摘 要 网络的广泛应用给生活带来了十分的便利。所以把画作展示系统与现在网络相结合&#xff0c;利用JSP技术建设画作展示系统&#xff0c;实现画作展示系统的信息化。则对于进一步提高画作展示系统的发展&#xff0c;丰富画作展示系统经验能起到不少的促进作用。 画作展示系统…

selenium下载安装 -- 使用谷歌驱动碰到的问题

安装教程参考: http://c.biancheng.net/python_spider/selenium.html 1. 谷歌浏览器和谷歌驱动版本要对应(但是最新版本谷歌对应的驱动是没有的,因此要下载谷歌历史其他版本): 谷歌浏览器历史版本下载: https://www.chromedownloads.net/chrome64win/谷歌浏览器驱动下载: http:…

java并发编程 守护线程 用户线程 main

经常使用线程&#xff0c;没有对守护线程和用户线程的区别做彻底了解 下面写4个例子来验证一下 源码如下 /* Whether or not the thread is a daemon thread. */ private boolean daemon false;/*** Marks this thread as either a {linkplain #isDaemon daemon} thread*…

C进阶--字符函数和字符串函数介绍

✨ 更多细节参考 cplusplus.com/reference/cstring/ 使用方式&#xff1a; ⭕ 求字符串长度 &#x1f58c; strlen 函数原型&#xff1a; size_t strlen ( const char * str ); 作用&#xff1a; 获取字符串长度 ✨补充&#xff1a; ⭐字符串以 \0 作为结束标志&…

在移动固态硬盘上安装Ubuntu系统和ROS2

目录 原视频准备烧录 原视频 b站鱼香ros 准备 1.在某宝上买一个usb移动固态硬盘或固态U盘&#xff0c;至少64G 2.下载鱼香ros烧录工具 下载第二个就行了&#xff0c;不然某网盘的速度下载全部要一天 下载后&#xff0c;选择FishROS2OS制作工具压缩包&#xff0c;进行解压…

【Redis】五大数据类型 、历史概述、nosql分类

文章目录 NoSql概述NoSql年代缓存 Memcached MySQL垂直拆分&#xff08;读写分离&#xff09;分库分表水平拆分Mysql集群最近为什么要用 NoSqlNoSql的四大分类 Redis测试性能 五大数据类型keyStringSetHashZset 前言&#xff1a;本文为看狂神视频记录的笔记 NoSql概述 NoSql年…

【Django】4 Django模型

每个模型是一个Python 类&#xff0c;集成django.db.models.Modle类 该模型的每个属性表示一个数据库表字段 通过API 自动生成数据库访问 .../sign/modles.py 文件&#xff0c;通过模型完成表创建。 TypeError: ForeignKey.__init__() missing 1 required positional argumen…

ChatGPT多模态升级,支持图片和语音,体验如何?

一、前言 9 月 25 日&#xff0c;ChatGPT 多模态增加了新的语音功能和图像功能。这些功能提供了一种新的、更直观的界面&#xff0c;允许我们与 ChatGPT 进行语音对话或展示我们正在谈论的内容。 ChatGPT 现在可以看、听、和说话了&#xff0c;而不单单是一个文本驱动的工具了。…

算法通过村第十一关-位运算|白银笔记|高频题目

文章目录 前言1. 位移的妙用1.1 位1的个数1.2 比特位计算1.3 颠倒无符号整数 2. 位实现加减乘除专题2.1 位运算实现加法2.2 递归乘法 总结 前言 提示&#xff1a;他不是不想多明白些&#xff0c;但是每每在该用脑子的时候&#xff0c;他用了感情。 --老舍《黑白李》 与位运算和…

Centos7安装php-fpm

目录 第一步&#xff1a;查看系统IP地址和网卡名称 第二步&#xff1a;更改网络配置模式 第三步、重启network 查看iptablies ,将第十行&#xff0c;十一行删除 第四步&#xff1a;关闭config 第五步&#xff1a;创建nginx 文件夹 查看目录下的文件 进入nginx文件夹 第…

基于java的鲜花销售系统/网上花店

摘 要 本毕业设计的内容是设计并且实现一个基于Spring Boot框架的驿城鲜花销售系统。它是在Windows下&#xff0c;以MYSQL为数据库开发平台&#xff0c;Tomcat网络信息服务作为应用服务器。驿城鲜花销售系统的功能已基本实现&#xff0c;主要包括首页、个人中心、用户管理、鲜…

【VIM】初步认识VIM-2

2-6 Vim 如何搜索替换_哔哩哔哩_bilibili 1-6行将self改成this 精确替换quack单词为交

CSS基础语法第二天

目录 一、复合选择器 1.1 后代选择器 1.2 子代选择器 1.3 并集选择器 1.4 交集选择器 1.4.1超链接伪类 二、CSS特性 2.1 继承性 2.2 层叠性 2.3 优先级 基础选择器 复合选择器-叠加 三、Emmet 写法 3.1HTML标签 3.2CSS 四、背景属性 4.1 背景图 4.2 平铺方式 …

NPDP产品经理知识(市场调研-文化,团队,领导力)

--- VOC --- 市场调研的关键步骤 1.> 定义问题 2.>定义结果的准确度 3.>收集数据 4.>分析和解读数据 5.>得出结论 6.>实施 --- 二级市场研究/一级市场研究 --- 定性 > 焦点小组 > 深度访谈 > 人种学(On-Site In-Home) > 客户…

基于web的医院预约挂号系统/医院管理系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

【ElasticSearch 集群】Linux安装ElasticSearch集群(图文解说详细版)

上次我们讲了linux环境安装ElasticSearch Linux安装ElasticSearch以及Ik分词器&#xff08;图文解说详细版&#xff09; 这次我们来将一下ElasticSearch的集群安装 安装es的前置条件&#xff1a; Linux安装Java环境&#xff08;OracleJDK&#xff09; 这次我们安装的是Elasti…

GraphQL全面深度讲解

目录 一、GraphQL 是什么 二、GraphQL 规范 数据模型 字段 参数 三、运行示例 四、优势和劣势 优势 劣势 一、GraphQL 是什么 GraphQL 是一种用于 API 的查询语言&#xff0c;也是一个基于服务端的运行引擎。 GraphQL 提供了一套完整的规范和描述用于查询 API&#xf…

Django基础入门操作 (Django-01)

一 背景介绍 Django是一个开源的 Web应用框架&#xff0c;由Python写成。采用了MTV的框架模式&#xff0c;它最初是被用来做CMS&#xff08;内容管理系统&#xff09;软件。 官方中文文档&#xff1a;Django 文档 | Django 文档 | Django 应用&#xff1a;做内容管理系统(新…

JUC第十三讲:JUC锁: ReentrantLock详解

JUC第十三讲&#xff1a;JUC锁: ReentrantLock详解 本文是JUC第十三讲&#xff0c;JUC锁&#xff1a;ReentrantLock详解。可重入锁 ReentrantLock 的底层是通过 AbstractQueuedSynchronizer 实现&#xff0c;所以先要学习上一章节 AbstractQueuedSynchronizer 详解。 文章目录 …

数据结构与算法基础(青岛大学-王卓)(8)

哎呀呀&#xff0c;sorry艾瑞波地&#xff0c;这次真的断更一个月了&#xff0c;又发生了很多很多事情&#xff0c;秋风开始瑟瑟了&#xff0c;老父亲身体查出肿瘤了&#xff0c;有病请及时就医&#xff0c;愿每一个人都有一个健康的身体&#xff0c;God bless U and FAMILY. 直…