github:https://github.com/Ccww-lx/Sp... 模块:spring-boot-starter-base-service
git
SpringBoot的方便快捷主要体现之一starter pom
,Spring Boot
为咱们提供了简化企业级开发绝大多数场景的starter pom
, 只要使用了应用场景所须要的starter pom
,只须要引入对应的starter
便可,便可以获得Spring Boot
为咱们提供的自动配置的Bean
。github
然而,可能在不少状况下,咱们须要自定义stater
,这样能够方便公司内部系统调用共同的配置模块的时候能够自动进行装载配置。好比,不少公司将生产数据库的密码托管在公司的另一个专门管理生产密码的系统上,公司每一个系统须要使用的时候都须要调用其方法进行使用,如今能够经过starter
自动配置的形式进行配置。spring
Q:@SpringBootApplication
注解中核心注解@EnableAutoConfiguration
注解在starter起什么做用呢?数据库
@EnableAutoConfiguration
源码分析:windows
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
能够从源码看出关键功能是@import
注解导入自动配置功能类AutoConfigurationImportSelector
类,主要方法getCandidateConfigurations()
使用了SpringFactoriesLoader.loadFactoryNames()
方法加载META-INF/spring.factories的文件(spring.factories声明具体自动配置)。app
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); 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."); return configurations; }
Q:一般状况下,starter
会根据条件进行操做处理,好比根据不一样条件建立不一样Bean
。在SpringBoot
有哪些注解可用呢?
可以使用org.springframwork.boot.autoconfigure.condition
的条件注解,具体以下所示:dom
注解 | 解析 |
---|---|
@ConditionalOnBean | 当容器里有指定的Bean的条件下。 |
@ConditionalOnClass | 当类路径下有指定的类的条件下。 |
@ConditionalOnExpression | 基于SpEL表达式做为判断条件。 |
@ConditionalOnJava | 基于JVM版本做为判断条件。 |
@ConditionalOnJndi | 在JNDI存在的条件下查找指定的位置。 |
@ConditionalOnMissingBean | 当容器里没有指定Bean的状况下。 |
@ConditionalOnMissingClass | 当类路径下没有指定的类的条件下。 |
@ConditionalOnNotWebApplication | 当前项目不是Web项目的条件下。 |
@ConditionalOnProperty | 指定的属性是否有指定的值。 |
@ConditionalOnResource | 类路径是否有指定的值。 |
@ConditionalOnSingleCandidate | 当指定Bean在容器中只有一个, 或者虽然有多个可是指定首选的Bean。 |
@ConditionalOnWebApplicatio | 当前项目是Web项目的条件下。 |
在此将模拟公司获取生产密码模块进行自定义starter demo
spring-boot
<dependencyManagement> <dependencies> <dependency> <!-- Import dependency management from Spring Boot --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.5.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> </dependencies>
PasswordService
服务类:源码分析
public class PasswordService { //第三方系统获取密码所需的key private String objectKey; @Autowired //模拟的第三方系统service private ThirdPartySystemService thirdPartySystemService; public String getSystemPassword(String objectKey,String originalPassord){ if(StringUtils.isEmpty(objectKey)){ return originalPassord; } //从第三方系统获取密码 String password= thirdPartySystemService.getPassword(objectKey); //返回密码 return password!=null?password:originalPassord; } } //模拟第三方系统service public class ThirdPartySystemService { public String getPassword(String objectKey){ //返回一个32位随机数 return UUID.randomUUID().toString(); } }
属性配置类:测试
//经过@ConfigurationProperties注解获取属性值 @ConfigurationProperties(prefix = "project.starter") public class BaseServiceProperties { private String serviceName; private String serviceVersion; public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getServiceVersion() { return serviceVersion; } public void setServiceVersion(String serviceVersion) { this.serviceVersion = serviceVersion; } }
配置属性使用类:
public class BaseStarterService { public void addServiceName(BaseServiceProperties baseServiceProperties){ System.out.println("serviceName:"+baseServiceProperties.getServiceName()+"----"+"serviceVersion"+baseServiceProperties.getServiceVersion()); } }
其余类:
//判断是否windows系统 public class WindowsCondition implements Condition { private final static String WINDOWS="Windows"; /** * ConditionContext:判断条件能使用的上下文(环境) * AnnotatedTypeMetadata:注释信息 */ public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { //获取当前环境变量 Environment environment=conditionContext.getEnvironment(); //获取bean注册器 BeanDefinitionRegistry registry = conditionContext.getRegistry(); //能获取到ioc使用的beanfactory ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory(); //获取环境变量中操做系统 String property = environment.getProperty("os.name"); //判断操做系统是否为windows if(property.contains(WINDOWS)){ //判断是否存在baseWindowsSevice类,不存在则进行bean注册 boolean isWindowsSevice = registry.containsBeanDefinition("baseStarterService"); if(!isWindowsSevice){ //指定Bean定义信息;(Bean的类型,Bean的一系列信息) RootBeanDefinition beanDefinition = new RootBeanDefinition(BaseStarterService.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("baseStarterService", beanDefinition); BaseStarterService windowsSevice = (BaseStarterService)beanFactory.getBean("baseStarterService"); } return true; } return false; } }
代码解读:
@EnableConfigurationProperties
:读取配置文件的属性@Import
:导入其余配置类或者自定义类@Conditional
:判断当前环境是否为windows,是则注册该类@ConditionalOnProperty
:判断属性spring.project.ThirdPartySystemService.isPassword
是否等于true
,不为true
则不注册该类@ConditionalOnClass
:判断IOC容器中是否存在ThirdPartySystemService
类,存在则建立PasswordService bean
@Configuration //自动加载配置文件属性值 @EnableConfigurationProperties(BaseServiceProperties.class) @Import(BeanConfiguration.class) //判断当前环境是否为windows @Conditional(WindowsCondition.class): //判断属性spring.project.ThirdPartySystemService.isPassword是否等于true @ConditionalOnProperty(prefix = "spring.project.ThirdPartySystemService",value = "enablePassword", havingValue = "true",matchIfMissing = true) public class AutoConfigurationPassoword { @Autowired private BaseServiceProperties baseServiceProperties; @Autowired private BaseStarterService baseWindowsService; //加载第三方系统service @Bean("thirdPartySystemService") public ThirdPartySystemService thirdPartySystemService(){ baseWindowsService.addServiceName(baseServiceProperties); return new ThirdPartySystemService(); } @Bean //判断IOC容器中是否存在ThirdPartySystemService类,存在则建立PasswordService bean @ConditionalOnClass(ThirdPartySystemService.class) public PasswordService passwordService(){ baseWindowsService.addServiceName(baseServiceProperties); return new PasswordService(); } }
想自动配置生效, 须要注册自动配置类,即在src/main/resources
下新建METAINF/spring.factories
。在spring.factorie
配置以下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.cn.ccww.configuration.AutoConfigurationPassoword
如有多个自动配置, 则用“,”隔开, 此处“”是为了换行后还可以读取到属性。
<dependencies> <dependency> <artifactId>spring-boot-starter-base-service</artifactId> <groupId>com.cn.ccww</groupId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
application.properties
文件有对应的字段是否启动自定义starter,还能够设置starter所需的属性。以下所示:
//自定义Starter配置 //当该属性的值不为true时,才不会启动自定义starter spring.project.ThirdPartySystemService.enablePassword=true project.starter.serviceName=ccww project.starter.serviceVersion=1.0
由上所述, starter
的大致的工做流程:
SpringBoot
启动时会自动搜索包含spring.factories
文件的JAR包;spring.factories
文件加载自动配置类AutoConfiguration
;AutoConfiguration
类,加载知足条件(@ConditionalOnXxx)
的bean
到Spring IOC
容器中;IOC
的bean
。