spring boot 1.x完整學習指南(含各類常見問題servlet、web.xml、maven打包,spring mvc差異及解決方法)

spring boot 入門

關於版本的選擇,spring boot 2.0開始依賴於  Spring Framework 5.1.0,而spring 5.x和以前的版本差距比較大,並且應該來講尚未普遍的使用,因此生產中,通常來講目前仍是建議使用spring boot 1.x,目前最新版本是1.5.9,官方手冊 https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/pdf/spring-boot-reference.pdf
spring boot相對於原來來講,有一個較大的區別就是極大的弱化了spring配置文件的重要性,幾乎全部的配置官方文檔中都使用註解,這對於一直以來使用配置文件爲主的同窗來講,須要去適應的地方(注:對於有過框架開發經驗來講,彷佛特別偏好使用註解來進行各類設置,筆者也同樣,其實不是由於配置自己很差用,主要是爲了儘量避免大部分開發其實不care各類配置的精確性),spring框架的主要配置類註解可參考 https://docs.spring.io/spring/docs/4.3.18.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#beans-java,其中最經常使用的是@Configuration類註解,@Bean方法註解,和@ComponentScan類註解。
spring boot應該來講一方面是對標tomcat外置容器的war啓動方式(固然它自己也支持外置容器),另外一方面是經過約定俗成來簡化配置。對於不提供http服務的java應用來講,java service wrapper(https://wrapper.tanukisoftware.com/doc/english/download.jsp)也提供了使用java -jar啓動應用的方式,spring boot也推薦這種方式。
對於使用spring boot,官方推薦設置maven工程的parent爲spring-boot-starter-parent,以下:
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>

打開spring-boot-starter-parent的pom.xml文件,能夠發現spring-boot-starter-parent提供了一些maven的默認設置,好比build中配置文件的路徑,在dependency-management節點中設置了不少spring自身庫以及外部三方庫的版本等,這樣咱們引入依賴的時候就不須要設置版本信息了,spring-boot-starter-parent應該來講是總體spring-boot的骨架管理者,各具體的starter則是特定類型應用的骨架,好比spring-boot-starter-web是web應用的骨架。html

要開發web應用,還須要引入spring-boot-starter-web依賴便可。以下:
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
查看maven依賴樹,能夠發現spring-boot-starter-web實際上引入了不少咱們在開發spring mvc應用時的依賴包,以及嵌入式的tomcat。
注:若是咱們想知道完整的spring-boot-starter-*,能夠從https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#using-boot-starter和https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-starters查詢到。
 
=============================
@EnableAutoConfiguration註解是spring boot引入的最主要註解,其完整類名是org.springframework.boot.autoconfigure.EnableAutoConfiguration。其含義是告訴Spring Boot,根據已知信息包括jar依賴判斷下用戶但願如何配置spring,它一般置於應用的啓動類(也就是帶main函數的類,全部的java應用都是由main類啓動,tomcat也同樣)上,以下所示:
package com.yidoo.springboot.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan @EnableAutoConfiguration public class Application { public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } }

 

同時它也定義了默認的自動掃描根目錄。由於spring-boot-starter-web增長了tomcat和spring mvc,因此天然而然就認爲是web應用了,其實現原理其實就是根據有沒有引入特定jar來判斷。
不須要配置web.xml,也不須要配置spring-mvc.xml、spring-context.xml,就能夠啓動運行了。
運行 maven spring-boot:run就能夠啓動spring boot應用了,在eclipse下,能夠maven build ...輸入,spring-boot:run,以下:

 

這樣maven就會開始打包,並啓動,以下:
.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.9.RELEASE)

2018-06-12 14:00:18.782  INFO 17268 --- [           main] Example                                  : Starting Example on TF017564 with PID 17268 (D:\eclipse\workspace\spring-boot-example\target\classes started by TF017564 in D:\eclipse\workspace\spring-boot-example)
2018-06-12 14:00:18.786  INFO 17268 --- [           main] Example                                  : No active profile set, falling back to default profiles: default
2018-06-12 14:00:19.052  INFO 17268 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5455de9: startup date [Tue Jun 12 14:00:19 CST 2018]; root of context hierarchy
2018-06-12 14:00:21.201  INFO 17268 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2018-06-12 14:00:21.220  INFO 17268 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2018-06-12 14:00:21.221  INFO 17268 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.23
2018-06-12 14:00:21.398  INFO 17268 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-06-12 14:00:21.399  INFO 17268 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2348 ms
2018-06-12 14:00:21.661  INFO 17268 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2018-06-12 14:00:21.686  INFO 17268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-06-12 14:00:21.688  INFO 17268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-06-12 14:00:21.690  INFO 17268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-06-12 14:00:21.691  INFO 17268 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2018-06-12 14:00:22.331  INFO 17268 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5455de9: startup date [Tue Jun 12 14:00:19 CST 2018]; root of context hierarchy
2018-06-12 14:00:22.466  INFO 17268 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto java.lang.String Example.home()
2018-06-12 14:00:22.476  INFO 17268 --- [           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.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-06-12 14:00:22.478  INFO 17268 --- [           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.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-06-12 14:00:22.529  INFO 17268 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-12 14:00:22.529  INFO 17268 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-12 14:00:22.607  INFO 17268 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-12 14:00:22.829  INFO 17268 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-06-12 14:00:22.928  INFO 17268 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-06-12 14:00:22.936  INFO 17268 --- [           main] Example                                  : Started Example in 4.606 seconds (JVM running for 36.043)
2018-06-12 14:00:46.142  INFO 17268 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-06-12 14:00:46.142  INFO 17268 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2018-06-12 14:00:46.166  INFO 17268 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 24 ms
學習spring boot原理,查看啓動日誌是必要的,能夠發現,整體和咱們原來的日誌相似,但也增長了一些額外的日誌,後續筆者會從源碼講解spring boot的原理。
既然@ EnableAutoConfiguration是spring boot的核心,咱們就須要瞭解下,spring boot爲那些應用提供了auto-config,https://docs.spring.io/spring-boot/docs/current/reference/html/auto-configuration-classes.html包含了完整的自動配置列表。自動配置的類是由spring-boot-autoconfigure模塊管理的,而spring-boot-autoconfigure模塊是由spring-boot-starter(它是Spring Boot的核心starter,包含了對自動配置、日誌、spring框架核心以及yaml的依賴)引入的。技術全部的其餘spring boot starter都依賴於spring-boot-starter,spring cloud也採用相似的組織方式。以下:

 

 
至關於原來的各類繁瑣,spring-boot確實簡化了開發過程。
雖然能夠直接運行了,可是一般咱們須要部署到其餘環境,因此仍是須要打個可執行的包出來。原來的作法一般是,咱們只是打war,依賴於目標服務器已經安裝的tomcat等容器,使用spring boot,咱們能夠打出一個徹底自我包含的可執行jar,只要目標環境安裝了JRE便可。可執行jar一般指的是包含了全部依賴的jar的jar包,好比dubbo就能夠認爲是自我包含的。要建立可執行的jar,須要在pom中增長spring-boot-maven-plugin插件,以下:
<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
如今,咱們就能夠進行maven打包了。打出來的有兩個包:myproject-0.0.1-SNAPSHOT.jar和myproject-0.0.1-SNAPSHOT.jar.original。一個是合併了依賴的,一個沒有合併依賴。
注意:打開合併了依賴的jar,咱們能夠發現它和dubbo打包不一樣,不是純粹的從依賴jar中取出class合併到一個jar,而是採用了本身的一套規則,具體可參考官方文檔11.5 Creating an executable jar一節。
在cmd中執行java -jar myproject-0.0.1-SNAPSHOT.jar就能夠啓動了,以下:
.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.9.RELEASE)
2018-06-12 14:21:40.510 INFO 18208 --- [ main] Example : Starting Example on TF017564 with PID 18208 (D:\eclipse\workspace\spring-boot-example\target\myproject-0.0.1-SNAPSHOT.jar started by TF017564 in D:\eclipse\workspace\spring-boot-example\target)
2018-06-12 14:21:40.518 INFO 18208 --- [ main] Example : No active profile set, falling back to default profiles: default
2018-06-12 14:21:40.636 INFO 18208 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@69663380: startup date [Tue Jun 12 14:21:40 CST 2018]; root of context hierarchy
2018-06-12 14:21:43.156 INFO 18208 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2018-06-12 14:21:43.190 INFO 18208 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2018-06-12 14:21:43.195 INFO 18208 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.23
2018-06-12 14:21:43.407 INFO 18208 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2018-06-12 14:21:43.407 INFO 18208 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2776 ms
2018-06-12 14:21:43.659 INFO 18208 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2018-06-12 14:21:43.670 INFO 18208 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-06-12 14:21:43.673 INFO 18208 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-06-12 14:21:43.674 INFO 18208 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-06-12 14:21:43.674 INFO 18208 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2018-06-12 14:21:44.175 INFO 18208 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@69663380: startup date [Tue Jun 12 14:21:40 CST 2018]; root of context hierarchy
2018-06-12 14:21:44.343 INFO 18208 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto java.lang.String Example.home()
2018-06-12 14:21:44.350 INFO 18208 --- [ 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.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-06-12 14:21:44.351 INFO 18208 --- [ 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.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-06-12 14:21:44.412 INFO 18208 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-12 14:21:44.413 INFO 18208 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-12 14:21:44.479 INFO 18208 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-06-12 14:21:44.757 INFO 18208 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-06-12 14:21:44.887 INFO 18208 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-06-12 14:21:44.897 INFO 18208 --- [ main] Example : Started Example in 5.143 seconds (JVM running for 6.129)
從上可知,基本相同。
如今基於spring boot的應用跑起來了,接下去就要開始看咱們原來開發中的那些配置、參數、tomcat端口等如何設置。
 
spring boot自己對於代碼結構沒有要求,不過通常來講,應該將main應用類放在根package,好比com.yidoo.k3c。具體的業務代碼在下一級的package以下:

 

而後在main應用類Application上放置@EnableAutoConfiguration註解,這樣,其實就定義了自動組件掃碼時的默認根目錄,@ComponentScan註解的時候就不須要聲明basePackage屬性,它會自動掃描main應用類所在的package以及子package,主應用類還應該聲明@Configuration,這是推薦的作法。
不過因爲可能會有不少配置,因此配置類極可能會有多個,能夠經過@Import註解導入其餘配置類(這不是spring boot的特性)。
package com.yidoo.springboot.example.web; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.yidoo.springboot.example.service.ExampleService; @RestController public class ExampleWeb { @Autowired private ExampleService exampleSerivce; @RequestMapping("/") String home() { return exampleSerivce.get(); } }
package com.yidoo.springboot.example.service; import org.springframework.stereotype.Service; @Service public class ExampleService { public String get() { return "Hello World"; } }

能夠發現,從應用層面來講,和原來開發基本無異,基本上就是引導類由tomcat變成了咱們定義的。啓動後,經過localhost:8080能夠返回hello world。java

Spring Boot的自動配置特性是經過在主應用類上增長@EnableAutoConfiguration或@SpringBootApplication註解(它是個快捷方式)啓用的,須要注意的,只應該定義一個主應用類,並加上@EnableAutoConfiguration註解,其餘輔助配置類不要加上@EnableAutoConfiguration註解。
有些時候,咱們並不但願使用starter定義,或者以爲spring自動配置的類不是咱們想要的,能夠經過啓動應用時帶上--debug標誌查看緣由(這和執行計劃性質相似),也能夠在@EnableAutoConfiguration註解上聲明exclude屬性排除某些配置類或者具體類,例如@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})(更多可參考16.2 Disabling specific auto-configuration)。
咱們幾乎老是在主應用類上同時聲明@Configuration, @EnableAutoConfiguration和@ComponentScan註解,由於他們基本上是一塊兒使用的,因此spring提供了一個快捷方式@SpringBootApplication,它至關於同時聲明@Configuration, @EnableAutoConfiguration和@ComponentScan。
SpringApplication是用來啓動應用的類,其構造器參數是spring bean的配置源,大多數狀況下指向@Configuration類。默認狀況下,啓動過程當中會執行下列操做:
  • 建立合適的ApplicationContext實例;
  • 註冊CommandLinePropertySource實例,將命令行參數暴露爲Spring屬性;
  • 刷新application context,加載全部單例;
  • 觸發全部 CommandLineRunner實例;
同ApplicationContext中各類生命週期事件同樣,由於ApplicationContext運行在SpringApplication中,因此SpringApplication相似的提供了不少生命週期事件。SpringApplication能夠說是Spring Boot運行時的核心主控類,應該好好看看其實現,javadoc地址爲 https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/api/org/springframework/boot/SpringApplication.html
 

配置文件

同war包同樣,因爲各運行環境不一樣,咱們會有不少不一樣的配置項須要自定義,好比jdbc鏈接、redis鏈接、註冊中心等等。當咱們打出可執行的jar後,須要一種方式來設置某些參數值,Spring Boot提供了多種配置文件格式(這一點改進應該來講至關的不錯)來設置這些配置值,支持文本的properties配置文件、YAML(elastic出品的ELK使用的就是YAML配置文件)、JSON、環境變量、命令行參數。這些屬性能夠經過@Value註解、Spring Environment( https://docs.spring.io/spring/docs/4.3.18.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#beans-environment)抽象、或@ConfigurationProperties綁定到對象的方式訪問(主要是爲了確保配置值類型安全, https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#boot-features-external-config-typesafe-configuration-properties)。spring Boot加載配置的優先級從高到低(完整參考24. Externalized Configuration)經常使用的主要爲:
  1. Devtools配置文件中的值
  2. 命令行(默認狀況下,SpringApplication 會將命令行參數轉換爲property ,同時添加到Environment)
  3. ServletConfig初始化參數
  4. ServletContext初始化參數
  5. Java系統屬性
  6. 環境變量
  7. RandomValuePropertySource(主要用來生成隨機值,random.*格式,適合用來生成隨機值,參考24.1 Configuring random values)
  8. jar包外的application-{profile}.properties
  9. jar包內的application-{profile}.properties
  10. jar包外的application.properties
  11. jar包內的application.properties
  12.  @Configuration 類上的@PropertySource註解
  13. SpringApplication.setDefaultProperties聲明的默認屬性
對於jar包外的properties,其搜索順序爲:
  1. 當前運行目錄的/config子目錄
  2. 當前目錄
  3. classpath的/config中
  4. classpath
有些時候,咱們會有多個配置文件,好比jdbc的、mq的、dubbo的,此時能夠經過聲明spring.config.location明確指定配置文件,以下:
java -jar myproject.jar --spring.config.location=classpath:/override.properties,file:/v.properties
spring.config.location搜索的順序爲從最後開始。
profile相關的配置文件搜索順序同普通application.properties。
配置文件中可使用先前定義過的配置參數,由於Environment會進行回溯。因此能夠像下面這樣使用:
app.name=MyApp app.description=${app.name} is a Spring Boot application
 
在之前,咱們要爲某些類好比Node/Plugin配置屬性的時候,須要使用@Value("${property}")註解一個個的注入,有時候配置屬性有幾十個,並且層次嵌套,在spring boot中,不須要這麼作了,spring boot提供了@ConfigurationProperties(實際上這和spring boot毫無關係,純粹故意的)註解能夠自動主動全部相關屬性,好比:
@ConfigurationProperties("foo") public class FooProperties { private boolean enabled; private InetAddress remoteAddress; private final Security security = new Security(); public static class Security { private String username; private String password; } ...... }
能夠自動匹配下列屬性:
  • foo.enabled, 默認false
  • foo.remote-address, 只要可從String轉換過來
  • foo.security.username
  • foo.security.password
  • foo.security.roles, String集合
其實spring boot新增的不少特性跟spring boot自己沒有什麼關係,可是都增長到org.springframework.boot包中了,其實應該放在core或者context或者context-support中更加合理的,只能說是有意爲之。
 
爲了讓spring知道哪些類須要自動注入配置,須要在配置類上聲明@EnableConfigurationProperties註解,列出具體的類,以下:
@Configuration @EnableConfigurationProperties(FooProperties.class) public class MyConfiguration { }
這樣,FooProperties類就和常規的bean同樣,能夠經過@Autowired注入使用了。
 
@ConfigurationProperties類還能夠結合Spring的@Validated註解,若是配置類上標記了Validated註解,具體屬性上就可使用JSR-303 中定義的註解,Spring boot會自動驗證(這個特性也同樣,跟spring boot毫無關係)。例如:
@ConfigurationProperties(prefix="foo") @Validated public class FooProperties { @NotNull private InetAddress remoteAddress; // ... getters and setters
 }

對於嵌套屬性,要驗證的話,直接屬性值必須標記上@Valid註解以便觸發校驗,例如:mysql

@ConfigurationProperties(prefix="connection") @Validated public class FooProperties { @NotNull private InetAddress remoteAddress; @Valid private final Security security = new Security(); // ... getters and setters

    public static class Security { @NotEmpty public String username; // ... getters and setters
 } }

日誌

spring boot日誌配置 參考https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html#howto-configure-logback-for-logging
Spring Boot有一個LoggingSystem抽象,他會根據classpath中能夠找到的日誌實現選擇可用的,若是Logback可用,它會優先選擇。
若是隻是但願爲各類logger設置級別,只要在application.properties中增長logging.level開頭的配置便可,以下:
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR
要設置文件的位置,增長logging.file開頭的配置
若是要更細粒度的配置,則須要使用LoggingSystem的原生配置格式,對於logback,Spring Boot會加載classpath:logback.xml,具體路徑搜索順序參考spring boot學習筆記。
原則上,不該該在application.properties中設置日誌配置
spring boot提供了一些logback模板,能夠參考或者適當修改,logback官方文檔參考https://logback.qos.ch/documentation.html。git

若是Log4j 2在classpath上,Spring Boot也支持(注:spring boot不支持1.2.x),若是使用了各類starter組裝依賴,則須要排除掉Logback,不然啓動的時候會報衝突。若是沒有使用starter,則須要額外引入spring-jcl依賴。
配置log4j最簡單的方法就是使用spring-boot-starter-log4j2,以下:github

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

爲了確保java.util.logging執行的debug會被Log4j 2記錄,須要配置系統屬性java.util.logging.manager爲org.apache.logging.log4j.jul.LogManager。
log4j 2手冊能夠參考http://logging.apache.org/log4j/2.x/log4j-users-guide.pdfweb

注:通常來講,使用logback或者log4j2其實關係並不大的,但實際上,對於高負載、複雜邏輯的系統,咱們會發現一個業務服務上最終響應時間上rpc調用次數、日誌、序列化佔據了挺大的比例。redis

對於spring boot下配置log4j 2,並支持MDC(咱們都提到了跨界點日誌上下文關聯的重要性,參考寫給大忙人的CentOS 7下最新版(6.2.4)ELK+Filebeat+Log4j日誌集成環境搭建完整指南一文),官方並無文檔說明,網上也沒有直接說起,雖然如此,鑑於上一段所述緣由,筆者仍是研究了怎麼樣才能讓spring boot使用log4j2又支持MDC(參考寫給大忙人的spring cloud 1.x學習指南一文)。spring

application.yml中增長以下:
logging:
  config: classpath:log4j2.xml
log4j2.xml配置以下:sql

<Properties>
        <Property name="pattern">%d{yyyy-MM-dd HH:mm:ss,SSS} [%X{X-B3-TraceId},%X{X-B3-SpanId},%X{X-B3-ParentSpanId},%X{X-Span-Export}] %5p %c{1}:%L - %m%n</Property>
    </Properties>
輸出爲[3bfdd6f72352ef7e,3bfdd6f72352ef7e,,false]

或者:apache

<Properties>
    <Property name="pattern">%d{yyyy-MM-dd HH:mm:ss,SSS} %X %5p %c{1}:%L - %m%n</Property>
</Properties>

輸出爲{X-B3-SpanId=3bfdd6f72352ef7e, X-B3-TraceId=3bfdd6f72352ef7e, X-Span-Export=false}

除了這兩種自帶格式外,還能夠自定義,例如在HandlerInterceptor接口的preHandle方法中設置上下文以下:

 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String path = request.getContextPath().length() > 1 ? request.getRequestURI().replace(request.getContextPath(), "") : request.getRequestURI(); String sessionId = getSessionCookie(request); if (StringUtils.isEmpty(sessionId)) { logger.warn("請求" + path + "的sessionId爲空!"); response.sendRedirect(appWebHomeUrl + "/logout.html"); return false; } try { String session = redisUtils.get(REDIS_SESSION_ID_PREFIX + sessionId).toString(); String traceId = sessionId.substring(0, 8) + "_" + path + "_" + formatter.format(new Date()); // 設置log4j2 mdc
 ThreadContext.push(traceId); return true; } catch (NullPointerException e) { logger.warn("請求" + path + "的sessionId不存在或已失效!"); response.sendRedirect(appWebHomeUrl + "/logout.html"); return false; } }

同理,在postHandle清除,以下:

 @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object handler, ModelAndView arg3) throws Exception { ThreadContext.clearAll(); }

log4j2.xml Pattern配置以下:

<Pattern>%x %d{yyyy-MM-dd HH:mm:ss} [%r] [%c{1.}]-[%p] %t %l %m%n</Pattern>

輸出格式爲:

[c327093e_/filesend_21:31:39] 2018-11-25 21:31:39 [138805] [c.h.t.a.d.c.CheckItemController]-[DEBUG] http-nio-8086-exec-8 {"operatorId":null,"memberId":null,"memberName":null,"branchId":null,"branchIds":null,"mobile":null,"operatorName":null,"realName":null,"email":null,"sessionKey":null,"sessionId":null,"traceId":"c327093e_/filesend_21:31:39"}

相比默認格式的可讀性要好得多,若是使用了rpc好比dubbo或者其它,能夠經過filter進行透傳。

相關參考文檔

http://logging.apache.org/log4j/2.x/manual/thread-context.html
https://github.com/spring-cloud/spring-cloud-sleuth
https://zipkin.io/pages/instrumenting.html
https://github.com/openzipkin/b3-propagation
http://ryanjbaxter.com/cloud/spring%20cloud/spring/2016/07/07/spring-cloud-sleuth.html
https://github.com/spring-cloud/spring-cloud-sleuth/issues/162

若是繼承了ELK的話,logstash日誌解析到字段配置以下:
filter { # pattern matching logback pattern grok { match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" } } }

 

MVC web應用

若是但願使用Spring Boot的MVC特性,僅僅增長額外的配置,好比攔截器等,能夠建立WebMvcConfigurerAdapter類型的@Configuration,可是去掉@EnableWebMvc(參見spring framework官方文檔22.16.1 Enabling the MVC Java Config or the MVC XML Namespace)註解。若是但願自定義RequestMappingHandlerMapping, RequestMappingHandlerAdapter,ExceptionHandlerExceptionResolver,能夠定義一個WebMvcRegistrationsAdapter類提供上述組件。若是要徹底控制Spring MVC,則在@Configuration類上加上@EnableWebMvc註解。
Spring MVC使用HttpMessageConverter接口轉換HTTP請求和應答,spring boot包含了開箱即用的合理默認值,好比對象自動使用jackson轉換爲JSON,字符串使用UTF-8編碼。
有些時候咱們須要自定義轉換器,好比對於JSON類型,一般忽略不存在的參數,能夠以下配置:
@Configuration public class MyConfiguration { @Bean public HttpMessageConverters customConverters() { HttpMessageConverter<?> additional = ... HttpMessageConverter<?> another = ... return new HttpMessageConverters(additional, another); } }

CORS(https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)支持

spring 4.2開始,Spring MVC對CORS開箱即用的支持,能夠經過在控制器方法或者類上標記  @CrossOrigin 註解便可,也能夠經過全局性的在WebMvcConfigurer 中註冊,以下所示:
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://domain2.com") .allowedMethods("PUT", "DELETE") .allowedHeaders("header1", "header2", "header3") .exposedHeaders("header1", "header2") .allowCredentials(false).maxAge(3600); } }

嵌入式容器的配置

 
Spring Boot支持嵌入式的Tomcat, Jetty, 以及Undertow,默認狀況下8080端口。
當使用嵌入式容器時,全部servlet規範中的組件Servlets, Filters、listeners等均可以經過spring bean的方式註冊,這樣就能夠直接使用application.properties以及各類依賴bean,相對於原來方便了很多。
除了做爲bean註冊外,還能夠經過使用@ServletComponentScan註解自動註冊標記了@WebServlet, @WebFilter, @WebListener註解的類。
注:@ServletComponentScan不能用於標準容器。
 
嵌入式Web應用上下文EmbeddedWebApplicationContext
對於嵌入式容器,spring boot使用了一種新的ApplicationContext類型,EmbeddedWebApplicationContext 是一個特殊的WebApplicationContext ,它會在啓動時尋找一個EmbeddedServletContainerFactory bean,並使用它啓動本身。一般TomcatEmbeddedServletContainerFactory, JettyEmbeddedServletContainerFactory, 或UndertowEmbeddedServletContainerFactory會自動配置。

自定義容器配置

某些servlet容器配置能夠經過Spring Environment屬性配置,用戶能夠定義在application.properties中。主要包括:
  • server.port
  • server.address
  • server.session.timeout

JDBC等配置

 spring boot經過spring.datasource.*配置暴露DataSource屬性,以下:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
對於持久化部分,由於咱們使用的是druid,因此不須要,即便是使用dbcp2和mysql,咱們也在鏈接上設置了某些特殊的配置以使得系統更加穩定和高性能,若是自動包含了的話,能夠排除掉相應的starter配置。redis、mq等雷同。
=======================
因此,整體來講,spring boot自己是很簡單的,它的目的主要應該是提供另一種更加self-contain的部署方式、同時從第三方公正機構的角度規範了開發,這一點其實很是重要,至於說開發效率自己上,倒不見得會提升。
 
最後,很重要的是,前面咱們看到application.yml有各類配置,那咱們怎麼知道到底有哪些配置呢?https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html列出了完整的配置屬性清單。
 

相關問題

SpringBoot 文件上傳臨時文件路徑被自動清空。
解決方法:
在啓動的額環境變量裏面添加參數:-Djava.io.tmpdir = /xxx
或:
在代碼中增長系統默認目錄配置 ,以下: @Bean MultipartConfigElement multipartConfigElement() { MultipartConfigFactory factory = new MultipartConfigFactory(); factory.setLocation("/app/tmp"); return factory.createMultipartConfig(); }

 在windows中,若是指向c:\目錄,可能會提示無權限,須要注意。

指定應用的context-path

在application.properties文件中添加以下內容:

# 若是無需修改默認端口,此配置可不要
server.port=8080
# 配置次路徑後,全部的資源訪問路徑都會加上/app前綴
server.context-path=/app
須要注意的的,配置了server.context-path路徑,全部的資源,請注意,包括靜態資源,訪問地址都會加上/app前綴。

在啓動JVM時,添加以下啓動參數:

-Dserver.context-path=/app

@Component public class CustomContainer implements EmbeddedServletContainerCustomizer { @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.setContextPath("/app"); } }

Spring Boot默認只有一個Servlet,默認會映射到根路徑/,沒法像配置DispatcherServlet的方式只將@Controller的路徑指向到上下文地址。

注意:在spring boot 2.x,參數發生了變動。

weblogic集成

參考:

https://blog.csdn.net/MT_xiaoshutong/article/details/54019993

https://segmentfault.com/a/1190000015721951

條件化注入

Springboot中提供了不少條件化配置的註解,只要輸入@ConditionalOn就能出現一大堆。不過比較經常使用的也就幾種:

/******************* * Class包含Bean * ******************/

// 容器中有ThreadPoolTaskExecutor類型的bean時才注入
@ConditionalOnBean(ThreadPoolTaskExecutor.class) @ConditionalOnMissingBean(ThreadPoolTaskExecutor.class) // 類路徑中有ThreadPoolTaskExecutor類型的bean時才注入
@ConditionalOnClass(ThreadPoolTaskExecutor.class) @ConditionalOnMissingClass // 在配置文件中查找hello.name的值,若是能找到而且值等於yan,就注入,若是根本就沒配,也注入,這就是matchIfMissing = true的含義
@ConditionalOnProperty(prefix = "hello", name = "name", havingValue = "yan", matchIfMissing = true) //只在web環境下注入
@ConditionalOnWebApplication // java8或以上環境才注入
@ConditionalOnJava(ConditionalOnJava.JavaVersion.EIGHT)

 

問題描述:spring boot使用maven的package命令打出來的包,卻不包含依賴的jar包

問題緣由:打包時使用了maven默認的maven-jar-plugin插件,而不是spring-boot-maven-plugin插件

解決方法:pom中必須配置spring-boot-maven-plugin插件,並且必須指定須要執行的目標構建

<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
<!-- 下面可選 -->
<executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>

問題: spring boot沒有打包本地第三方庫,如src/lib下的oracle jdbc。

解決方法:在build標籤下增長下列配置。

<resources>
        <resource>
          <directory>src/lib</directory>
          <targetPath>BOOT-INF/lib/</targetPath>
          <includes>
            <include>**/*.jar</include>
          </includes>
        </resource>
</resources>

 spring servlet、listener、context param、error-page、index-page、session-timeout配置:

 啓動到一半終止,沒有日誌,以下:

2019-02-19 09:27:46,343 main DEBUG Reconfiguration complete for context[name=18b4aac2] at URI E:\恆生TA\TA-BASE\trunk\Sources\stage-source\Sources\ta-base\ta-base-webapp\target\classes\log4j2.xml (org.apache.logging.log4j.core.LoggerContext@3a80515c) with optional ClassLoader: null . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot :: (v1.5.8.RELEASE)
 [] 2019-02-19 09:27:47 [2662] [o.j.logging]-[DEBUG] background-preinit org.hibernate.validator.internal.util.logging.LoggerFactory.make(LoggerFactory.java:19) Logging Provider: org.jboss.logging.Log4j2LoggerProvider 2019-02-19 09:27:47,107 background-preinit DEBUG AsyncLogger.ThreadNameStrategy=CACHED [] 2019-02-19 09:27:47 [2670] [o.h.v.i.u.Version]-[INFO] background-preinit org.hibernate.validator.internal.util.Version.<clinit>(Version.java:30) HV000001: Hibernate Validator 5.3.5.Final [] 2019-02-19 09:27:47 [2698] [o.h.v.i.e.r.DefaultTraversableResolver]-[DEBUG] background-preinit org.hibernate.validator.internal.engine.resolver.DefaultTraversableResolver.detectJPA(DefaultTraversableResolver.java:80) Found javax.persistence.Persistence on classpath, but no method 'getPersistenceUtil'. Assuming JPA 1 environment. All properties will per default be traversable.

日誌框架配置不正確到時有些信息沒有顯示,改爲debug啓動可能就報錯了。如:

log4j棧溢出:

java.lang.StackOverflowError: null
at org.slf4j.impl.JDK14LoggerAdapter.fillCallerData(JDK14LoggerAdapter.java:595) ~[slf4j-jdk14-1.7.25.jar:1.7.25]
at org.slf4j.impl.JDK14LoggerAdapter.log(JDK14LoggerAdapter.java:581) ~[slf4j-jdk14-1.7.25.jar:1.7.25]
at org.slf4j.impl.JDK14LoggerAdapter.log(JDK14LoggerAdapter.java:632) ~[slf4j-jdk14-1.7.25.jar:1.7.25]
at org.slf4j.bridge.SLF4JBridgeHandler.callLocationAwareLogger(SLF4JBridgeHandler.java:221) ~[jul-to-slf4j-1.7.25.jar:1.7.25]
at org.slf4j.bridge.SLF4JBridgeHandler.publish(SLF4JBridgeHandler.java:303) ~[jul-to-slf4j-1.7.25.jar:1.7.25]
at java.util.logging.Logger.log(Logger.java:738) ~[?:1.8.0_171]
at org.slf4j.impl.JDK14LoggerAdapter.log(JDK14LoggerAdapter.java:582) ~[slf4j-jdk14-1.7.25.jar:1.7.25]

解決方法,去掉jul依賴,以下:

 

 spring boot將外部路徑添加到classpath

默認狀況下,spring boot不會將可執行jar以外的目錄做爲classpath的一部分,經過-classpath指定也不起做用。要使用該功能,須要使用spring boot的PropertiesLauncher特性,也就是使用zip佈局,以下所示:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <layout>ZIP</layout><!-- enables PropertiesLauncher -->
            </configuration>
        </plugin>
    </plugins>
</build>

而後經過-Dloader.path=/your/folder/containing/password/file/指定做爲classpath的目錄。

spring boot maven plugin全部配置:https://docs.spring.io/spring-boot/docs/current/maven-plugin/repackage-mojo.html

注意其中的executable不能爲true,不然沒法修改。

爲了知足監控方便,社區開發了spring boot admin,至關於spring boot版的jvisualvm相似了,1.x和2.x版本都支持,參見https://github.com/codecentric/spring-boot-admin。

參考:

https://stackoverflow.com/questions/46728122/add-an-external-xml-file-containing-passwords-to-class-path-in-spring-boot

https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html

相關文章
相關標籤/搜索