一、前言
本系列用于学习SpringBoot,这也是开篇之作。由于博主也是刚刚接触微服务,且当前微服务盛行,无论是从个人技能的提升还是工作中的需要出发,都要求我们去学习SpringBoot。好记性不如烂笔头、知识分享,本着这些个高大上的观念,博主决定使用博客记录下来,希望能有所得。
通常微服务被人津津乐道的有两点:服务拆分、开发迅速。
所谓服务拆分,是指类似于SpringBoot的微服务框架能够将某个业务功能从系统中拆分出来,单独使用,重复利用,比如电商中的支付、积分、用户系统等。个人认为,微服务通常伴随着高可用、高并发、前后端分离等特点。
开发迅速目前主要体现在项目搭建的快速性,这里的快速性体现在简化配置,并践行“约定大于配置”的原则。不同于传统的框架,SpringBoot不需要配置繁琐的配置文件,很多的东西框架已经帮我们做好了,我们只需要引入相关的依赖就可以了。例如:Spring Boot内嵌Tomcat、springframework,省略了web.xml的配置,我们只需设置starter即可,这极大简化了web项目的配置。设置如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 去掉默认的tomcat部署方式 -->
<!--<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>-->
</dependency>
二、搭建框架
1. 开发工具
博主使用IDEA来搭建我的第一个微服务框架,使用maven来管理项目,具体配置jdk、maven的过程不进行叙述,主要说一点:
由于IDEA和myeclipse一样是要收费的,许多人下载的免费版,因此在创建springboot项目时少了 Spring Initializr 这个选择。这时我们可以选择创建一个maven项目,然后将一些springboot需要的东西手动加进来。
2. 搭建框架
2.1 选择file-new-project,选择Spring Initializr,再选择项目的jdk,点击next
2.2 补充项目信息,主要填写 group、artifact 两项信息,完成后点击下一步
2.3 选择依赖,这里只需勾选web即可
2.4 选择项目路径,这里需要提前创建一个同名的文件夹
2.5 点击下一步,由于需要导入相关的配置和下载依赖包,因此需要等待一段时间。项目的结构如下:
目录解析:
java——存放后台代码
resources——这个包下主要存放一些配置文件和静态资源,相对于springMVC等传统框架,springboot的前端资源都是存放在resources目录下,也可以清楚地看到,这里并没有webapp这样的目录信息
pom——与maven的pom没有区别,主要是一些项目的信息、jar包、部署信息等
PullulatesApplication为程序的入口,启动类,具体的信息下文再介绍。
2.6 添加controller
接下来,我们要添加一个访问的控制类,也就是controller。新建一个包,用来存放后台的代码。这里为了方便之后的扩展,直接将目录结构提前写好。
UserController里的内容如下:
package top.pullulates.project.system.user.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* HelloWorld
*
*/
@RestController
public class UserController {
@RequestMapping(value="/")
public String hello() {
return "my first springboot project";
}
}
接下来就是运行程序,可以选择右击项目,点击运行PullulatesApplication,也可以进入该类运行main方法,项目的启动信息如下:
E:\JAVA\JDK1.8\bin\java.exe -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=53335 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=localhost -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true "-javaagent:E:\开发软件\IDEA\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar=53336:E:\开发软件\IDEA\IntelliJ IDEA 2018.2.4\bin" -Dfile.encoding=UTF-8 -classpath "E:\JAVA\JDK1.8\jre\lib\charsets.jar;E:\JAVA\JDK1.8\jre\lib\deploy.jar;E:\JAVA\JDK1.8\jre\lib\ext\access-bridge-64.jar;E:\JAVA\JDK1.8\jre\lib\ext\cldrdata.jar;E:\JAVA\JDK1.8\jre\lib\ext\dnsns.jar;E:\JAVA\JDK1.8\jre\lib\ext\jaccess.jar;E:\JAVA\JDK1.8\jre\lib\ext\jfxrt.jar;E:\JAVA\JDK1.8\jre\lib\ext\localedata.jar;E:\JAVA\JDK1.8\jre\lib\ext\nashorn.jar;E:\JAVA\JDK1.8\jre\lib\ext\sunec.jar;E:\JAVA\JDK1.8\jre\lib\ext\sunjce_provider.jar;E:\JAVA\JDK1.8\jre\lib\ext\sunmscapi.jar;E:\JAVA\JDK1.8\jre\lib\ext\sunpkcs11.jar;E:\JAVA\JDK1.8\jre\lib\ext\zipfs.jar;E:\JAVA\JDK1.8\jre\lib\javaws.jar;E:\JAVA\JDK1.8\jre\lib\jce.jar;E:\JAVA\JDK1.8\jre\lib\jfr.jar;E:\JAVA\JDK1.8\jre\lib\jfxswt.jar;E:\JAVA\JDK1.8\jre\lib\jsse.jar;E:\JAVA\JDK1.8\jre\lib\management-agent.jar;E:\JAVA\JDK1.8\jre\lib\plugin.jar;E:\JAVA\JDK1.8\jre\lib\resources.jar;E:\JAVA\JDK1.8\jre\lib\rt.jar;F:\IDEASpace\pullulates\target\classes;C:\Users\win 10\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.0.5.RELEASE\spring-boot-starter-web-2.0.5.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\boot\spring-boot-starter\2.0.5.RELEASE\spring-boot-starter-2.0.5.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\boot\spring-boot\2.0.5.RELEASE\spring-boot-2.0.5.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.0.5.RELEASE\spring-boot-autoconfigure-2.0.5.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.0.5.RELEASE\spring-boot-starter-logging-2.0.5.RELEASE.jar;C:\Users\win 10\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\win 10\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\win 10\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.10.0\log4j-to-slf4j-2.10.0.jar;C:\Users\win 10\.m2\repository\org\apache\logging\log4j\log4j-api\2.10.0\log4j-api-2.10.0.jar;C:\Users\win 10\.m2\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;C:\Users\win 10\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\win 10\.m2\repository\org\yaml\snakeyaml\1.19\snakeyaml-1.19.jar;C:\Users\win 10\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.0.5.RELEASE\spring-boot-starter-json-2.0.5.RELEASE.jar;C:\Users\win 10\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.9.6\jackson-databind-2.9.6.jar;C:\Users\win 10\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;C:\Users\win 10\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.9.6\jackson-core-2.9.6.jar;C:\Users\win 10\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.6\jackson-datatype-jdk8-2.9.6.jar;C:\Users\win 10\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.6\jackson-datatype-jsr310-2.9.6.jar;C:\Users\win 10\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.6\jackson-module-parameter-names-2.9.6.jar;C:\Users\win 10\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.0.5.RELEASE\spring-boot-starter-tomcat-2.0.5.RELEASE.jar;C:\Users\win 10\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.34\tomcat-embed-core-8.5.34.jar;C:\Users\win 10\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.34\tomcat-embed-el-8.5.34.jar;C:\Users\win 10\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.34\tomcat-embed-websocket-8.5.34.jar;C:\Users\win 10\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.12.Final\hibernate-validator-6.0.12.Final.jar;C:\Users\win 10\.m2\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;C:\Users\win 10\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\Users\win 10\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\win 10\.m2\repository\org\springframework\spring-web\5.0.9.RELEASE\spring-web-5.0.9.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\spring-beans\5.0.9.RELEASE\spring-beans-5.0.9.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\spring-webmvc\5.0.9.RELEASE\spring-webmvc-5.0.9.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\spring-aop\5.0.9.RELEASE\spring-aop-5.0.9.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\spring-context\5.0.9.RELEASE\spring-context-5.0.9.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\spring-expression\5.0.9.RELEASE\spring-expression-5.0.9.RELEASE.jar;C:\Users\win 10\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\Users\win 10\.m2\repository\org\springframework\spring-core\5.0.9.RELEASE\spring-core-5.0.9.RELEASE.jar;C:\Users\win 10\.m2\repository\org\springframework\spring-jcl\5.0.9.RELEASE\spring-jcl-5.0.9.RELEASE.jar" top.pullulates.PullulatesApplication
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)
2018-09-29 14:14:43.751 INFO 3480 --- [ main] top.pullulates.PullulatesApplication : Starting PullulatesApplication on Win10 with PID 3480 (F:\IDEASpace\pullulates\target\classes started by win 10 in F:\IDEASpace\pullulates)
2018-09-29 14:14:43.753 INFO 3480 --- [ main] top.pullulates.PullulatesApplication : No active profile set, falling back to default profiles: default
2018-09-29 14:14:43.780 INFO 3480 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5c1a8622: startup date [Sat Sep 29 14:14:43 CST 2018]; root of context hierarchy
2018-09-29 14:14:44.468 INFO 3480 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2018-09-29 14:14:44.483 INFO 3480 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2018-09-29 14:14:44.483 INFO 3480 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.34
2018-09-29 14:14:44.486 INFO 3480 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [E:\JAVA\JDK1.8\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;E:\JAVA\ORACLE\product\11.2.0\dbhome_1\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;E:\开发软件\TORTOISE SVN\bin;E:\JAVA\JDK1.8\bin;E:\JAVA\JDK1.8\jre\bin;E:\JAVA\TOMCATE\tomcat8.0\bin;E:\JAVA\MAVEN\apache-maven-3.0.3\bin;E:\JAVA\MYSQL\bin;C:\Users\win 10\AppData\Local\Microsoft\WindowsApps;;.]
2018-09-29 14:14:44.556 INFO 3480 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2018-09-29 14:14:44.556 INFO 3480 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 778 ms
2018-09-29 14:14:44.599 INFO 3480 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2018-09-29 14:14:44.602 INFO 3480 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-09-29 14:14:44.602 INFO 3480 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-09-29 14:14:44.602 INFO 3480 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-09-29 14:14:44.602 INFO 3480 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2018-09-29 14:14:44.723 INFO 3480 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-29 14:14:44.837 INFO 3480 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5c1a8622: startup date [Sat Sep 29 14:14:43 CST 2018]; root of context hierarchy
2018-09-29 14:14:44.867 INFO 3480 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String top.pullulates.project.system.user.controller.UserController.hello()
2018-09-29 14:14:44.870 INFO 3480 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-09-29 14:14:44.870 INFO 3480 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-09-29 14:14:44.883 INFO 3480 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-29 14:14:44.883 INFO 3480 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-29 14:14:44.959 INFO 3480 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-09-29 14:14:44.983 INFO 3480 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-09-29 14:14:44.986 INFO 3480 --- [ main] top.pullulates.PullulatesApplication : Started PullulatesApplication in 1.45 seconds (JVM running for 1.92)
2018-09-29 14:15:01.858 INFO 3480 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-09-29 14:15:01.858 INFO 3480 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2018-09-29 14:15:01.871 INFO 3480 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 13 ms
打开浏览器,访问 http://localhost:8080/,即可出现
ok,大功告成!我们可以发现,相对于传统的ssm等框架,我们没有配置web.xml,也没有配置tomcate,更没有spring-mvc.xml,一切都变得很简单。接下来我们看一下细节的信息。
三、了解springboot
1. 选择Spring Initializr并勾选web后,发生了什么?
Spring Initializr是一个快速构建maven或gradle项目的应用,它允许我们使用两种方式,一是连接start.spring.io去创建,二是使用自定义的方式去创建。由于第一种方式受制于网络,所以也有很多人选择将Spring Initializr down到本地来,然后使用第二种方法来构建项目。也就是说,我们在选择Spring Initializr后,idea会帮我们去创建一个maven项目,而之后勾选的web,会帮我们引入spring的相关配置和依赖,这点在maven的pom中有所体现。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. web.xml去哪了?
即便是原生servlet(不使用web框架),web.xml就是非常重要的一个配置,无论是servlet、filter、listener都需要在web.xml里面配置下。但是在servlet3.0里,这个配置得到了简化,可以通过java配置(注解等)省去web.xml配置。其中javax.servlet.ServletContainerInitializer为尤其重要的类,这个类会在web容器启动阶段被回调,可以在onStartup方法里做一些servlet、filter、listener的注册等操作。
startup的逻辑是在web容器启动后,调用所有WebApplicationInitializer的onStartup方法。而SpringServletContainerInitializer被@HandlesTypes(WebApplicationInitializer.class)修饰,因此在项目启动时,SpringServletContainerInitializer会被传入onStartup里作为参数。一般使用Springboot的时候,都会继承一个类SpringBootServletInitializer,WebApplicationInitializer由SpringBootServletInitializer实现,在这个类的onStartup方法中,启动了整个Spring容器。
具体信息请参照博客:springboot 是如何帮我们省去web.xml配置的
3. tomcat呢?还可以选择其他的部署方式吗?
springboot内嵌tomcat,关于tomcat的配置信息可以在配置文件里进行配置。如果希望使用其他方式启动项目比如jetty,可以在pom中指定并引入对应的依赖信息。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 去掉默认的tomcat部署方式 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- jetty容器引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
4. 怎么去配置项目的信息呢?
关于项目、数据库、tomcat等的信息可以在application.propertie或application.yml中配置(最常用的两种,springboot还支持xml和yaml),我们这里使用后者,路径这里也做个修改,将配置文件统一放在config的目录下。
那么,配置文件的路径可以随意修改吗?
SpringBoot配置文件默认可以放到以下目录中,可以自动读取到:
- 项目根目录下
- 项目根目录中config目录下
- 项目的resources目录下
- 项目resources目录中config目录下
如果在不同的目录中存在多个配置文件,它的读取顺序是:
1、config/application.properties(项目根目录中config目录下)
2、config/application.yml
3、application.properties(项目根目录下)
4、application.yml
5、resources/config/application.properties(项目resources目录中config目录下)
6、resources/config/application.yml
7、resources/application.properties(项目的resources目录下)
8、resources/application.yml
注:
1、如果同一个目录下,有application.yml也有application.properties,默认先读取application.properties。
2、如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,后面读取的不覆盖前面读取到的。
3、创建SpringBoot项目时,一般的配置文件放置在“项目的resources目录下”
以上信息源于:SpringBoot 配置文件存放位置及读取顺序
5. 关于@RestController
@RestController相当于 @Controller + @ResponseBody,返回string,如果是json,则返回json的字符串