编写不易,转载请注明(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:绑定外部配置