目录
- 简介
- 源文件介绍
- 编译
- 编译restful-common
- 编译manual-springboot
- 打包&部署&执行
- jar命令介绍
- 不打包直接运行
- 打普通jar包,通过java -jar运行
- 打fat jar通过java -jar
- 打war,通过部署至tomcat运行
- 纯手工命令开发打包部署的缺点
- 参考
简介
本文将使用jdk命令进行java文件编译、打包、部署启动
- 编译:使用javac进行编译
- 打包:使用jar分别打jar包、war包
- 部署执行:使用直接java -jar执行jar包、使用tomcat+war包
源文件介绍
以下是目录和源文件,一共有三个源文件。这个目录不是生成的,就是我的个人习惯自己创建的目录
|manual-springboot
| |____classes
| |____lib
| |____src
| | |____Application.java
|restful-common
| |____classes
| |____src
| | |____ResultFactory.java
| | |____Result.java
package com.jaan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.jaan.restful.common.*;
@SpringBootApplication
@Controller
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@PostMapping("/hello")
@ResponseBody
public Result hello(){
return ResultFactory.succ();
}
}
package com.jaan.restful.common;
public class Result{
private String code;
private String errorCode;
private String errorDesc;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorDesc() {
return errorDesc;
}
public void setErrorDesc(String errorDesc) {
this.errorDesc = errorDesc;
}
public Result(String code, String errorCode, String errorDesc) {
this.code = code;
this.errorCode = errorCode;
this.errorDesc = errorDesc;
}
}
package com.jaan.restful.common;
public class ResultFactory{
public static Result succ(){
return new Result("1",null,null);
}
}
编译
Application 中import了spring的好多类,我们怎么知道import哪些类呢,我这是直接从其他spring项目中复制的。当我们使用三方代码的时候,我们肯定能先拿到三方的jar包或者文档,阅读后才知道如何使用才会再去使用的,所以这种情况下肯定知道import什么。如果我们使用了别人的代码,但是没有import,这种情况下进行编译会出错的,如我把Application.java中的import全部删掉,进行编译javac -d classes src/*
,如下报错
jaan@zhangdeshuaideMacBook-Pro manual-springboot % javac -d classes src/*
src/Application.java:3: 错误: 找不到符号
@SpringBootApplication
^
符号: 类 SpringBootApplication
src/Application.java:4: 错误: 找不到符号
@Controller
^
符号: 类 Controller
src/Application.java:12: 错误: 找不到符号
public Result hello(){
^
符号: 类 Result
位置: 类 Application
src/Application.java:10: 错误: 找不到符号
@PostMapping("/hello")
^
符号: 类 PostMapping
位置: 类 Application
src/Application.java:11: 错误: 找不到符号
@ResponseBody
^
符号: 类 ResponseBody
位置: 类 Application
src/Application.java:7: 错误: 找不到符号
SpringApplication.run(Application.class, args);
^
符号: 变量 SpringApplication
位置: 类 Application
src/Application.java:13: 错误: 找不到符号
return ResultFactory.succ();
^
符号: 变量 ResultFactory
位置: 类 Application
7 个错误
原因是我们使用了别人的类但是没有进行import导致的,只有在使用java基础类库或者与源文件同包或是源文件子包下的类的时候才不用import。
我们撤销掉刚才的删除的import,即此时的代码就是上文源文件介绍中贴出的源码,再次编译javac -d classes src/*
jaan@zhangdeshuaideMacBook-Pro manual-springboot % javac -d classes src/*
src/Application.java:2: 错误: 程序包org.springframework.boot不存在
import org.springframework.boot.SpringApplication;
^
src/Application.java:3: 错误: 程序包org.springframework.boot.autoconfigure不存在
import org.springframework.boot.autoconfigure.SpringBootApplication;
^
src/Application.java:4: 错误: 程序包org.springframework.stereotype不存在
import org.springframework.stereotype.Controller;
^
src/Application.java:5: 错误: 程序包org.springframework.web.bind.annotation不存在
import org.springframework.web.bind.annotation.PostMapping;
^
src/Application.java:6: 错误: 程序包org.springframework.web.bind.annotation不存在
import org.springframework.web.bind.annotation.ResponseBody;
^
src/Application.java:7: 错误: 程序包com.jaan.restful.common不存在
import com.jaan.restful.common.*;
^
src/Application.java:8: 错误: 找不到符号
@SpringBootApplication
^
符号: 类 SpringBootApplication
src/Application.java:9: 错误: 找不到符号
@Controller
^
符号: 类 Controller
src/Application.java:17: 错误: 找不到符号
public Result hello(){
^
符号: 类 Result
位置: 类 Application
src/Application.java:15: 错误: 找不到符号
@PostMapping("/hello")
^
符号: 类 PostMapping
位置: 类 Application
src/Application.java:16: 错误: 找不到符号
@ResponseBody
^
符号: 类 ResponseBody
位置: 类 Application
src/Application.java:12: 错误: 找不到符号
SpringApplication.run(Application.class, args);
^
符号: 变量 SpringApplication
位置: 类 Application
src/Application.java:18: 错误: 找不到符号
return ResultFactory.succ();
^
符号: 变量 ResultFactory
位置: 类 Application
13 个错误
这次多了程序包xxx不存在的报错,找不到符号的错误依然存在的原因就是因为import的包没找到,所以符号依然找不到,如果包找到了,那么这些错误都将会被解决
为什么会报程序包xxx不存在呢?
是因为javac在编译的时候搜索类是按这样顺序搜索的①在当前路径下搜索②如果有使用-cp(-classpath)参数则在-cp指定的路径下搜索③若没有使用-cp则读取环境变量CLASSPATH的值进行搜索,②和③只会执行一个
我们刚才的命令为javac -d classes src/*
没有使用-cp执行classpath,我的电脑也没有设置CLASSPATH。所有只在当前路径下搜索,但是没有搜索到,所以就报错了
编译restful-common
因为manual-springboot依赖restful-common,故我们切到restful-common目录下先编译restful-common,因为restful-common没有使用包外的任何类,所以直接执行javac -d classes src/*
,-d
是指定编译后class文件保存的目录地址
编译manual-springboot
我们将所有import类所在的jar包下载(我是从其他项目里复制的)并放入lib目录下,图片中是lib中的所有包
在manual-springboot目录下执行javac -cp classes:lib/spring-boot-2.3.2.RELEASE.jar:lib/spring-boot-autoconfigure-2.1.6.RELEASE.jar:lib/spring-web-5.1.8.RELEASE.jar:lib/spring-context-5.2.8.RELEASE.jar:lib/spring-core-5.2.8.RELEASE.jar:lib/spring-beans-5.2.8.RELEASE.jar:lib/spring-jcl-5.2.8.RELEASE.jar:lib/spring-aop-5.2.8.RELEASE.jar:lib/spring-expression-5.2.8.RELEASE.jar:lib/spring-boot-starter-tomcat-2.1.6.RELEASE.jar:lib/spring-boot-starter-web-2.1.6.RELEASE.jar:lib/spring-webmvc-5.1.8.RELEASE.jar:lib/tomcat-embed-core-9.0.21.jar:lib/javax.annotation-api-1.3.2.jar:lib/tomcat-embed-websocket-9.0.21.jar:lib/spring-boot-starter-2.1.6.RELEASE.jar:../restful-common/classes -d classes src/Application.java
至此,编译完成。
javac的-cp后面跟的参数是有说法的,我当时在这里踩了几个坑,①-cp会面跟的只能包含class文件夹的地址或归档文件的文件地址,这里不是对应的class文件名,而是放class文件的文件夹名,但是如果是归档文件的话,可以放文件地址;②如果有多个路径的时候使用分隔符分割,这个分隔符不同的操作系统可能是不一样的,在Linux中,用“:”分隔classpath,而在windows中,用“;”分隔
-cp <路径> 指定查找用户类文件和注释处理程序的位置
此外,我只编译了restful-common,并没有将restful-common的class文件打包放入manual-springboot的lib,从我上面的javac命令中也可以看出我写的是文件夹的路径 …/restful-common/classes,如果打了jar包,这里也可以写成jar文件的地址
打包&部署&执行
- 无论打jar还是打war,都是使用jar命令,自己的目录是啥样打完的包里的目录结构就是啥样
- 通过java -jar执行,所有环境变量和命令行指定的搜索路径都将被忽略,除了java基础包中的类外,其他类的只从指定运行的jar中搜搜索
jar命令介绍
用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
选项:
-c 创建新档案
-t 列出档案目录
-x 从档案中提取指定的 (或所有) 文件
-u 更新现有档案
-v 在标准输出中生成详细输出
-f 指定档案文件名
-m 包含指定清单文件中的清单信息
-n 创建新档案后执行 Pack200 规范化
-e 为捆绑到可执行 jar 文件的独立应用程序
指定应用程序入口点
-0 仅存储; 不使用任何 ZIP 压缩
-P 保留文件名中的前导 '/' (绝对路径) 和 ".." (父目录) 组件
-M 不创建条目的清单文件
-i 为指定的 jar 文件生成索引信息
-C 更改为指定的目录并包含以下文件 #跟两个参数 一个是目录 第二个是这个目录下的那个文件或目录
如果任何文件为目录, 则对其进行递归处理。
清单文件名, 档案文件名和入口点名称的指定顺序
与 'm', 'f' 和 'e' 标记的指定顺序相同。
示例 1: 将两个类文件归档到一个名为 classes.jar 的档案中:
jar cvf classes.jar Foo.class Bar.class
示例 2: 使用现有的清单文件 'mymanifest' 并
将 foo/ 目录中的所有文件归档到 'classes.jar' 中:
jar cvfm classes.jar mymanifest -C foo/ .
不打包直接运行
在不打包的情况下我们可以直接通过java -cp xxxxx:xxxx:xxx com.jaan.Application
方式运行
jaan@zhangdeshuaideMacBook-Pro manual-springboot % java -cp classes:lib/spring-boot-2.3.2.RELEASE.jar:lib/spring-boot-autoconfigure-2.1.6.RELEASE.jar:lib/spring-web-5.1.8.RELEASE.jar:lib/spring-context-5.2.8.RELEASE.jar:lib/spring-core-5.2.8.RELEASE.jar:lib/spring-beans-5.2.8.RELEASE.jar:lib/spring-jcl-5.2.8.RELEASE.jar:lib/spring-aop-5.2.8.RELEASE.jar:lib/spring-expression-5.2.8.RELEASE.jar:lib/spring-boot-starter-tomcat-2.1.6.RELEASE.jar:lib/spring-boot-starter-web-2.1.6.RELEASE.jar:lib/spring-webmvc-5.1.8.RELEASE.jar:lib/tomcat-embed-core-9.0.21.jar:lib/javax.annotation-api-1.3.2.jar:lib/tomcat-embed-websocket-9.0.21.jar:lib/spring-boot-starter-2.1.6.RELEASE.jar:../restful-common/classes com.jaan.Application
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.2.RELEASE)
[2023-01-16 19:34:00.188] - 99179 信息 [main] --- com.jaan.Application: Starting Application on zhangdeshuaideMacBook-Pro.local with PID 99179 (/Users/jaan/code/manual/manual-springboot/classes started by jaan in /Users/jaan/code/manual/manual-springboot)
打普通jar包,通过java -jar运行
jar包指定Main-Class的方式有两种,可以通过自己创建META-INF/MENIFEST.MF并将Main-Class写入,也可以通过-e参数指定Main-Class,让jar命令帮我们生成META-INF/MENIFEST.MF
-C这个大C参数后跟两个参数 一个是目录 第二个是这个目录下的那个文件,通过用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files …可以看出,-C是应该写在所有的后面的
jaan@zhangdeshuaideMacBook-Pro manual-springboot % jar -cvfe manual-springboot.jar com.jaan.Application -C classes .
已添加清单
正在添加: .DS_Store(输入 = 6148) (输出 = 443)(压缩了 92%)
正在添加: com/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/jaan/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/jaan/Application.class(输入 = 908) (输出 = 504)(压缩了 44%)
我们打了jar包之后我们可以通过java -cp xxxxx:xxxx:xxx com.jaan.Application
运行,也可以java -jar manual-springboot.jar
运行,java -jar的方式,执行的是jar包中META-INF/MENIFEST.MF里的Main-Class的main方法
jaan@zhangdeshuaideMacBook-Pro manual-springboot % java -jar manual-springboot.jar
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/jaan/restful/common/Result
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:650)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:632)
Caused by: java.lang.ClassNotFoundException: com.jaan.restful.common.Result
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
... 7 more
可以看到这样执行报错了,我们通过前面的编译经验可大概猜测应该是没有通过-cp指定其他的依赖导致的,那我们加上-cp就行了吗?
jaan@zhangdeshuaideMacBook-Pro manual-springboot % java -cp lib/spring-boot-2.3.2.RELEASE.jar:lib/spring-boot-autoconfigure-2.1.6.RELEASE.jar:lib/spring-web-5.1.8.RELEASE.jar:lib/spring-context-5.2.8.RELEASE.jar:lib/spring-core-5.2.8.RELEASE.jar:lib/spring-beans-5.2.8.RELEASE.jar:lib/spring-jcl-5.2.8.RELEASE.jar:lib/spring-aop-5.2.8.RELEASE.jar:lib/spring-expression-5.2.8.RELEASE.jar:lib/spring-boot-starter-tomcat-2.1.6.RELEASE.jar:lib/spring-boot-starter-web-2.1.6.RELEASE.jar:lib/spring-webmvc-5.1.8.RELEASE.jar:lib/tomcat-embed-core-9.0.21.jar:lib/javax.annotation-api-1.3.2.jar:lib/tomcat-embed-websocket-9.0.21.jar:lib/spring-boot-starter-2.1.6.RELEASE.jar:../restful-common/classes -jar manual-springboot.jar
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/jaan/restful/common/Result
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:650)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:632)
Caused by: java.lang.ClassNotFoundException: com.jaan.restful.common.Result
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
... 7 more
答案是不行的,因为如下对-jar的使用介绍
“-jar filename
执行封装在 JAR 文件中的程序。参数是带有清单的 JAR 文件的filename名称,清单中包含一行定义类和用作应用程序起点的方法的表单。当您使用该选项时,指定的 JAR 文件是所有用户类的源,其他类路径设置将被忽略。”
jar包中META-INF/MENIFEST.MF里有一个参数是Class-Path,我们这里可以写我们依赖的类路径。回想之前我们在只指定Main-Class的时候,我们可以通过-e的方式指定,这种方式jar命令会自动帮我们创建一个含有Main-Class的META-INF/MENIFEST.MF,可以不手工创建META-INF/MENIFEST.MF。我们现在要指定Class-Path了,jar命令没有选项给我们让我们指定Class-Path,只能自己手动生成META-INF/MENIFEST.MF然后自己填写MENIFEST.MF的内容然后-m选项来指定MENIFEST.MF文件
我们将我们依赖的地址类路径或jar包通过空格分割写入Class-Path,如下
Main-Class: com.jaan.Application
Class-Path: lib/spring-boot-2.3.2.RELEASE.jar lib/spring-boot-autoconfigure-2.1.6.RELEASE.jar
lib/spring-web-5.1.8.RELEASE.jar lib/spring-context-5.2.8.RELEASE.jar
lib/spring-core-5.2.8.RELEASE.jar lib/spring-beans-5.2.8.RELEASE.jar
lib/spring-jcl-5.2.8.RELEASE.jar lib/spring-aop-5.2.8.RELEASE.jar
lib/spring-expression-5.1.8.RELEASE.jar lib/spring-boot-starter-tomcat-2.1.6.RELEASE.jar
lib/spring-boot-starter-web-2.1.6.RELEASE.jar lib/spring-webmvc-5.1.8.RELEASE.jar
lib/tomcat-embed-core-9.0.21.jar lib/javax.annotation-api-1.3.2.jar
lib/tomcat-embed-websocket-9.0.21.jar lib/spring-boot-starter-2.1.6.RELEASE.jar
../restful-common/restful-common.jar #也可以是../restful-common/classes/但是../restful-common/classes不行
- 记着要换行不然打包的时候会报错:
java.io.IOException: line too long
- 并且使用了-m,并且MENIFEST.MF中含有Main-Class的话就不能使用-e,不然会报错:
不能同时指定 'e' 标记和具有 'Main-Class' 属性的清单!
- Class-Path分多行写时注意:从第二行开始,必须以两个空格开头,不用空格开头和一个空格开头都是不行的,我已经试过了。
- Class-Path中的各项应使用空格分隔,不是逗号或分号。
- Class-Path写完之后最后一定要有一个空行
- jar包内有些配置文件想放在jar包外面,比如文件config.properties:如果这个文件是以路径方式载入的,比如new file(“./config/config.properties”),那么将config.properties放在jar包相同目录下的config目录下即可,也就是说“./”路径等价于jar包所在目录;如果这个文件是以ClassPath下的文件这种方式载入的,比如在Spring中载入classpath:config.properties,则在MF文件的配置文件的ClassPath中添加“./”,然后将这个配置文件与jar包放在同一个目录即可,当然也可以在MF文件的配置文件的ClassPath中添加“./config/”,然后把配置文件都放在jar包相同目录下的config目录下
- MENIFEST.MF中Class-Path下填写class文件的目录的时候必须写成这样…/restful-common/classes/,最后的
/
一定要有不然查找不到类,这里跟java -cp指定class文件目录的时候有区别,java -cp指定的时候目录最后的/
写不写都可以
jaan@zhangdeshuaideMacBook-Pro manual-springboot % java -jar manual-springboot.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.2.RELEASE)
[2023-01-30 16:09:52.412] - 21000 信息 [main] --- com.jaan.Application: Starting Application on zhangdeshuaideMacBook-Pro.local with PID 21000 (/Users/jaan/code/manual/manual-springboot/manual-springboot.jar started by jaan in /Users/jaan/code/manual/manual-springboot)
[2023-01-30 16:09:52.423] - 21000 信息 [main] --- com.jaan.Application: No active profile set, falling back to default profiles: default
[2023-01-30 16:09:53.019] - 21000 警告 [main] --- org.apache.tomcat.util.modeler.Registry: The MBean registry cannot be disabled because it has already been initialised
[2023-01-30 16:09:53.091] - 21000 信息 [main] --- org.springframework.boot.web.embedded.tomcat.TomcatWebServer: Tomcat initialized with port(s): 8080 (http)
[2023-01-30 16:09:53.111] - 21000 信息 [main] --- org.apache.catalina.core.StandardService: Starting service [Tomcat]
[2023-01-30 16:09:53.112] - 21000 信息 [main] --- org.apache.catalina.core.StandardEngine: Starting Servlet engine: [Apache Tomcat/9.0.21]
[2023-01-30 16:09:53.177] - 21000 信息 [main] --- org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/]: Initializing Spring embedded WebApplicationContext
[2023-01-30 16:09:53.177] - 21000 信息 [main] --- org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext: Root WebApplicationContext: initialization completed in 717 ms
[2023-01-30 16:09:53.297] - 21000 信息 [main] --- org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor: Initializing ExecutorService 'applicationTaskExecutor'
[2023-01-30 16:09:53.440] - 21000 信息 [main] --- org.springframework.boot.web.embedded.tomcat.TomcatWebServer: Tomcat started on port(s): 8080 (http) with context path ''
[2023-01-30 16:09:53.454] - 21000 信息 [main] --- com.jaan.Application: Started Application in 1.307 seconds (JVM running for 1.528
打fat jar通过java -jar
- fat jar就是胖jar,也就是说这个jar里就包含运行时需要的所有类,包括依赖包。
虽然上节里的也是java -jar,但是跟这节的还是有区别的,区别就是,上节jar包里MENIFEST.MF存在Class-Path,且指定的都是jar包外的目录或其他jar包。 - jar 包里嵌套其他 jar,这个方法可以彻底避免解压同名覆盖的问题,但是这个方法不被 JVM 原生支持,因为 JDK 提供的 ClassLoader 仅支持装载嵌套 jar 包的 class 文件。所以这种方法需要自定义 ClassLoader 以支持嵌套 jar。
故打这种包是需要自定义ClassLoader并在MENIFEST.MF存在Class-Path指定出的,所以一般我们不会自己写,都是使用插件来进行打包,插件将会把他们开发好的自定义ClassLoader的这套代码来放到我们的jar里,并且以他们自定义ClassLoader约定的目录格式来打包,如果我们不用插件的话,这相当于我们自己开发了个软件,这个软件是专门读取jar包里嵌套jar包的,还有就是我们需要自己将jar包的目录调整到ClassLoader代码里约定的结构。如果使用插件的话这些工作全部自动完成。
本文不进行打fat jar实操,下面我放一个打完的springboot项目的fat jar里的目录结构及个别文件内容
顶级目录就是这三个BOOT-INF、META-INF、org
|____org
| |____springframework
| | |____boot
| | | |____loader
| | | | |____archive
| | | | | |____*****.class(很多class文件)
| | | | |____util
| | | | | |____SystemPropertyUtils.class
| | | | |____jar
| | | | | |____******.class(很多class文件)
| | | | |____PropertiesLauncher$PrefixMatchingArchiveFilter.class
| | | | |____MainMethodRunner.class
| | | | |____PropertiesLauncher$1.class
| | | | |____JarLauncher.class
| | | | |____LaunchedURLClassLoader.class
| | | | |____WarLauncher.class
| | | | |____ExecutableArchiveLauncher.class
| | | | |____Launcher.class
| | | | |____PropertiesLauncher$ArchiveEntryFilter.class
|____LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class
| | | | |____data
| | | | | |____RandomAccessData.class
| | | | | |____RandomAccessDataFile.class
| | | | | |____RandomAccessDataFile$1.class
| | | | | |____RandomAccessDataFile$DataInputStream.class
| | | | | |____RandomAccessDataFile$FileAccess.class
| | | | |____PropertiesLauncher.class
|____META-INF
| |____MANIFEST.MF
| |____maven
| | |____com.iboxchain.goodarights
| | | |____pay-web
| | | | |____pom.xml
| | | | |____pom.properties
| |____build-info.properties
| |____dubbo
| | |____com.alibaba.dubbo.rpc.Filter
|____BOOT-INF(我们自己写的代码和配置文件,代码略,部分配置文件略)
| |____classes
| | |____mybatis
| | | |____mybatis-config.xml
| | |____errorCode.properties
| | |____dubbo
| | | |____applicationContext-dubbo-provider.xml
| | | |____applicationContext-dubbo.xml
| | | |____dubbo-provider-biz.xml
| | |____com(我们的代码-略)
| |____lib(所有依赖的jar包都在这里)
| | |____*******.jar
下面是MANIFEST.MF文件的内容
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: appl
Start-Class: com.iboxchain.goodarights.pay.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.1.6.RELEASE
Created-By: Apache Maven 3.6.3
Build-Jdk: 1.8.0_65
Main-Class: org.springframework.boot.loader.JarLauncher
打war,通过部署至tomcat运行
如果是非.war的文件无反应
如果是.war的文件,可以部署,且支持热部署,也就是说不用重启tomcat,只要你丢进去webappst里tomcat会检测到,然后进行部署,
如果是.war但是war里的文件目录结构不符合约定,则访问不了
如果.war里的无继承SpringBootServletInitializer无重写SpringApplicationBuilder configure(SpringApplicationBuilder application)则没有spring启动的日志,就好像部署了没任何反应。
此时浏览器访问http://localhost:8080/manual-springboot/hello
继承并重写之后出现spring日志,此时catalina.out报错:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.2.RELEASE)
一月 30, 2023 9:33:55 下午 org.springframework.boot.SpringApplication reportFailure
严重: Application run failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.jaan.Application]; nested exception is java.lang.IllegalStateException: Fail
ed to introspect annotated methods on class com.jaan.Application
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:188)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:319)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:280)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:173)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:153)
at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:95)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:171)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5218)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:727)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1016)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1903)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Failed to introspect annotated methods on class com.jaan.Application
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:162)
at org.springframework.context.annotation.ConfigurationClassParser.retrieveBeanMethodMetadata(ConfigurationClassParser.java:402)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:325)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:249)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:206)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:174)
... 27 more
Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.jaan.Application] from ClassLoader [ParallelWebappClassLoader
context: manual-springboot
delegate: false
----------> Parent Classloader:
java.net.URLClassLoader@e9e54c2
]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481)
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:455)
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:151)
... 32 more
Caused by: java.lang.NoClassDefFoundError: com/jaan/restful/common/Result
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.getDeclaredMethods(Class.java:1975)
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463)
... 34 more
Caused by: java.lang.ClassNotFoundException: com.jaan.restful.common.Result
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1420)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1228)
... 38 more
根据报错看出应该是打的war包中缺少了com.jaan.restful.common.Result类的jar包,将该jar包放入manual-springboot/war/WEB-INF/lib下,重新打包
重新打包后catalina.out日志无报错,但只有spring logo
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.2.RELEASE)
30-Jan-2023 21:50:39.214 信息 [localhost-startStop-6] org.apache.catalina.startup.HostConfig.deployWAR web应用程序存档文件[/Library/tomcat-8.5.84/webapps/manual-springboot.war]的部
署已在[2,419]ms内完成
此时浏览器访问http://localhost:8080/manual-springboot/hello
说明项目是部署成功了,只是浏览器请求hello接口的时候项目报错了而已
纯手工命令开发打包部署的缺点
参考
- https://blog.csdn.net/bible_reader/article/details/105110864
- https://blog.csdn.net/sayyy/article/details/81120749
- https://docs.oracle.com/javase/10/tools/java.htm#JSWOR624
- https://blog.csdn.net/wter26/article/details/104758345/