注:本文Spring Boot为2.X版本
在Spring Boot中,官方提供了
spring-boot-autoconfigure
包和starter包用来帮助我们简化配置,比如之前要建一个Spring mvc项目,需要我们配置web.xml,dispatcherservlet-servlet.xml,applicationContext.xml等等。而在Spring Boot中只需要在pom中引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
就能完成之前所有的工作了。简直so easy啊。
但是只会用是不行的,还要知其所以然,本文以官方的starter:
spring-boot-starter-data-redis
为例,从源码层面上分析整个自动化配置的过程。以期对starter和autoconfigure这两个Spring Boot的核心模块进行梳理。
了解原理后,我会通过模拟
spring-boot-starter-data-redis
,并使用Jedis来创建一个处理redis的自定义starter:
my-redis-starter
。源码下载
点我
,最后会详细说明自定义starter的创建过程。
在Spring Boot中使用默认的redis客户端只需要
在pom.xml中引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后在
application.properties
中配置ip,密码等必要参数
spring.redis.host=106.15.108.50
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=123456
#等等
就可以直接在我们的业务中调用
org.springframework.data.redis.core.RedisTemplate
来处理缓存的相关操作了。
一.RedisTemplate的注入
让我们先来看下RedisTemplate是如何被注入的。
1.RedisProperties
在
application.properties
中ctrl+左击redis的相关配置项,会打开
spring-boot-autoconfigure\2.0.2.RELEASE\spring-boot-autoconfigure-2.0.2.RELEASE.jar
中的
RedisProperties
。
打开
org.springframework.boot.autoconfigure.data.redis.RedisProperties.class
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
/**
* Database index used by the connection factory.
*/
private int database = 0;
/**
* Connection URL. Overrides host, port, and password. User is ignored. Example:
* redis://user:password@example.com:6379
*/
private String url;
/**
* Redis server host.
*/
private String host = "localhost";
/**
* Login password of the redis server.
*/
private String password;
/**
* Redis server port.
*/
private int port = 6379;
/**
* Whether to enable SSL support.
*/
private boolean ssl;
/**
* Connection timeout.
*/
private Duration timeout;
private Sentinel sentinel;
private Cluster cluster;
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
......
}
(1)
@ConfigurationProperties(prefix = "spring.redis")
设置绑定属性的前缀,然后看下前面的一些属性,是不是很眼熟?前缀+属性名就是之前在
application.properties
中配置的,如果我们没有配置端口这种属性,那么这里也会提供部分默认配置。
当然,只是这些是没办法让Spring Boot在启动时扫描到该类的,所以需要下一个类
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class
。
然后我们还能找到
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
一般用Java操作redis用的较多几个Java客户端为Jedis,Redisson,Lettuce。这里可知官方提供的
spring-boot-starter-data-redis
底层是用Jedis/Lettuce实现的,知道了这个我们也能够借鉴这个starter来使用其他的客户端来实现了。
2.RedisAutoConfiguration
打开
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
(1)
@Configuration
常见的配置注解,内部含有一个以上的
@Bean
,让Spring能扫描到内部的
@Bean
,当然在Spring Boot中,默认只会扫描到启动类所在包或其下级包的类,所以还会通过其他的设置来让这个类被扫描到,这个后面会详细说明。
@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
(2)
@ConditionalOnClass(RedisOperations.class)
,当存在
RedisOperations
类时才会进行扫描,这个类什么时候被引入classpath的之后会提到。
(3)
@EnableConfigurationProperties(RedisProperties.class)
让
RedisProperties
类被扫描到的关键。这时,如果
RedisAutoConfiguration
被扫描到,则同时也会去扫描
RedisProperties
类。
(4)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
通过@Import注解方式生成类实例并注入Spring容器。
@Import注解通过导入的方式实现把实例加入springIOC容器中
让我们打开
JedisConnectionConfiguration
简要看下
@Configuration
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
private final RedisProperties properties;
private final List<JedisClientConfigurationBuilderCustomizer> builderCustomizers;
JedisConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
ObjectProvider<RedisClusterConfiguration> clusterConfiguration,
ObjectProvider<List<JedisClientConfigurationBuilderCustomizer>> builderCustomizers) {
super(properties, sentinelConfiguration, clusterConfiguration);
this.properties = properties;
this.builderCustomizers = builderCustomizers
.getIfAvailable(Collections::emptyList);
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
return createJedisConnectionFactory();
}
......
}
-
@Import
注解会通过
JedisConnectionConfiguration
构造方法将
JedisConnectionConfiguration
的实例注入到Spring容器中,这里有一个
RedisProperties
参数,实际上就是在
(3)
中注入的
RedisProperties
,这样
JedisConnectionConfiguration
就获得了
RedisProperties
,也就获得了之前我们在
application.propertie
中配置的redis服务器连接属性。 -
通过
@Configuration
和
@Bean
的定义可知,会扫描到
redisConnectionFactory()
方法并返回实体,并注入到Spring容器,对应的类为
RedisConnectionFactory
。(
JedisConnectionConfiguration
实现了
RedisConnectionFactory
接口,所以可以这样)@Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException { return createJedisConnectionFactory(); } private JedisConnectionFactory createJedisConnectionFactory() { JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(); if (getSentinelConfig() != null) { return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration); } if (getClusterConfiguration() != null) { return new JedisConnectionFactory(getClusterConfiguration(), clientConfiguration); } return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration); } private JedisClientConfiguration getJedisClientConfiguration() { JedisClientConfigurationBuilder builder = applyProperties( JedisClientConfiguration.builder()); RedisProperties.Pool pool = this.properties.getJedis().getPool(); if (pool != null) { applyPooling(pool, builder); } if (StringUtils.hasText(this.properties.getUrl())) { customizeConfigurationFromUrl(builder); } customize(builder); return builder.build(); }
①
getJedisClientConfiguration()
方法,该方法从之前注入的
RedisProperties
中获取了 Jedis客户端连接池。
②
createJedisConnectionFactory
会根据配置的redis参数判断用单机/哨兵/集群模式来创建
JedisConnectionFactory
实例。
总结:创建并注入了
JedisConnectionFactory
实例,
JedisConnectionFactory
实例中包含有Jedis的客户端连接池,之后就能用其创建连接了。
(5)
redisTemplate
方法
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
终于找到注入redisTemplate的地方了= =。
-
这是个被
@Bean
注解的方法,因此会被Spring扫描并注入。 -
@ConditionalOnMissingBean(name = "redisTemplate")
当Spring容器中不存在RedisTemplate实例时才会进行扫描注入,很明显是为了防止重复注入。 -
该方法有一个
RedisConnectionFactory
参数。
而我们知道(4)中
redisConnectionFactory
方法最后会注入一个
JedisConnectionFactory
实例,而
JedisConnectionFactory
又是继承于
RedisConnectionFactory
。同志们,你们懂我的意思了吧∠( ᐛ 」∠)_。
总结:该方法会将先前注入的
redisConnectionFactory
赋给新建的
redisTemplate
实例,
然后将
redisTemplate
实例注入Spring容器。
但是这里出现一个问题了
开始时通过
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
注入了Lettuce和Jedis两个连接配置实例,
而这两个中又都已
@Bean
的形式注入了
JedisConnectionFactory
和
LettuceConnectionFactory
两个实例(这两个实例的类又都是继承于
RedisConnectionFactory
的),并且注入时都是对应
RedisConnectionFactory
类的。那么
redisTemplate
方法最后是使用哪个实例来创建RedisTemplate的呢?
通过debug我们知道实际用的是
LettuceConnectionFactory
实例。
这么看是按照
@Import
中的排序来的,
这里
LettuceConnectionConfiguration
在前,所以会先扫描
LettuceConnectionConfiguration
。相关代码
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public LettuceConnectionFactory redisConnectionFactory(
ClientResources clientResources) throws UnknownHostException {
LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(
clientResources, this.properties.getLettuce().getPool());
return createLettuceConnectionFactory(clientConfig);
}
LettuceConnectionConfiguration
中会创建
LettuceConnectionFactory
实例,并将其注入为
redisConnectionFactory
类的实例,
然后在
JedisConnectionConfiguration
中的类似代码:
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
return createJedisConnectionFactory();
}
也会创建一个
JedisConnectionFactory
实例,并将其注入为
redisConnectionFactory
类的实例。
双方都有
@ConditionalOnMissingBean(RedisConnectionFactory.class)
约束,所以当
LettuceConnectionConfiguration
中
RedisConnectionFactory
类被注入了对应的实例后,
JedisConnectionConfiguration
对应的代码就不会再执行了,所以最后
RedisConnectionFactory
类的实例实际上是
LettuceConnectionFactory
。
只要把
@Import
中的顺序换一下就能改变
RedisConnectionFactory
类的实例了。
可能有的童鞋会问,“如果把
@ConditionalOnMissingBean(RedisConnectionFactory.class)
去掉呢?”
这样的话
JedisConnectionConfiguration
中的
@Bean
是否能覆盖掉之前的那个,实现重复注入呢?抱歉,这样会报错(大致意思是RedisConnectionFactory已经有一个对应的bean了,不能再注入第二个)。
这个我们可以做个小测试
新建一个Spring Boot项目,勾一个web即可。
新建
B
,
C
类。
B
,
C
类用来模拟
LettuceConnectionConfiguration
和
JedisConnectionConfiguration
类
这里类上没有添加
@Configuration
注解也是为了不被Spring扫描到,然后通过
@Import
才会进行注入。
public class B{
@Bean
@ConditionalOnMissingBean(TestInfoA.class)
public TestInfoB testInfoA() {
return new TestInfoB();
}
}
public class C{
@Bean
@ConditionalOnMissingBean(TestInfoA.class)
public TestInfoC testInfoA() {
return new TestInfoC();
}
}
新建
TestInfoA
,
TestInfoB
,
TestInfoC
。其中
TestInfoA
为接口,
TestInfoB
和
TestInfoC
都实现了
TestInfoA
。用来模拟
RedisConnectionFactory
接口,
JedisConnectionFactory
和
LettuceConnectionFactory
。
public interface TestInfoA {
}
public class TestInfoB implements TestInfoA{
}
public class TestInfoC implements TestInfoA{
}
新建
TestInfo
,模拟
RedisTemplate
public class TestInfo {
private TestInfoA info;
public TestInfoA getInfo() {
return info;
}
public void setInfo(TestInfoA info) {
this.info = info;
}
}
新建
TestConfig
,用来模拟
RedisAutoConfiguration
@Configuration
@Import({B.class,C.class})
public class TestConfig {
@Bean
@ConditionalOnMissingBean(name = "testInfo")
public TestInfo testInfo(TestInfoA param) {
TestInfo info = new TestInfo();
info.setInfo(param);
return info;
}
}
为了更好的展示,所以这里的结构完全仿照redis-starter的,实际上也不用那么复杂就是了,然后在
TestConfig
中的
testInfo
方法中打个断点,为了看
TestInfoA
实际上是
TestInfoB
还是
TestInfoC
类,运行。
可以看到实际上是
TestInfoB
,而
@import
中也是B在前。
然后改成
@Import({C.class,B.class})
然后结果
可以看出是按照出现在
@Improt
中的顺序来注入的。
然后测试下把B,C类的
@ConditionalOnMissingBean(TestInfoA.class)
注释掉
报错
The bean ‘testInfoA’, defined in class path resource
[com/my/startingProcedure/my/C.class], could not be registered. A bean
with that name has already been defined in class path resource
[com/my/startingProcedure/my/B.class] and overriding is disabled.
总结:
redisTemplate
方法中的
RedisConnectionFactory
其实是
LettuceConnectionFactory
。
然后我们就可以通过这个注入的RedisTemplate来操作redis了。
3. spring-boot-autoconfigure
Spring Boot可以依据classpath里面的依赖内容来自动配置bean到IOC容器。
但是要开启这个自动配置功能需要添加@EnableAutoConfiguration注解。
上面指的自动配置功能事实上就是
spring-boot-autoconfigure
模块
然后让我们打开一个Spring Boot项目的启动项,是否注意到有一个
@SpringBootApplication
注解,这个是默认就有的。然后点开
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
发现
@EnableAutoConfiguration
,也就是说Spring Boot是默认开启自动配置功能的,即
spring-boot-autoconfigure
模块是被默认引用的。
然后让我们看下
spring-boot-autoconfigure.jar
中的该文件
相信能够找到
RedisAutoConfiguration
EnableAutoConfiguration
是不是和刚才讲到的注解一模一样呢?
_(:з」∠*)_
。
这里还涉及到了Spring Boot的启动过程
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
在Spring Boot启动时,会在
refreshContext(context);
阶段完成配置类的解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等,这时会读取pom中引入jar的配置文件
/META-INF/spring.factories
,所以这里
EnableAutoConfiguration
下的所有类都会被实例化并注入Spring容器。
所以
RedisAutoConfiguration
就被扫描到了。
再来回顾下
RedisAutoConfiguration
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
}
会发现
@ConditionalOnClass(RedisOperations.class)
,如果想要被扫描到还需要在classpath中存在
RedisOperations.class
,这个又在哪呢?
点开
RedisOperations.class
,会发现其存在于
spring-data-redis-2.1.3.RELEASE.jar
中
但我们貌似没有引入
spring-data-redis
,这个是哪里来的呢?先让我们先看下之前pom中的引入,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后ctrl+左键继续查看
能看到版本了,再继续查看。
发现其会引入
spring-data-redis
,因此只有当我们在pom中引入
spring-boot-starter-data-redis
时,
RedisAutoConfiguration
才会真正的开启扫描。这也体现了Spring Boot的即插即用和方便快捷的自动配置。
然后下面还有一个io.lettuce,而之前在
RedisAutoConfiguration
中我们知道
redisTemplate
方法最终会把一个
LettuceConnectionFactory
实例注入Spring容器,而在这里实际上就已经大致表明了
RedisAutoConfiguration
会使用Lettuce客户端了。
4.总结
当要使用Spring Boot提供的redis客户端功能时,注入
RedisTemplate
的流程大致如下。
1.pom中引入
spring-boot-starter-data-redis
,并配置
application.properties
。
2.pom会根据
spring-boot-starter-data-redis
来引入
spring-data-redis
。
3.
spring-data-redis
中包含
RedisOperations
类。
4.启动Spring Boot,在
refreshContext(context);
中会初始化beanFactory,读取配置信息,初始化Spring容器,注入bean。因为
@EnableAutoConfiguration
开启的关系,会读取配置中
EnableAutoConfiguration
相关的类,并实例化注入Spring 容器。
5.根据配置文件扫描到
RedisAutoConfiguration
。当
RedisOperations
存在时
RedisAutoConfiguration
才会被扫描。
6.通过
@EnableConfigurationProperties(RedisProperties.class)
和
@ConfigurationProperties(prefix = "spring.redis")
,把
application.properties
中的对应属性进行绑定,并注入RedisProperties配置类。
7.
RedisAutoConfiguration
中的
@Import
会引入
LettuceConnectionConfiguration
和
JedisConnectionConfiguration
8.
LettuceConnectionConfiguration
和
JedisConnectionConfiguration
被扫描,扫描到内部的
@Bean
,使用上一步中注入的
RedisProperties
bean作为参数来实例化
LettuceConnectionFactory
和
JedisConnectionFactory
,并以
RedisConnectionFactory
类注入Spring容器。
8.扫描并注入
RedisAutoConfiguration
类内的@Bean,其中会使用
RedisConnectionFactory
bean作参数实例化
RedisTemplate
。
9.将
RedisTemplate
实例注入。
10.然后就能通过引用
RedisTemplate
来操作redis了。
五.创建自定义starter
my-redis-starter
项目代码下载
点我
完成后的项目结构
1.新建maven项目
结构最简单的就行
2.引入spring-boot-autoconfigure
在pom中引入spring-boot-autoconfigure jar。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.5.4.RELEASE</version>
</dependency>
</dependencies>
因为需要用到Spring Boot的autoconfigure功能进行自动化配置。
3.引入相关外部jar
引入jedis-2.9.0.jar,commons-pool2-2.4.2.jar
因为相关的jar已经放在项目下了(/src/WEB-INF/lib),所以直接引入即可。
4.新建配置类
新建
MyRedisProperties.java
,用来绑定配置文件中的属性值。
@ConfigurationProperties(prefix = "my.redis")
public class MyRedisProperties {
//Redis服务器IP
private String ADDR = "192.168.0.41";
//Redis的端口号
private int PORT = 6379;
//访问密码
private String AUTH = "admin";
public String getADDR() {
return ADDR;
}
public void setADDR(String aDDR) {
ADDR = aDDR;
}
public int getPORT() {
return PORT;
}
public void setPORT(int pORT) {
PORT = pORT;
}
public String getAUTH() {
return AUTH;
}
public void setAUTH(String aUTH) {
AUTH = aUTH;
}
/**
* 初始化Redis连接池
*/
public JedisPool getJedisPool(){
JedisPoolConfig config = new JedisPoolConfig();
//省略具体设置
JedisPool myJedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
return myJedisPool;
}
}
这里把一些参数设置省略了,只保留了最重要的url,port,password3个属性,详细可下载源码查看。
当前类会将
application.properties
中的my.redis.ADDR,my.redis.PORT ,my.redis.AUTH 绑定到对应的属性中,并且提供了一个创建连接池的方法。
5.新建redis操作模板类
新建
JedisTemplete.java
public class JedisTemplete {
private JedisPool jedisPool;
public JedisPool getJedisPool() {
return jedisPool;
}
public void setJedisPool(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* 获取Jedis实例
* @return
*/
public Jedis getJedis() {
try {
if (jedisPool != null) {
Jedis resource = jedisPool.getResource();
return resource;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 释放jedis资源
* @param jedis
*/
public void close(final Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
public String getValue(String key) {
return getJedis().get(key);
}
}
使用连接池连接redis服务器,并提供了一个根据key查询value的方法。
6.新建自动化配置类
新建
MyRedisAutoConfiguration.java
@Configuration
@ConditionalOnClass(MyRedisProperties.class)
@EnableConfigurationProperties(MyRedisProperties.class)
public class MyRedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name="jedisTemplete")
public JedisTemplete jedisTemplete(MyRedisProperties myRedisProperties) {
JedisTemplete jedisTemplete = new JedisTemplete();
jedisTemplete.setJedisPool(myRedisProperties.getJedisPool());
return jedisTemplete;
}
}
- @ConditionalOnClass(MyRedisProperties.class),当存在MyRedisProperties.class时才会进行扫描。
-
@EnableConfigurationProperties(MyRedisProperties.class),进行属性绑定,当当前类被扫描时,才会去创建MyRedisProperties实例,并绑定
application.properties
中对应的属性,然后注入Spring容器。 -
jedisTemplete
方法,因为注解
@Bean
,所以在MyRedisAutoConfiguration 被扫描到时,也会扫描该方法并生成实例注入Spring容器。
public JedisTemplete jedisTemplete(MyRedisProperties myRedisProperties)
myRedisProperties参数,就是通过
@EnableConfigurationProperties(MyRedisProperties.class)
注入的
MyRedisProperties
类实例。 -
通过
myRedisProperties
获取到
JedisPool
。 -
创建
JedisTemplete
实例,并将连接池赋给它。 -
返回创建的
JedisTemplete
实例,因为注解
@Bean
,所以会将其注入Spring容器,对应类为
JedisTemplete
。
7.新建spring.factories配置文件
新建src/main/resources/META-INF/spring.factories文件。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.auto.config.MyRedisAutoConfiguration
写入刚才创建的
MyRedisAutoConfiguration
类全名,注意前半段为
EnableAutoConfiguration
,这样在Spring Boot启动时,才会在配置文件中扫描到
MyRedisAutoConfiguration
,进而去扫描该类。
8.测试
(1)新建一个Spring Boot项目,在pom中引入刚才创建的maven项目。
<dependency>
<groupId>com.my.redis</groupId>
<artifactId>my-redis-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
(2)创建一个Controller,用来查看
jedisTemplete
是否被注入,能否连接到redis服务器并获取到数据。
@RestController
@RequestMapping(value="/print")
public class PrintController {
@Autowired
private JedisTemplete jedisTemplete;
@ResponseBody
@RequestMapping(value="/getRedis")
public String getRedis() {
return jedisTemplete.getValue("123");
}
}
(3)配置
application.properties
添加配置信息
my.redis.ADDR=redis服务器的url
my.redis.PORT=端口
my.redis.AUTH=密码
(4)然后让我们先在redis中存一个值,key:123,value:321。
(5)启动测试
成功获取。
总结:自定义starter时
1.首先需要在pom中引入
spring-boot-autoconfigure
2.新建
XXXAutoConfiguration
,然后引入需要注入的类。
3.配置
spring.factories
。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=XXXAutoConfiguration类全路径
//当有多个时
org.springframework.boot.autoconfigure.EnableAutoConfiguration=/
A,/
B,/
C