Spring Boot:通过spring-boot-starter-data-redis源码了解starter和autoconfigure模块

  • Post author:
  • Post category:其他


注:本文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



版权声明:本文为yeyinglingfeng原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。