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


    一、开场白

    1. 程序员打赌的故事

        小A、小B、小C都在一家初创公司工作,小A是系统运维,小B和小C都是后台开发,他们都是能力卓越的IT工程狮。
        某日加班后,三人聚在一起闲聊。小A向其余2人抱怨说,咱们公司开发小组维护的那个历史悠久的B项目,在测试服务上部署太麻烦了,每次更新都需要先停Tomcat服务器,再删掉旧的war和目录,然后上传war包,最后再重启服务。你们看看能不能改造下,最好能象springboot工程一样,上传一个jar,一个命令启停就搞定了,能这样就谢天谢地了!小B、小C你们俩老鸟给我出出点子,毙了这个问题!

        小B撇撇嘴说,据我所知,那个B项目是springmvc框架的,历史悠久,想把他转换为springboot框架Jar运行,基本上不太可能,除非大动。
        小C云淡风轻,不经意的飘出一句,我看有一定可行性,能整!只是需要大老板给点时间做下小小的技术预研。

        于是,3人约定将这件事情作为一个技术攻坚工作,谁能够先成功解决,另外2人请吃饭外加娱乐,具体什么项目到时候再定。而且可以上报本季度的效率提升之星,maybe 能获得2天免费的调休和物质(or 精神、荣誉)奖励。

    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-databaseJenkins

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

    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

    
    <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.0modelVersion>
    	<groupId>com.flygroupId>
    	<artifactId>jetty-runner-extraartifactId>
    	<version>1.0.0version>
    	<name>jetty-runner-extraname>
    	<url>http://maven.apache.orgurl>
    	<packaging>jarpackaging>
    	<properties>
    		<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    		<log4j.version>2.12.1log4j.version>
    		<java.version>1.8java.version>
    		<skipTests>trueskipTests>
    	properties>
    	<dependencies>
    		<dependency>
    			<groupId>org.eclipse.jettygroupId>
    			<artifactId>jetty-runnerartifactId>
    			<version>9.4.52.v20230823version>
    		dependency>
    		<dependency>
    			<groupId>commons-iogroupId>
    			<artifactId>commons-ioartifactId>
    			<version>2.5version>
    		dependency>
    		
    		<dependency>
    			<groupId>org.apache.logging.log4jgroupId>
    			<artifactId>log4j-slf4j-implartifactId>
    			<version>${log4j.version}version>
    		dependency>
    		
    		<dependency>
    			<groupId>org.projectlombokgroupId>
    			<artifactId>lombokartifactId>
    			<version>1.18.12version>
    			<scope>providedscope>
    		dependency>
    	dependencies>
    	<build>
    		<finalName>${project.artifactId}finalName>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.maven.pluginsgroupId>
    				<artifactId>maven-compiler-pluginartifactId>
    				<version>3.8.1version>
    				<configuration>
    					<source>1.8source>
    					<target>1.8target>
    				configuration>
    			plugin>
    			<plugin>
    				<groupId>org.apache.maven.pluginsgroupId>
    				<artifactId>maven-shade-pluginartifactId>
    				<version>3.4.0version>
    				<executions>
    					<execution>
    						<phase>packagephase>
    						<goals>
    							<goal>shadegoal>
    						goals>
    						<configuration>
    							<minimizeJar>falseminimizeJar>
    							<filters>
    								<filter>
    									<artifact>*:*artifact>
    								filter>
    							filters>
    							<transformers>
    								
    								<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
    									<mainClass>com.fly.JettyExtraRunnermainClass>
    								transformer>
    								
    								<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
    									<resource>META-INF/services/javax.servlet.ServletContainerInitializerresource>
    								transformer>
    							transformers>
    						configuration>
    					execution>
    				executions>
    			plugin>
    		plugins>
    	build>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    log4j2.xml

    
    <configuration status="off" monitorInterval="0">
    	<appenders>
    		<console name="Console" target="system_out">
    			<patternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
    		console>
    	appenders>
    	<loggers>
    		<root level="INFO">
    			<appender-ref ref="Console" />
    		root>
    	loggers>
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里我们引入了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("新文件: {}", newJar);
            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);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202

    三、测试

    我们在项目的根目录执行mvn clean package 便生成了 jetty-runner-extra.jar
    目录结构如下:
    在这里插入图片描述
    我们来实际运行下:
    在这里插入图片描述
    在这里插入图片描述

    五、遗留难题

    难难难!!! 小C突然发现这个代码在开发阶段竟然是无法调试的,啊啊啊,好害怕被关小黑屋。。。。。。

    各位大佬,快来帮帮他,提供思路来解决他的难题!!
    把这段代码变得聪明起来!!解决问题有帮助的网友,版主会点名感谢!!
    众人拾柴火焰高,一个好汉三个帮!!
    谁才是聪明代码的缔造者,大家拭目以待!

    六、远程调试

    代码在开发阶段虽然无法调试,但是打成Jar后开启远程调试还是可以debug的。

    假设jar名字为jetty-runner-extra.jar,在JDK5-8调试模式参数命令为:

    java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000 -jar jetty-runner-extra.jar --addwar
    
    • 1

    七,完整代码

    springmvc-by-jetty

  • 相关阅读:
    About Random Forest
    什么是P = NP?问题
    Markdown使用教程
    三维模型OBJ格式轻量化的数据压缩与性能平衡分析
    三季度毛利“转正”的零跑,四季度已经打响“第一枪”?
    webpack构建01-vue项目之 手动webpack打包
    vite创建的vue3项目使用jsx
    netty面试题及答案
    创建数据透视表:根据表中一列作为分类的依据统计每个类别下不同子项数量cross_tab()
    原装芯片现货,价格优惠,实单可谈
  • 原文地址:https://blog.csdn.net/qq_16127313/article/details/133500980