SpringBoot自动配置原理 Spring注解方式整合redis redis.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 redis.hostname =127.0.0.1 redis.port =6379 redis.database =0 redis.pool.maxActive =600 redis.pool.maxIdle =300 redis.pool.maxWait =3000 redis.pool.testOnBorrow =true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 @Configuration @PropertySource("classpath:redis.properties") public class RedisConfig { @Value("${redis.hostname}") private String hostname; @Value("${redis.port}") private int port; @Value("${redis.database}") private int database; @Value("${redis.pool.maxActive}") private int maxActive; @Value("${redis.pool.maxIdle}") private int maxIdle; @Value("${redis.pool.maxWait}") private int maxWait; @Value("${redis.pool.testOnBorrow}") private boolean testOnBorrow; @Bean public JedisPoolConfig jedisPoolConfig () { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(maxActive); poolConfig.setMaxIdle(maxIdle); poolConfig.setMaxWaitMillis(maxWait); poolConfig.setTestOnBorrow(testOnBorrow); return poolConfig; } @Bean public JedisConnectionFactory jedisConnectionFactory () { JedisConnectionFactory factory = new JedisConnectionFactory(); factory.setHostName(hostname); factory.setPort(port); factory.setPoolConfig(jedisPoolConfig()); factory.setDatabase(database); return factory; } @Bean public RedisTemplate<String,Object> redisTemplate () { Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class); RedisTemplate<String,Object> redisTemplate = new RedisTemplate(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(jacksonSeial); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(jacksonSeial); redisTemplate.setEnableTransactionSupport(true ); redisTemplate.setConnectionFactory(jedisConnectionFactory()); return redisTemplate; } }
1 2 3 4 5 6 7 8 9 10 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.10.2</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.1.5.RELEASE</version> </dependency>
SpringBoot自动装配整合redis 1 2 3 4 5 6 7 8 9 10 @Configuration public class RedisConfig { @Bean public RedisTemplate<Object, Object> redisTemplate (RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setDefaultSerializer(new Jackson2JsonRedisSerializer(Object.class)); template.setConnectionFactory(redisConnectionFactory); return template; } }
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency >
传统整合和springboot自动装配对比
传统整合:
自己引入依赖
版本冲突
配置复杂
自动装配
起步依赖
自动配置
约定 > 配置 > 编码
SpringBoot自动配置原理 @Condition Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以根据系统中已有组件实现动态选择性的创建 Bean 操作 ,从而达到自动配置的目的。
思考: SpringBoot是如何知道要创建哪个Bean的?比如SpringBoot是如何知道要创建RedisTemplate的?
SpringBoot引入spring-data-redis坐标后,RedisAutoConfiguration配置类起作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") @ConditionalOnSingleCandidate(RedisConnectionFactory.class) public RedisTemplate<Object, Object> redisTemplate (RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean @ConditionalOnSingleCandidate(RedisConnectionFactory.class) public StringRedisTemplate stringRedisTemplate (RedisConnectionFactory redisConnectionFactory) { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }
模仿使用 判断是否存在哪些类而注入当前类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 1 、定义一个Condtion实现类public class MyCondition implements Condition { @Override public boolean matches (ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Map<String, Object> map = annotatedTypeMetadata.getAnnotationAttributes(MyConditionOnClass.class.getName()); String[] className = (String[]) map.get("value" ); System.out.println(className); for (String s : className) { try { Class<?> aClass = Class.forName(s); } catch (ClassNotFoundException e) { return false ; } } return true ; } }2 、声明一个注解,用来接收要判断是否存在的类@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(value = MyCondition.class) public @interface MyConditionOnClass { String [] value(); }3 、@Configuration public class AutoConfig { @Bean @MyConditionOnClass("com.study.springboot.Springboot02AutoconfigApplication") public Person person () { return new Person("小帅" ); } public class Person { private String name; public Person (String name) { this .name = name; } @Override public String toString () { return "Person{" + "name='" + name + '\'' + '}' ; } } }
小结 自定义条件: 第一步 定义条件类:
自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回boolean值 。 matches 方法两个参数:
context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
metadata:元数据对象,用于获取注解属性。
第二步 充当判断条件:
在初始化Bean时,使用 @Conditional(条件类.class)注解
SpringBoot 提供的常用条件注解: ConditionalOnProperty :判断配置文件中是否有对应属性和值才初始化Bean
ConditionalOnClass :判断环境中是否有对应字节码文件才初始化Bean
ConditionalOnMissingBean :判断环境中没有对应Bean实例才初始化Bean
@Enable* SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import注解导入一些配置类,实现Bean的动态加载
@Import注解 @Enable底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:
导入配置类(最先注入)
导入 ImportSelector 实现类。一般用于加载配置文件中的类(返回new String[] 可以返回配置文件的key)
直接注入类
导入 ImportBeanDefinitionRegistrar 实现类(最后注入)。
@EnableAutoConfiguration
@EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class)来加载配置类。
配置文件位置:META-INF/spring.factories ,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些配置类,初始化Bean
并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean
在本版本中(2.5.3)AutoConfigurationImportSelector实际起作用的是process()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public void process (AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s" , AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(annotationMetadata); this .autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this .entries.putIfAbsent(importClassName, annotationMetadata); } }
SpringFactoriesLoader:去spring.factories 中去查询EnableAutoConfirution类
1 2 3 4 5 6 7 8 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
最终筛选出需要自动配置的类
RedisAutoConfiguration 导入了三个组件
RedisTemplate
StringRedisTemplate
JedisConnectionConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") @ConditionalOnSingleCandidate(RedisConnectionFactory.class) public RedisTemplate<Object, Object> redisTemplate (RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean @ConditionalOnSingleCandidate(RedisConnectionFactory.class) public StringRedisTemplate stringRedisTemplate (RedisConnectionFactory redisConnectionFactory) { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }
总结 SpringBoot自动配置原理:简单来说就是通过
@SpringBootApplication ->
@EnableAutoConfiguration ->
@Import(AutoConfigurationImportSelector.class)
导入项目所有 META-INF/spring.factories 中所有org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,再通过@condition、import,@ConfigurationProperties @EnableConfigurationProperties等系列注解达到根据环境自动注入配置的目的。
具体例子可见SpringBoot04篇章