作为一个老java, 说来惭愧,spring boot出来这么多年了,才第一次使用。 不知从何下手,先看看官方文档 说了些什么吧。
后面内容就是照搬, 没动脑,只是记录,免得以后还得面对abc,实在是累
环境 首先确认开发环境
1 2 3 4 5 6 7 8 9 10 11 $ java -version java version "1.8.0_201" Java(TM) SE Runtime Environment (build 1.8.0_201-b09) Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode) $ mvn -v Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-25T02:41:47+08:00) Maven home: /Users/huazhizui/application/apache-maven-3.6.0 Java version: 1.8.0_201, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre Default locale: zh_CN, platform encoding: UTF-8 OS name: "mac os x", version: "10.15.4", arch: "x86_64", family: "mac"
创建一个目录,作为本示例的工作目录, 后面都操作都在这个目录中进行。
1 2 $ mkdir spring-boot-demo2 $ cd spring-boot-demo2
创建pom文件
作为一个maven项目, 那必须得有一个pom.xml
文件
在pom.xml
文件中添加如下内容:
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 <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>myproject</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.0.RELEASE</version> </parent> <description/> <developers> <developer/> </developers> <licenses> <license/> </licenses> <scm> <url/> </scm> <url/> <!-- Additional lines to be added here... --> </project>
这个pom
里面我们只设置了一个parent
节点, spring-boot-starter-parent
是一个特殊的starter
,它主要提供一些maven默认配置, 还有依赖管理器, 这样我们在引入依赖的时候, 就可以省略version
标签了, 他们使用spring-boot-starter-parent
中 dependency-management
里定义的版本。 但是它本身不会添加任何实际的依赖。我们查看一下maven依赖树,可以看到依赖是空的:
添加web starter 既然要做web应用, 那添加的第一个依赖必须是spring-boot-starter-web
1 2 3 4 5 6 7 8 9 10 11 <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <!-- 省略了其它配置 --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <!-- 省略了其它配置 --> </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 $ mvn dependency:tree [INFO] com.example:myproject:jar:0.0.1-SNAPSHOT [INFO] \- org.springframework.boot:spring-boot-starter-web:jar:2.3.0.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot-starter:jar:2.3.0.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot:jar:2.3.0.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.3.0.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot-starter-logging:jar:2.3.0.RELEASE:compile [INFO] | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile [INFO] | | | +- ch.qos.logback:logback-core:jar:1.2.3:compile [INFO] | | | \- org.slf4j:slf4j-api:jar:1.7.30:compile [INFO] | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.13.2:compile [INFO] | | | \- org.apache.logging.log4j:log4j-api:jar:2.13.2:compile [INFO] | | \- org.slf4j:jul-to-slf4j:jar:1.7.30:compile [INFO] | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile [INFO] | +- org.springframework:spring-core:jar:5.2.6.RELEASE:compile [INFO] | | \- org.springframework:spring-jcl:jar:5.2.6.RELEASE:compile [INFO] | \- org.yaml:snakeyaml:jar:1.26:compile [INFO] +- org.springframework.boot:spring-boot-starter-json:jar:2.3.0.RELEASE:compile [INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.11.0:compile [INFO] | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.11.0:compile [INFO] | | \- com.fasterxml.jackson.core:jackson-core:jar:2.11.0:compile [INFO] | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.11.0:compile [INFO] | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.11.0:compile [INFO] | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.11.0:compile [INFO] +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.3.0.RELEASE:compile [INFO] | +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.35:compile [INFO] | +- org.glassfish:jakarta.el:jar:3.0.3:compile [INFO] | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.35:compile [INFO] +- org.springframework:spring-web:jar:5.2.6.RELEASE:compile [INFO] | \- org.springframework:spring-beans:jar:5.2.6.RELEASE:compile [INFO] \- org.springframework:spring-webmvc:jar:5.2.6.RELEASE:compile [INFO] +- org.springframework:spring-aop:jar:5.2.6.RELEASE:compile [INFO] +- org.springframework:spring-context:jar:5.2.6.RELEASE:compile [INFO] \- org.springframework:spring-expression:jar:5.2.6.RELEASE:compile [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 03:08 min [INFO] Finished at: 2020-05-25T16:29:09+08:00 [INFO] ------------------------------------------------------------------------
可以看到spring-web
spring-beans
spring-webmvc
等spring web应用必须的jar包都添加进来了, 还有一个特殊的spring-boot-starter-tomcat
, 后面会知道它有什么用。
至此, spring boot web 开发环境就搭建好了, 简直不能更方便了。 开始写业务代码吧
开始编写业务代码了 根据maven的尿性,代码要放在src/main/java
目录,我们建一个:
1 $ mkdir -p src/main/java
然后在此目录下建我们java文件src/main/java/Example.java
:
1 $ touch src/main/java/Example.java
现在项目目录结构如下:
1 2 3 4 5 6 7 $ tree . . ├── pom.xml └── src └── main └── java └── Example.java
用最喜欢的编辑器编辑Example.java
, 写入如下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import org.springframework.boot.*; import org.springframework.boot.autoconfigure.*; import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration public class Example { @RequestMapping("/") String home() { return "Hello World!"; } public static void main(String[] args) { SpringApplication.run(Example.class, args); } }
运行项目 注意到有public static void main(String[] args)
方法, 这是java程序执行入口,如果在IDE里面, 直接运行这个类就可以了。
由于我们使用了spring-boot-starter-parent
, 所以可以直接在命令行运行我们的程序:
输出内容大致是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [INFO] Attaching agents: [] . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.0.RELEASE) 2020-05-25 17:21:42.595 INFO 4957 --- [ main] Example : Starting Example on huazhizuideMacBook-Pro.local with PID 4957 (/Users/huazhizui/code/java/spring-boot-demo2/target/classes started by huazhizui in /Users/huazhizui/code/java/spring-boot-demo2) 2020-05-25 17:21:42.596 INFO 4957 --- [ main] Example : No active profile set, falling back to default profiles: default 2020-05-25 17:21:43.191 INFO 4957 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-05-25 17:21:43.232 INFO 4957 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-05-25 17:21:43.232 INFO 4957 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.35] 2020-05-25 17:21:43.292 INFO 4957 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-05-25 17:21:43.292 INFO 4957 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 666 ms 2020-05-25 17:21:43.408 INFO 4957 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-05-25 17:21:43.529 INFO 4957 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-05-25 17:21:43.535 INFO 4957 --- [ main] Example : Started Example in 1.354 seconds (JVM running for 1.72)
可以看到启动了一个tomcat, 服务端口是8080
, 非常方便, 不用自己安装tomcat, 配置一大堆东西, just one command, 这就是spring-boot-starter-tomcat
的作用。
浏览器访问一下http://localhost:8080/
, 返回
分分钟, 一个简单的web项目就完成。
项目打包成可执行jar 项目依赖较多的情况下, 使用maven标准方法把项目打包成可执行jar, 问题一大堆, 被坑过的都懂。 以前使用maven UberJar
插件, 经常一通配置可以解决。但spring boot 提供了更好的方案: spring-boot-maven-plugin
。 pom文件添加如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <!-- 省略了其它配置 --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <!-- 省略了其它配置 --> </project>
然后运行mvn package
, 整个项目和依赖就全部打到一个jar包里了
1 2 3 4 5 6 7 8 9 $ mvn package ... [INFO] --- spring-boot-maven-plugin:2.3.0.RELEASE:repackage (repackage) @ myproject --- [INFO] Replacing main artifact with repackaged archive ... $ ll target ... -rw-r--r-- 1 huazhizui staff 16M 5 25 18:23 myproject-0.0.1-SNAPSHOT.jar -rw-r--r-- 1 huazhizui staff 2.2K 5 25 18:23 myproject-0.0.1-SNAPSHOT.jar.original
查看target
目录,这个myproject-0.0.1-SNAPSHOT.jar
文件大概16M, 就是我们要的可执行jar包了, myproject-0.0.1-SNAPSHOT.jar.original
是我们原来单纯的jar包,只有2.2K, 只包含我们自己写的代码。
如果好奇这个巨大的16M jar包里空间都有些撒, 请使用:
1 $ jar tvf target/myproject-0.0.1-SNAPSHOT.jar
这个包含一切的jar包, 执行姿势如下:
1 $ java -jar target/myproject-0.0.1-SNAPSHOT.jar
特别提示: java -jar
模式下,程序运行的classpath
只有这个jar包,就算指定了-classpath
选项,也没选用, 会直接被忽略。
运行效果和mvn spring-boot:run
一致。
总结 从搭建开始到最终页面展示“Hello World!” , 我们只写了10来行代码, 一个配置文件都没写过, 甚至连web.xml
文件都没管过。这究竟是如何发生的?
看一下Example.java
, 注意到三个注解@RestController
,@EnableAutoConfiguration
, @RequestMapping
, 还有一个main
方法, 这在普通的web项目里可以没有的。
@RestController、@RequestMapping
注解 这两注解, 是SpringMvc
里的注解,@RestController
表明这个类是一个Controller可以用来处理http请求, @RequestMapping
表明了这个为和方法能处理哪些请求地址。
@EnableAutoConfiguration
注解和自动配置这个注解就厉害了, 这就是0配置的核心, 有个这个注解, spring boot 会根据我们依赖的jar包来推测出我们需要对spring做的配置。这样我们就不需要手动去写一大堆spring配置文件了。
我们这个项目中,由于我们依赖了spring-boot-starter-web
,这个starter 添加了springMvc
和tomcat
依赖, 所以spring 自动配置功能 就会假设这是个web项目, 并做出相应的配置, 包括tomcat的配置。
当然, 自动配置功能 并不是只能跟starter一起使用, 就算不用starter, 通过自己手动添加依赖, 自动配置也能正常工作。
main 方法 可以看出这是java标准的main方法, 是java程序的入口。但是我们都知道, 通常web项目是在web容器中运行, 所以并不需要main方法, 那这个main方法是干什么用的?
回到pom.xml
文件, 发现里面并没有正常web项目都会有的<packaging>war</packaging>
标签, 这是标签是告诉maven我们的项目要打成war包。 既然这个项目没有, 那说明, 压根就不是在容器里运行的, 而是做为普通的java程序 运行的。 我们程序运行起来也只干一件事, 调用SpringApplication.run
, 告诉SpringApplication
该干活了。
为了验证这个猜想, 我们可以看看通过mvn spring-boot:run
启动项目的实际命令, 先看一看有哪些java 进程:
1 2 3 $ jps -mlv | grep -v "jps" 1111 org.codehaus.plexus.classworlds.launcher.Launcher spring-boot:run -Dclassworlds.conf=/Users/huazhizui/application/apache-maven-3.6.0/bin/m2.conf -Dmaven.home=/Users/huazhizui/application/apache-maven-3.6.0 -Dlibrary.jansi.path=/Users/huazhizui/application/apache-maven-3.6.0/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/Users/huazhizui/code/java/spring-boot-demo2 1149 Example -Xverify:none -XX:TieredStopAtLevel=1
可以看到两个进程1111
和 1149
, 可以看到1111
是maven进程, 它的参数是spring-boot:run
。 1149
就是运行我们项目的进程,查看它的完整命令:
1 2 $ ps -ef | grep "1149" | grep -v "grep" 501 1149 1111 0 4:53下午 ttys001 0:03.06 /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/bin/java -Xverify:none -XX:TieredStopAtLevel=1 -cp /Users/huazhizui/code/java/spring-boot-demo2/target/classes:/Users/huazhizui/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.3.0.RELEASE/spring-boot-starter-web-2.3.0.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/boot/spring-boot-starter/2.3.0.RELEASE/spring-boot-starter-2.3.0.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/boot/spring-boot/2.3.0.RELEASE/spring-boot-2.3.0.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.3.0.RELEASE/spring-boot-autoconfigure-2.3.0.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.3.0.RELEASE/spring-boot-starter-logging-2.3.0.RELEASE.jar:/Users/huazhizui/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/huazhizui/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/huazhizui/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:/Users/huazhizui/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.2/log4j-to-slf4j-2.13.2.jar:/Users/huazhizui/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.2/log4j-api-2.13.2.jar:/Users/huazhizui/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/Users/huazhizui/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/huazhizui/.m2/repository/org/springframework/spring-core/5.2.6.RELEASE/spring-core-5.2.6.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/spring-jcl/5.2.6.RELEASE/spring-jcl-5.2.6.RELEASE.jar:/Users/huazhizui/.m2/repository/org/yaml/snakeyaml/1.26/snakeyaml-1.26.jar:/Users/huazhizui/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.3.0.RELEASE/spring-boot-starter-json-2.3.0.RELEASE.jar:/Users/huazhizui/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.0/jackson-databind-2.11.0.jar:/Users/huazhizui/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.0/jackson-annotations-2.11.0.jar:/Users/huazhizui/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.0/jackson-core-2.11.0.jar:/Users/huazhizui/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.0/jackson-datatype-jdk8-2.11.0.jar:/Users/huazhizui/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.0/jackson-datatype-jsr310-2.11.0.jar:/Users/huazhizui/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.0/jackson-module-parameter-names-2.11.0.jar:/Users/huazhizui/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.3.0.RELEASE/spring-boot-starter-tomcat-2.3.0.RELEASE.jar:/Users/huazhizui/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.35/tomcat-embed-core-9.0.35.jar:/Users/huazhizui/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar:/Users/huazhizui/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.35/tomcat-embed-websocket-9.0.35.jar:/Users/huazhizui/.m2/repository/org/springframework/spring-web/5.2.6.RELEASE/spring-web-5.2.6.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/spring-beans/5.2.6.RELEASE/spring-beans-5.2.6.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/spring-webmvc/5.2.6.RELEASE/spring-webmvc-5.2.6.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/spring-aop/5.2.6.RELEASE/spring-aop-5.2.6.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/spring-context/5.2.6.RELEASE/spring-context-5.2.6.RELEASE.jar:/Users/huazhizui/.m2/repository/org/springframework/spring-expression/5.2.6.RELEASE/spring-expression-5.2.6.RELEASE.jar Example
可以看到1149
是 1111
的子进程, 说明我们这个项目是通过maven启动的。整个命令非常长, 但大部分是设置classpath, 简化看看,就是这样一条命令
1 java -Xverify:none -XX:TieredStopAtLevel=1 -cp ... Example
得证,springboot 的运行方式,就是一个普通java 程序运行的方式
当然这种通过maven插件启动的方式, 只能在开发时用, 上线的话应该打成可运行jar包 来运行
仔细观察下 -cp, 其实就是我们项目的/target/classes
目录和所有依赖jar 的组合, 很正常,很标准
既然我们项目只是个普通java程序, 那为什么能提供web服务? 这就是SpringApplication.run
干的事情了,我们main方法调用SpringApplication.run
, 它先初始化好spring环境,根据依赖知道是个web项目而且引用了tomcat, 所以配置好tomcat, 然后启动tomcat,提供web服务功能。
至此,算是初识了spring boot