spring-boot-starter大力出奇跡

1、前言

​  上篇文章咱們已經聊了SpringBoot的啓動過程當中的各種擴展點,那麼從http://start.spring.io上咱們生成的demo項目中,到目前就剩下了maven工程的pom.xml尚未進行探索了,那麼本文咱們就來看看這裏面到底都有啥,把大力出奇跡的常見spring-boot-starter來聊一聊,以便更好地使用SpringBoot.css

2、SpringBoot項目的pom.xml文件解析

​   首先,咱們仍是按照一向的做風,先上源碼,再解析:html

<?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
http://maven.apache.org/xsd/maven-4.0.0.xsd"
>

<modelVersion>4.0.0</modelVersion>

<groupId>com.hafiz</groupId>
<artifactId>springboot-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>springboot-demo</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.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>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

​  咱們能夠看到全部的結構都是咱們熟悉的。首先映入眼簾的是:<parent></parent>標籤,熟悉Maven的朋友都知道,這個標籤用來定義要繼承的父pom的信息,它用來定義SpringBoot項目可能用到的依賴和插件聲明以及一些資源文件聲明,這樣咱們就能夠在本身的SpringBoot項目中用到這些依賴或者插件的時候直接飲用,而不用指定版本號,正如咱們上面看到的spring-boot-starter-webspring-boot-starter-test依賴以及spring-boot-maven-plugin插件同樣,父pom.xml的源碼以下:java

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
<artifactId>spring-boot-starter-parent</artifactId>
<packaging>pom</packaging>
<name>Spring Boot Starter Parent</name>
<description>Parent pom providing dependency and plugin management for applications
built with Maven</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<java.version>1.6</java.version>
<resource.delimiter>@</resource.delimiter> <!-- delimiter that doesn't clash with Spring ${} placeholders -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<!-- Turn on filtering by default for application properties -->
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
<excludes>
<exclude>**/application*.yml</exclude>
<exclude>**/application*.yaml</exclude>
<exclude>**/application*.properties</exclude>
</excludes>
</resource>
</resources>
<pluginManagement>
<plugins>
<!-- Apply more sensible defaults for user projects -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>${start-class}</mainClass>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests.java</include>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<archive>
<manifest>
<mainClass>${start-class}</mainClass>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>${start-class}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<delimiters>
<delimiter>${resource.delimiter}</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<verbose>true</verbose>
<dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
</configuration>
</plugin>
<!-- Support our own plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>${start-class}</mainClass>
</configuration>
</plugin>
<!-- Support shade packaging (if the user does not want to use our plugin) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.13.RELEASE</version>
</dependency>
</dependencies>
<configuration>
<keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factories</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${start-class}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

  可是咱們發現,該父pom文件還有本身的父pom,做用相似,再也不詳解,感興趣的能夠本身去翻閱源碼。那麼這樣看來咱們本身的SpringBoot項目中的pom.xml文件就剩下顯眼的兩個依賴以及一個插件了(沒有指定版本號的緣由前面已經解釋了),那咱們接下來就來聊一聊這些很是重要的spring-boot-starter依賴。mysql

3、不可或缺的spring-boot-starter

​  咱們從前面就知道了,SpringBoot可以如此方便便捷,其實都是得益於這些「開箱即用」的依賴模塊,那SpringBoot設計者約定這些「開箱即用」的依賴模塊的命名都以spring-boot-starter-開始,而且這些模塊都位於org.springframework.boot包或者命名空間下面。咱們也能夠模仿者來實現本身的自動配置依賴模塊,也已spring-boot-starter-開頭,是否是就很"正宗"呢?(雖然SpringBoot官方不建議咱們這樣作,以避免跟官方提供的混淆,可是其實咱們使用本身的groupId,這樣命名應該不是啥問題)。git

​  這些starter其實都有約定好的默認配置,可是它也容許咱們調整這些默認配置,以便完成定製化的需求,咱們能夠改變默認配置的常見方式有如下幾種:web

  • 命令行參數(Command Line Args)
  • 系統環境變量(Environment Variables)
  • 位於文件系統中的配置文件
  • 位於classpath中的配置文件
  • 固化到代碼中的配置項

  這幾種方式從上到下優先級從高到低排列,高優先級的配置會覆蓋優先級低的配置。還有就是無論位於文件系統仍是classpath中的配置文件,SpringBoot應用默認的文件名稱都是application.properties,能夠放在當前項目的根目錄下或者名稱爲config的子目錄下。spring

​  SpringBoot其實提供了不少這樣的模塊,咱們就挑幾個咱們經常使用的這樣的模塊來解析,其餘的你們就觸類旁通。以達到在工做和開發中靈活運用這些spring-boot-starter模塊的效果。sql

1. spring-boot-starter-logging以及應用日誌

 若是咱們在maven依賴中添加了spring-boot-starter-logging:mongodb

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

  那也就意味着咱們的SpringBoot應用自動使用logback做爲日誌框架,在啓動的時候,由org.springframework.boot.logging.LoggingApplicationListener根據狀況初始化並使用。默認狀況下,SpringBoot已經給咱們提供好了不少默認的日誌配置,咱們只須要將spring-boot-starter-logging做爲依賴加入到你的SpringBoot應用就能夠了,可是若是咱們要對這些默認配置進行定製,能夠有兩種方式進行:數據庫

  • 遵照logback的約定,在classpath中使用定製化的logback.xml配置文件。

  • 在文件系統中任意一個地方提供本身的logback.xml配置文件,而後經過以下配置來application.properties中指定咱們日誌系統配置文件位置:

    logging.config=/{your config file location}}/logback.xml

若是咱們已經習慣了log4j或log4j2,那咱們只須要把spring-boot-starter-logging換成以下的starter就好。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
</dependency>

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

2. 用於快速構建web應用的spring-boot-starter-web

​  現現在,咱們在工做中大部分實際用的仍是SpringMVC開發的web應用,SpringBoot固然貼心的爲咱們開發了一個web項目模塊,讓咱們更加方便的開發web應用。maven依賴以下:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

  這樣咱們就能夠獲得一個能夠直接執行的Web應用,而後咱們運行mvn spring-boot:run,就能直接啓動一個基於嵌入式tomcat容器的Web應用了,而後就能夠像這篇文章中定義controller來供用戶訪問了。可是呢,這簡單的表象之下,其實卻隱藏着不少約定,咱們要把這些潛規則瞭解清楚才能更好地應用spring-boot-starter-web

2.1 項目結構的「潛規則」

​   傳統的Java Web項目中,咱們的靜態文件以及頁面模板都是放在src/main/webapp目錄下,可是在SpringBoot應用中,這些文件被統一放在src/main/resources相應的子目錄下:

  • src/main/resources/static目錄用於存放各類靜態資源,如:js、css、image等。
  • src/main/resources/template目錄用於存放模板文件。

細心地咱們會發現SpringBoot的web應用已經變成了jar包而再是war包,若是咱們仍是但願以war包的形式發佈也是能夠的。

2.2 SpringMVC框架層面的約定及定製

spring-boot-starter-web默認將爲咱們自動配置以下一些SpringMVC必要的組件:

  • ViewResolver,如:ContentNegotiatingViewResolverBeanNameViewResolver
  • Converter,如:GenericConverterFormatter等bean被註冊到IoC容器。
  • 默認添加一系列HttpMessageConverter用於支持對Web請求和相應的類型轉換。
  • 自動配置和註冊MessageCodesResolver
  • 其餘必要組件…
2.3 嵌入式Web容器的約定和定製

​  咱們知道spring-boot-starter-web默認把嵌入式tomcat做爲web容器來對外提供HTTP服務,默認使用8080端口對外監聽和提供服務。這裏咱們可能會有兩個疑問:

  • 咱們不想使用默認的嵌入式tomcat容器怎麼辦?

    很簡單,咱們只須要引入spring-boot-starter-jettyspring-boot-starter-undertow依賴就能替代默認嵌入式tomcat容器了。

  • 咱們想要把啓動後提供服務的端口改掉怎麼辦?

    咱們能夠經過在配置文件中修改啓動端口就能夠了,如:

    server.port=9000

其實,spring-boot-starter-web提供了不少以server.做爲前綴的配置以用來修改嵌入式容器的配置,如:

server.port
server.address
server.ssl.*
server.tomcat.*

那若這些還知足不了你,SpringBoot甚至都容許咱們直接對嵌入式Web容器實例進行定製化,咱們經過向IoC容器中註冊一個EmbeddedServletContainerCustomizer類型的組件來實現:

package com.hafiz.springbootdemo;

import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;

/**
* @author hafiz.zhang
* @description: 自定義內嵌容器配置
* @date Created in 2018/6/10 12:09.
*/

public class DemoEmbeddedTomcatCustomizer implements EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(9111);
container.setContextPath("/demo");
// ...
}
}

若是還要再深刻的定製,那就須要實現對應內嵌容器的Factory並註冊到IoC容器:

  • TomcatEmbeddedServletContainerFactory
  • JettyEmbeddedServletContainerFactory
  • UndertowEmbeddedServletContainerFactory

  可是,咱們幾乎沒有可能須要這樣的定製化,也不建議這樣的定製化,使用SpringBoot默認的spring-boot-starter-web提供的配置項列表已經很簡單、很完整了。

3. 用於數據訪問的spring-boot-starter-jdbc

  咱們知道,現實中大多數的Java應用都須要訪問數據庫,那SpringBoot確定不會放過這個組件,它會很貼心的爲咱們自動配置好相應的數據訪問工具。咱們只須要在pom.xml中添加如下依賴就行了:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

  這樣,在咱們沒有配置任何DataSource的狀況下,SpringBoot會默認爲咱們自動配置一個基於嵌入式數據的DataSource,這種自動配置適合於測試場景,生產環境不適合。大多數狀況下,咱們都會本身配置DataSource實例,或經過自動配置模塊提供的配置參數對DataSource實例配置自定義的參數。

若咱們的SpringBoot應用只依賴一個數據庫,那咱們直接使用自動配置模塊提供的配置參數最方便快捷:

spring.datasource.url=jdbc:mysql://{db host}:{db port}/{db name}
spring.datasource.username={db user name}
spring.datasource.password={db password}

  有的小夥伴說了:那我本身配置一個DataSource行不行?答案是固然能夠,SpringBoot會很智能的優先選擇使用咱們本身配置的這個DataSource,可是感受畫蛇添足!你要知道,SpringBoot除了自動幫咱們配置DataSource之外,還自動幫咱們配置了相應的JdbcTemplate以及DataSourceTransactionManager等相關的組件,咱們只須要在須要使用的地方直接使用@Autowired註解引用就行了。

​  那SpringBoot是否是一直貼心呢?很明顯不是的,若是咱們的單個項目須要依賴和訪問多個數據庫,這個時候就不行了,就算是咱們在ApplicationContext中配置了多個DataSource實例來訪問多個數據庫:

@Bean
public DataSource dataSource1() throws Throwable {
DruidDataSource ds = new DruidDataSource();
ds.setUrl(...);
ds.setUsername(...);
ds.setPassword(...);
// set other db setting
return ds;
}
@Bean
public DataSource dataSource2() throws Throwable {
DruidDataSource ds = new DruidDataSource();
ds.setUrl(...);
ds.setUsername(...);
ds.setPassword(...);
// set other db setting
return ds;
}

啓動項目時,你就會發現以下的異常:

No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2...

那怎麼解決這個問題呢?有兩種方式:

  • 在SpringBoot的啓動類上「動手腳」

    @SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class
    })
    public class DemoSpringBootApplication {
    public static void main(String[] args) {
    SpringApplication.run(DemoSpringBootApplication.class, args);
    }
    }

    這也就是說咱們須要排除掉SpringBoot默認的DataSource的相關的自動配置。

  • 使用@primary註解

    那咱們既要配置兩個數據源,又要使用SpringBoot默認的DataSource,這時咱們就能夠爲咱們配置的兩個DataSource中的任意一個使用@primary註解就能夠了。

    @Bean
    @Primary
    public DataSource dataSource1() throws Throwable {
    DruidDataSource ds = new DruidDataSource();
    ds.setUrl(...);
    ds.setUsername(...);
    ds.setPassword(...);
    // set other db setting
    return ds;
    }
    @Bean
    public DataSource dataSource2() throws Throwable {
    DruidDataSource ds = new DruidDataSource();
    ds.setUrl(...);
    ds.setUsername(...);
    ds.setPassword(...);
    // set other db setting
    return ds;
    }

    除此以外,SpringBoot還提供了不少其餘數據源訪問相關的自動配置模塊,如:spring-boot-starter-jpaspring-boot-starter-mongodb等。

4、總結

  除了本文咱們介紹的經常使用的三個spring-boot-starter之外,SpringBoot還提供了不少別的starter,包括spring-boot-starter-aopspring-boot-starter-securityspring-boot-starter-actuator等等。咱們經過本文觸類旁通,能夠作到用的時候駕輕就熟。棒!給本身一個贊~

相關文章
相關標籤/搜索