编写不易,转载请注明(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包,决定格式化信息方式
(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:绑定外部配置