編寫不易,轉載請注明(http://shihlei.iteye.com/blog/2407689)!
一 概述
SpringBoot 個人感覺特點:
1)衆多庫的集合(各種Starter),友善快速建構應用系統。
2)自動配置spring(通過AutoConfiguration機制),簡化配置,也友善擴充新的Starter。
3)内嵌web容器,無需WAR部署。
本文自定義一個helloworld-starter 揭示starter的定義過程,及Spring AutoConfiguration 自動配置方法。
注:
SpringBoot AutoConfiguration機制:
SpringBoot啟動時,掃描 classpath 所有Jar中 META-INF/spring.factories檔案,讀取org.springframework.boot.autoconfigure.EnableAutoConfiguration 指定的Configuration,根據Configuration上的Conditional條件自動建立bean,注入容器。
Spring自動配置的Starter及自動配置能力,由如下包提供。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
關于Spring架構@Configuration使用,可參見部落格《Spring:@Configuration 使用》
二 Demo 工程規劃
(1)spring-boot-helloworld-starter項目—— 自定義Starter
實作 HelloWorldTemplate 自動配置,并根據classpath 中是否有FastJson包,決定格式化資訊方式
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuQjY2UWZ2kjY4MDOw0yN5ITYtE2NjNTL1IGO00iYmJzMwETN18CXyczM18CX4ITMw8CX05WZth2YhRHdh9CXkF2bsBXdvwVbvNmLllXZ0lmLywGZvw1LcpDc0RHaiojIsJye.png)
(2)spring-boot-web 項目—— 調用Starter
依賴spring-boot-helloworld-starter項目HelloWorldTemplate,将格式化的資訊傳回用戶端(資訊包括application.yml中的 helloworld.author中資訊,“Hello World”,調用的傳入的對象資訊)
三 spring-boot-helloworld-starter項目
(1)工程:
1)父 pom :
其中:spring-boot-dependencies 提供了spring boot的依賴聲明
<?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>x.demo.springboot.starter</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>module/spring-boot-helloworld-starter</module>
</modules>
<properties>
<java.version>1.8</java.version>
<spring.boot.version>1.5.9.RELEASE</spring.boot.version>
<fastjson.version>1.2.28</fastjson.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring boot 基礎依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2)module:
其中FastJson 使用,<optional>true</optional> ,指定用戶端可以選擇依賴該jar,用于實作如果classpath 中沒有fastjson的jar,則不使用JSON FastJsonOutputFormater
<?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">
<parent>
<artifactId>spring-boot-starter</artifactId>
<groupId>x.demo.springboot.starter</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-helloworld-starter</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<!-- 指定用戶端可以選擇依賴該jar,用于實作如果classpath 中沒有fastjson的jar,則不使用JSON FastJsonOutputFormater-->
<optional>true</optional>
</dependency>
</dependencies>
</project>
(2)主要服務類:HelloWorldTemplate
實作組裝配置檔案中的作者資訊、HelloWorld、使用者傳入的對象資訊
package x.demo.springboot.starter.hw;
import x.demo.springboot.starter.hw.autoconfiguration.HelloWorldProperties;
import x.demo.springboot.starter.hw.outputformater.OutputFormater;
/**
* 配置檔案中的作者資訊
* HelloWorld
* 對象資訊
*/
public class HelloWorldTemplate {
private HelloWorldProperties helloWorldProperties;
private OutputFormater outputFormater;
public HelloWorldTemplate(HelloWorldProperties helloWorldProperties, OutputFormater outputFormater) {
this.helloWorldProperties = helloWorldProperties;
this.outputFormater = outputFormater;
}
/**
* 列印作者資訊
* 列印對象資訊
*
* @param obj 列印的對象
*/
public <T> String generate(T obj) {
StringBuilder message = new StringBuilder();
message.append("author: ").append(outputFormater.format(helloWorldProperties.getAuthor())).append("\n");
message.append("say: Hello World!").append("\n");
message.append("object: ").append(outputFormater.format(obj)).append("\n");
return message.toString();
}
}
(3)HelloWorldProperties:映射application.yaml檔案中的 helloworld.authors資訊:
以通過@ConfigurationProperties(prefix = HelloWorldProperties.HELLOWORLD_PREFIX) 辨別是屬性映射類,同時指定了在主配置檔案中的字首。需要通過@EnableConfigurationProperties(HelloWorldProperties.class)開啟。
package x.demo.springboot.starter.hw.autoconfiguration;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 用于springboot 從配置檔案 (application.yml) 中讀取 helloworld配置
*/
@ConfigurationProperties(prefix = HelloWorldProperties.HELLOWORLD_PREFIX)
public class HelloWorldProperties {
public static final String HELLOWORLD_PREFIX = "helloworld";
private Map<String, Object> author;
public Map<String, Object> getAuthor() {
return author;
}
public void setAuthor(Map<String, Object> author) {
this.author = author;
}
}
(4)OutputFormater:輸出格式化:
1)接口
package x.demo.springboot.starter.hw.outputformater;
/**
* 格式化工具
*/
public interface OutputFormater {
/**
* 格式
* @param obj 對象
* @return 格式字元串
*/
<T> String format(T obj);
}
2)toString()實作
package x.demo.springboot.starter.hw.outputformater;
import java.util.Objects;
/**
* 使用對象toString() 格式化
*/
public class ToStringOutputFormater implements OutputFormater {
/**
* 格式
*
* @param obj 對象
* @return 格式字元串
*/
public <T> String format(T obj) {
return "ToStringOutputFormater:" +Objects.toString(obj);
}
}
3)json 實作
package x.demo.springboot.starter.hw.outputformater;
import com.alibaba.fastjson.JSON;
/**
* 使用fastjson格式化
*/
public class FastJsonOutputFormater implements OutputFormater {
/**
* 格式
*
* @param obj 對象
* @return 格式字元串
*/
public <T> String format(T obj) {
return "FastJsonOutputFormater: " + JSON.toJSONString(obj);
}
}
(5)自動配置類,用于向容器注冊相應的對象:
關于Spring架構@Configuration使用,可參見部落格《Spring:@Configuration 使用》
1)OutputFormaterAutoConfiguration:配置OutputFormater
這裡通過@Conditional注解指定注冊條件,主要用了:
@ConditionalOnMissingClass : classpath 中沒有com.alibaba.fastjson.JSON 執行個體化,建立ToStringOutputFormater
@ConditionalOnClass(name = "com.alibaba.fastjson.JSON") classpath 中有com.alibaba.fastjson.JSON 執行個體化 FastJsonOutputFormater
package x.demo.springboot.starter.hw.autoconfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import x.demo.springboot.starter.hw.outputformater.FastJsonOutputFormater;
import x.demo.springboot.starter.hw.outputformater.OutputFormater;
import x.demo.springboot.starter.hw.outputformater.ToStringOutputFormater;
@Configuration
public class OutputFormaterAutoConfiguration {
/**
* @ConditionalOnMissingClass : classpath 中沒有com.alibaba.fastjson.JSON 執行個體化,建立ToStringOutputFormater
*/
@ConditionalOnMissingClass("com.alibaba.fastjson.JSON")
@Bean
public OutputFormater toStringOutputFormater() {
return new ToStringOutputFormater();
}
/**
* @ConditionalOnClass(name = "com.alibaba.fastjson.JSON") classpath 中有com.alibaba.fastjson.JSON 執行個體化 FastJsonOutputFormater
*/
@ConditionalOnClass(name = "com.alibaba.fastjson.JSON")
@Bean
public OutputFormater fastJsonOutputFormater() {
return new FastJsonOutputFormater();
}
}
2)HelloWorldAutoConfiguration:配置HelloWorldTemplate
package x.demo.springboot.starter.hw.autoconfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import x.demo.springboot.starter.hw.HelloWorldTemplate;
import x.demo.springboot.starter.hw.outputformater.OutputFormater;
@EnableConfigurationProperties(HelloWorldProperties.class)
@Import(OutputFormaterAutoConfiguration.class)
@Configuration
public class HelloWorldAutoConfiguration {
@Bean
public HelloWorldTemplate helloWorldTemplate(HelloWorldProperties helloWorldProperties, OutputFormater outputFormater) {
return new HelloWorldTemplate(helloWorldProperties, outputFormater);
}
}
(6)注冊自動配置類:META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
x.demo.springboot.starter.hw.autoconfiguration.HelloWorldAutoConfiguration
四 spring-boot-web 項目
(1)工程:
1)父 pom : 繼承了spring-boot-starter-parent
<?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>x.demo.springboot.starter</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>module/spring-boot-helloworld-starter</module>
</modules>
<properties>
<java.version>1.8</java.version>
<spring.boot.version>1.5.9.RELEASE</spring.boot.version>
<fastjson.version>1.2.28</fastjson.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring boot 基礎依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2)module:
依賴下spring-boot-helloworld-starter,這裡不依賴FastJson的包,則自定義Starter中執行個體化的是沒 ToStringOutputFormater
<?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>x.demo.springboot</groupId>
<artifactId>spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-web</artifactId>
<packaging>jar</packaging>
<name>spring-boot-web</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>x.demo.springboot.starter</groupId>
<artifactId>spring-boot-helloworld-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
(2)配置檔案:application.yml
# 應用配置
server:
port: 8080
# helloworld starter 配置
helloworld.author:
name: foo
roles: admin, developer
email: [email protected]
(3)controller 調用 HelloWorldTemplate
package x.demo.springboot.web.controller;
import javax.annotation.Resource;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import x.demo.springboot.starter.hw.HelloWorldTemplate;
@RestController
@RequestMapping("/helloworld")
public class HelloWorldContorller {
@Resource
HelloWorldTemplate helloWorldTemplate;
@GetMapping("/say")
public String say() {
String message = helloWorldTemplate.generate("------------");
return StringUtils.replace(message,"\n","<br/>");
}
}
(4)springboot啟動類:
注:@SpringBootApplication 同時添加重要的兩個MetaAnnotation @EnableAutoConfiguration,@ComponentScan,啟動了AutoConfiguration和包掃描。
由于:
web包:x.demo.springboot.web
starter包:x.demo.springboot.starter.hw
包不同,是以Starter不是包掃描注入的,是通過讀取META-INF/spring.factories注入的
package x.demo.springboot.web;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootWeb {
public static void main(String[] args) {
SpringApplication.run(SpringBootWeb.class, args);
}
}
五 結果
1)正常結果
2)添加FastJson依賴的結果
web module pom:
<?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>x.demo.springboot</groupId>
<artifactId>spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-web</artifactId>
<packaging>jar</packaging>
<name>spring-boot-web</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>x.demo.springboot.starter</groupId>
<artifactId>spring-boot-helloworld-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
</project>
六 涉及注解說明
1) 配置
@Configuration,@Import,@Bean:《Spring:@Configuration 使用》
2) 條件組裝
@ConditionalOnBean:Spring容器中存在指定執行個體時
@ConditionalOnClass:類加載器中存在指定類時
@ConditionalOnExpression:指定表達式成立時
@ConditionalOnMissingBean:Spring容器總缺少指定執行個體時
@ConditionalOnMissingClass:類加載器中不存在指定類時
@ConditionalOnNotWebApplication:非Web應用時
@ConditionalOnResource:存在指定資源檔案時
@ConditionalOnWebApplication:Web應用時
3)讀取屬性
@EnableConfigurationProperties:開始掃描處理@ConfigurationProperties注解的Bean
@ConfigurationProperties:綁定外部配置