一个简单的自定义starter流程记录,不涉及深入讲解。
springboot的优势之一是我们需要使用什么只需要引入一个xx-strater或spring-boot-starter-xx的依赖就行,不需要担心 xx 中需要的依赖。
xx-strater是第三方提供的依赖,如 mybatis-spring-boot-starter
spring-boot-starter-xx是springboot提供的依赖,如spring-boot-starter-data-redis
要自己写一个starter就要先知道springboot是怎么实现自动装配的。
自动装配主要涉及到三个注解,这三个注解都在启动类的@SpringBootApplication注解里
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
@SpringBootConfiguration 将类标识为配置文件,里面使用了@Configuration注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@ComponentScan 扫描启动类所在包及其子包下的所有@Service,@Controller,@RestController,@Component,@Repository等可以将类表示为bean的注解,这些注解的类会转化为bean,由spring容器管理
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
//....
}
@EnableAutoConfiguration 是自动装配的关键注解,适用于 动态启用某个功能的,底层原理是使用 @Import注解导入一下配置,实现Bean的动态加载
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
/**
* Environment property that can be used to override when auto-configuration is
* enabled.
*/
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
springboot自动装配的关键注解是
@EnableAutoConfiguration,该注解上使用
@Import(AutoConfigurationImportSelector.class) 【import有四种实现方式,这是其一】
AutoConfigurationImportSelector实现 ImportSelector重写selectImports()方法,
selectImports()最终返回一个String[],即所需要的所有Bean
疑问:selectImports()方法里如何获取到所需的所有bean呢?
selectImports()中调用了getAutoConfigurationEntry()方法,
getAutoConfigurationEntry()方法内部调用了getCandidateConfigurations()方法,点进去可以看到下面的信息,说明是读取的 META-INF/spring.factories这个文件里面的内容
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct.");
META-INF/spring.factories文件里有org.springframework.boot.autoconfigure.EnableAutoConfiguration 这个key,后面的value就是自定义的一个提供Bean的配置文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.custom.customconfig.configuration.CustomConfiguration
所有如果要向自定义一个starter,首先要先自定义一个configuration
前面说了xx-starter这个项目只是整合了所有的依赖,并不涉及方法的实现,所以xx-starter和xx-configuration是两个项目或模块,提供bean是在xx-configuration项目中实现的,最后在xx-starter项目中引入xx-configuration的依赖就行了。
所以第一步,先创建两个module : custom-auto-configuration 和 custom-spring-boot-starter
custom-auto-configuration 就来实现bean的配置文件和spring.factories
custom-auto-configuration项目结构
src/main/java/com/custom/customconfig
/configuration
-- CustomConfiguration.java(配置文件类)
/entity
-- OriginFire.java(实体类)
/properties
-- CustomRedisProperties.java(属性类)
-- OriginFireProperties.java(属性类)
src/main/resources
/META-INF
-- spring.factories
属性类是啥???--- 读取用户在application.properties或application.yml中配置的属性【因为在创建Bean的时候有些属性值需要根据用户的配置来赋值】
实现代码:
OriginFire.java
@Data
@AllArgsConstructor
public class OriginFire {
private int degree;
private String fireName;
private String location;
}
OriginFireProperties.java
@Data
@ConfigurationProperties(prefix = "origin.fire")
public class OriginFireProperties {
private int degree = 1163;
private String fireName = "tengchong volcano";
private String location = "tengchong";
}
注意类上使用了@ConfigurationProperties(prefix = "origin.fire"),这个注解允许您将配置外部化并将外部源 (如属性文档) 中的属性绑定到 Java 对象。
说白了就是可以将我们自己在项目的application.properties或application.yml中配置的相对应的属性值绑定到对象上,需要配合@EnableConfigurationProperties注解使用【在CustomConfiguration.java中用到了】
prefix = "origin.fire" 是限定前缀,只读取这个前缀下面的属性,比如有两个degree属性,一个在origin.fire下面origin.fire.degree=1200,另一个是在外部 degree=30,有这个限定前缀,最终获取的值是1200
CustomRedisProperties.java
/**
* 读取配置文件中的配置信息
*/
@Data
@ConfigurationProperties(prefix = "spring.redis")
public class CustomRedisProperties {
private String host;
private int port;
}
CustomConfiguration.java
/**
* 用于创建bean
*/
@Configuration
@EnableConfigurationProperties({CustomRedisProperties.class, OriginFireProperties.class})
public class CustomConfiguration {
@Bean
@ConditionalOnMissingBean(name = "jedis")
public Jedis jedis(CustomRedisProperties properties){
return new Jedis(properties.getHost(), properties.getPort());
}
@Bean
@ConditionalOnProperty(name = "origin.fire.location")
public OriginFire originFire(OriginFireProperties properties){
return new OriginFire(properties.getDegree(), properties.getFireName(), properties.getLocation());
}
}
CustomConfiguration.java中使用
注解@Configuration 标注这个类是个配置类,用于提供Bean,这个注解也会使CustomConfiguration.java本身注册为spring bean
注解@EnableConfigurationProperties() 主要是用来吧properties或yml配置文件转化为bean来使用,其作用就是使@ConfigurationProperties注解生效
@Bean 将对象注册为spring bean
@ConditionalOnMissingBean(name = "jedis") 是一个限制是否创建bean的条件,这个注解的限制是只有在 jedis 这个bean不存在的时候才调用这个方法创建bean,如果存在就不再次创建了
@ConditionalOnProperty(name = "origin.fire.location") 也是一个限制是否创建bean的条件,这个注解是限制在properties或yml中必须又origin.fire.location属性才会创建bean,否则不创建
最后在项目的resources下面创建 META-INF/spring.factories, 将自己定义的CustomConfiguration配置进来
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.custom.customconfig.configuration.CustomConfiguration
至此custom-auto-configuration项目中的配置类就写完了
最后在custom-spring-boot-starter 的pom.xml文件中引入custom-auto-configuration的依赖即可
<dependency>
<groupId>com.custom</groupId>
<artifactId>custom-auto-configuration</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
至此 自定义的starter就完成拉,打好jar包,就可以在其他项目中使用了。