1、SpringBoot starter机制
在springboot中,使用的最多的就是starter。starter可以理解为一个可拔插式的插件,例如,你想使用jdbc插件,那么可以使用spring-boot-starter-jdbc;如果想使用mongodb,可以使用spring-boot-starter-data-mongodb。
SpringBoot提供了各种场景的spring-boot-starter依赖模块,所有这些依赖模块都遵循着默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。,应用者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。starter让我们摆脱了各种依赖库的处理,需要配置各种信息的困扰。SpringBoot会自动通过classpath路径下的类发现需要的Bean,并注册进IOC容器。
2、为什么要自定义starter
日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍。如果我们将这些可独立于业务代码之外的功配置模块封装成一个starter,复用的时候只需要将其在pom中引用依赖即可。
SpringBoot提供的starter以spring-boot-starter-xxx
的方式命名的。自定义的starter使用xxx-spring-boot-starter
命名,以区分SpringBoot生态提供的starter。
3、starter的实现方法
2. 引入必要的依赖
注意:这里的packaging为jar,starter需要使用到Spring boot的自动配置功能,所以需要引入自动配置相关的依赖
<groupId>com.mengday</groupId>
<artifactId>helloworld-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging><dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.0.0.RELEASE</version>
<optional>true</optional>
</dependency>
</dependencies>
XxxProperties
在使用Spring官方的Starter时通常可以在application.properties中来配置参数覆盖掉默认的值,例如在使用redis时一般就会有对应的RedisProperties
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String password;
private int port = 6379;
}
我们来模仿来定义自己的Properties类
@ConfigurationProperties(prefix = "spring.demo")
public class DemoProperties {
private String sayWhat;
private String toWho;
// Getter & Setter
}
核心服务类
每个starter都有自己的功能,例如在spring-boot-starter-jdbc中最重要的类时JdbcTemplate,每个starter中的核心业务类明白都不同,也没什么规律(像spring-boot-starter-data-xxx的命名是比较有规律的)
public class DemoService {
public String sayWhat;
public String toWho;
public DemoService(String sayWhat, String toWho){
this.sayWhat = sayWhat;
this.toWho = toWho;
}
public String say(){
return this.sayWhat + "! " + toWho;
}
}
自动配置类
一般每个starter都至少会有一个自动配置类,一般命名规则使用XxxAutoConfiguration, 例如RedisAutoConfiguration
@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration { @Configuration
@ConditionalOnClass(GenericObjectPool.class)
protected static class RedisConnectionConfiguration {
private final RedisProperties properties;
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory()
throws UnknownHostException {
return applyProperties(createJedisConnectionFactory());
}
}
@Configuration
protected static class RedisConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}}
这里我们定义自己的自动配置
@Configuration
//@EnableConfigurationProperties({Demo1Properties.class, DemoProperties.class})
@EnableConfigurationProperties(DemoProperties.class)
@ConditionalOnClass(DemoService.class)
@ConditionalOnProperty(
prefix = "spring.demo",
name = "isopen",
havingValue = "true"
)
public class DemoConfig {
@Autowired
private Demo1Properties demo1Properties;
@Autowired
private DemoProperties demoProperties; @Bean
@ConditionalOnMissingBean(DemoService.class) // 当容器中没有指定Bean的情况下,自动配置PersonService类
public DemoService demoService(){
return new DemoService(demoProperties.getSayWhat(), demoProperties.getToWho());
}
/* @Bean
@ConditionalOnMissingBean(DemoService.class) // 当容器中没有指定Bean的情况下,自动配置PersonService类
public DemoService demo1Service(){
return new DemoService(demo1Properties.getSayWhat(), demo1Properties.getToWho());
}*/
}
@ConditionalOnClass:当类路径classpath下有指定的类的情况下进行自动配置
@ConditionalOnMissingBean:当容器(Spring Context)中没有指定Bean的情况下进行自动配置
@ConditionalOnProperty(prefix = “example.service”, value = “enabled”, matchIfMissing = true),当配置文件中example.service.enabled=true时进行自动配置,如果没有设置此值就默认使用matchIfMissing对应的值
@ConditionalOnMissingBean,当Spring Context中不存在该Bean时。
@ConditionalOnBean:当容器(Spring Context)中有指定的Bean的条件下
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
@ConditionalOnWebApplication:当前项目是Web项目的条件下
@ConditionalOnResource:类路径下是否有指定的资源
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean
.src/main/resources/META-INF/spring.factories
注意:META-INF是自己手动创建的目录,spring.factories也是手动创建的文件,在该文件中配置自己的自动配置类
#-------starter自动装配---------
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.liu.config.DemoConfig
测试
打包mvn clean install
<dependencies>
<dependency><!--引入依赖-->
<groupId>com.liu</groupId>
<artifactId>propertiesOnloadCustom-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
配置application.properties
spring.demo.isopen=true
spring.demo.say-what=hello
spring.demo.to-who=world
测试类
@RestController
public class TestController { @Resource
private DemoService demoService;
@GetMapping("/say")
public String sayWhat(){
return demoService.say();
}
}@SpringBootApplication
public class StartApp {
//TransactionAutoConfiguration
public static void main(String[] args) {
SpringApplication.run(StartApp.class, args);
}
}