springboot 自定义starter之AutoConfiguration【原】
八、自定义starter
AutoConfiguration:
1、这个场景需要使用到的依赖是什么?
没有特别依赖的配置
2、如何编写自动配置
@Configuration //指定这个类是一个配置类 @ConditionalOnXXX //在指定条件成立的情况下自动配置类生效 @AutoConfigureAfter({YYY.class,ZZZ.class}) //指定自动配置类的顺序,在哪个自动配置类之后才配置 @Bean //给容器中添加组件,写成@Component也可 @ConfigurationPropertie // 结合相关xxxProperties类来绑定相关的配置 @EnableConfigurationProperties //让xxxProperties生效直接加入到容器中,可以直接使用@Autowired自动包装该xxxProperties类 //自动配置类要能加载 //将需要启动就加载的自动配置类,配置在META-INF/spring.factories (比如spring-boot-autoconfigure-1.5.14.RELEASE.jar包的类路径下的/META-INF/spring.factories文件) org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
模式:
starter启动器只用来做依赖导入;可以是一个空jar文件,仅提供辅助性依赖管理,这些依赖可能用于自动装配或其它类库。所以建模块时建成普通manven即可。
autoconfigurer自动配置模块必需要依赖spring-boot-starter, 所以建模块时最好建成springboot项目。
starter和autoconfigurer分成两个项目, 是为了形成一个starter对应对多autoconfigurer的后期扩展,方便管理。以后别人引用时只需要引用starter,而不用关心autoconfigurer。
建议命名规则
命名规则 | 定义名 | 举例 | ||
官方starter | spring-boot-starter-OfficalDefinition | 名字在后面 | spring-boot-starter-jdbc | |
自定义starter | MyDefinition-spring-boot-starter | 名字在前面 | starter叫mybatis-spring-boot-starter | |
项目说明
1、my-spring-boot-starter-autoconfigurer 自动配置模块
2、my-spring-boot-starter 场景启动器starter模块 ,依赖my-spring-boot-starter-autoconfigurer
3、starter-demo 主项目 依赖my-spring-boot-starter
starter-demo-2019-6-12 15-17-11.snag
步骤
1、自动配置模块 my-spring-boot-starter-autoconfigurer
pom.xml
<?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-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>my-spring-boot-starter-autoconfigurer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>my-spring-boot-starter-autoconfigurer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--autoconfigurer只需要依赖原生的spring-boot-starter--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 配置文件自动映射 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> </project>
Ticket.java
package com.example.simple.bean; import java.io.Serializable; public class Ticket implements Serializable { public Integer id; public String name; private String type; public Ticket() { } public Ticket(Integer id, String name, String type) { this.id = id; this.name = name; this.type = type; } //get set toString .... }
TicketProperties.java
package com.example.simple.bean; import org.springframework.boot.context.properties.ConfigurationProperties; //和application.properties或application.yml产生关联 @ConfigurationProperties(prefix = "com.ticket") public class TicketProperties { Integer id;//可以用 application.properties中com.ticket.id=xxx 的配置自动赋值 String name;//可以用 application.properties中com.ticket.name=xxx 的配置自动赋值 String type;//可以用 application.properties中com.ticket.type=xxx 的配置自动赋值 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } }
ITicketService.java
接口
package com.example.simple.service; import com.example.simple.bean.Ticket; import com.example.simple.bean.TicketProperties; public interface ITicketService { public Ticket getDefaultTicket(String type); public TicketProperties getTicketProperties(); public void setTicketProperties(TicketProperties ticketProperties); }
PianoTicketService.java
package com.example.simple.service; import com.example.simple.bean.Ticket; import com.example.simple.bean.TicketProperties; /** * 为了在 TicketServiceAutoConfiguration 的@Bean注解的方法中使用以返回该对象到IOC容器中, * 最终可以让其它依赖后的项目的Controller,Service等组件使用@Autowired自动装配该Bean */ public class PianoTicketService implements ITicketService { TicketProperties ticketProperties; @Override public Ticket getDefaultTicket(String type){//用于其它项目的Controller中 Ticket ticket = new Ticket(9999,ticketProperties.getName(),ticketProperties.getType()); return ticket; } @Override public TicketProperties getTicketProperties() { //get方法 return ticketProperties; } @Override public void setTicketProperties(TicketProperties ticketProperties) {//set方法 this.ticketProperties = ticketProperties; } }
ViolinTicketService.java
package com.example.simple.service; import com.example.simple.bean.Ticket; import com.example.simple.bean.TicketProperties; public class ViolinTicketService implements ITicketService { TicketProperties ticketProperties; @Override public Ticket getDefaultTicket(String type){//用于其它项目的Controller中 Ticket ticket = new Ticket(9999,ticketProperties.getName(),ticketProperties.getType()); return ticket; } @Override public TicketProperties getTicketProperties() {//get方法 return ticketProperties; } @Override public void setTicketProperties(TicketProperties ticketProperties) {//set方法 this.ticketProperties = ticketProperties; } }
TicketServiceAutoConfiguration.java
package com.example.simple.config; import com.example.simple.bean.TicketProperties; import com.example.simple.service.ITicketService; import com.example.simple.service.PianoTicketService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration//标注这是一个配置类,这个类的位置不需要在启动类MySpringBootApplication的子包下,因为由META-INF/spring.factories指定了加载位置 @ConditionalOnWebApplication//添加一些条件(也可以不加)web项目时才有效 @ConditionalOnClass({ITicketService.class})//ITicketService.class存在时本配置才有效 //@ConditionalOnMissingClass({"com.example.simple.service.ITicketService"})//指定类不存在的时候配置才有效 @EnableConfigurationProperties(TicketProperties.class)//让TicketProperties在当前配置中直接生效 public class TicketServiceAutoConfiguration {//声明自动配置类 @Autowired TicketProperties ticketProperties;//自动包装 //声明自动装配的Bean,这样当于在项目启动时就内置加载了该Bean @Bean //这个配置就是SpringBoot可以优先使用自定义Bean的核心所在,如果没有我们的自定义Bean ITicketService.class 存在, 那么才会自动配置一个新的Bean @ConditionalOnMissingBean(ITicketService.class) public ITicketService ticketService() { System.out.println("my-spring-boot-starter-autoconfigurer的bean:ticketService装配到容器成功"); ITicketService ticketService = new PianoTicketService();//声明对象 ticketService.setTicketProperties(ticketProperties);//设置属性 return ticketService;//返回对象 } // 参考: https://blog.csdn.net/xcy1193068639/article/details/81517456 //@ConditionOnBean在判断list的时候,如果list没有值,返回false,否则返回true //@ConditionOnMissingBean在判断list的时候,如果list没有值,返回true,否则返回false,其他逻辑都一样 }
spring.factories
创建文件夹及文件 src\main\resources\META-INF\spring.factories , 目录不可更改
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.simple.config.TicketAutoConfiguration
如果有多个自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.xxx.yyy.Zzz1AutoConfiguration,\ com.xxx.yyy.Zzz2AutoConfiguration,\ com.xxx.yyy.Zzz3AutoConfiguration
2、启动器模块 my-spring-boot-starter
可以包含多个autoconfigurer模块, 方便其它项目引用
pom.xml
<?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.example</groupId> <artifactId>my-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> <!--引入自动配置模块--> <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>my-spring-boot-starter-autoconfigurer</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- <dependency> <groupId>com.example</groupId> <artifactId>my-spring-boot-starter-other-autoconfigurer</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> --> </dependencies> </project>
3、 starter-demo
starter-demo为依赖my-spring-boot-starter的项目 , 就像正常maven项目一样, 在pom.xml添加my-spring-boot-starter即可.
pom.xml
<?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-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>starter-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>starter-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--配置自定义的starter依赖--> <dependency> <groupId>com.example</groupId> <artifactId>my-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
TicketController.java
http get访问入口 http://localhost:8080/ticket?type=piano
package com.example.starterdemo.controller; import com.example.simple.bean.Ticket; import com.example.simple.service.ITicketService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TicketController { @Autowired ITicketService ticketService; //http://localhost:8080/ticket?type=piano @GetMapping(value = "/ticket") public Ticket getDefaultTicket(String type){ Ticket ticket = ticketService.getDefaultTicket(type); return ticket; } }
StarterDemoApplication.java
启动应用的类 , 启用了public ITicketService ticketService() 方法, 会在容器中添加ITicketService bean , 让默认自动配置失效, 当注释该方法后, 又可以启动自动配置.
package com.example.starterdemo; import com.example.simple.bean.TicketProperties; import com.example.simple.service.ITicketService; import com.example.simple.service.PianoTicketService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @SpringBootApplication public class StarterDemoApplication { //声明自动装配的Bean,这样当于在项目启动时就内置加载了该Bean //本bean将会优先于my-spring-boot-starter-autoconfigurer自动装配模块的 TicketServiceAutoConfiguration 类中的ticketService bean的初始化, //而自动装配模块有@ConditionalOnMissingBean(ITicketService.class)条件,所以一旦配了该bean,将替换自动装配模块的bean //为了作出区别,本bean的初始化值不再从properties文件中获取,而是直接代码声明了. @Bean public ITicketService ticketService() { System.out.println("starter-demo的bean:ticketService装配到容器成功, my-spring-boot-starter-autoconfigurer的bean:ticketService不再装配"); ITicketService ticketService = new PianoTicketService();//声明对象 TicketProperties ticketProperties = new TicketProperties(); ticketProperties.setId(8888); ticketProperties.setName("圣母颂"); ticketProperties.setType("violin"); ticketService.setTicketProperties(ticketProperties);//设置属性 return ticketService;//返回对象 } //http://localhost:8080/ticket?type=piano public static void main(String[] args) { SpringApplication.run(StarterDemoApplication.class, args); } }
参考
我的项目git地址
我的git代码地址: https://gitee.com/KingBoBo/Spring-Boot-Starter-Demo starter分支