SpringBoot自动装配原理详解
- 这里我们以redis的集成为例,先体会下有自动装配和没有自动装配功能的代码的配置
不使用自动装配功能
(1)加入配置
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
- 观察上面的依赖我们可以看到我们并没有导入redis自动装配的相关依赖,导入的只是redis的最基本的依赖。
(2)配置xml的bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!--//配置连接池-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="minIdle" value="10"></property>
<property name="maxTotal" value="20"></property>
</bean>
<!--//配置连接工厂-->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="127.0.0.1"></property>
<property name="password" value=""></property>
<property name="database" value="0"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
<!--//配置 redisTemplate 模版类-->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!! -->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
</bean>
</beans>
- 观察上面的xml,我们发现,我们配置了redis的连接池,redis的工厂(配置相关的连接信息),配置redisTemplate的模板类(在里面配置了其存储的类型,如果不规定存到redis中是二进制形式的,看不懂)。
(3)利用@ImportResource,在启动类中导入对应的xml文件
package com.xiyou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
/**
* @author 92823
*/
@SpringBootApplication
// 导入xml文件
@ImportResource(locations = "classpath:beans.xml")
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class, args);
}
}
(4)写一个启动类测试
package com.xiyou.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 92823
*/
@RestController
public class RedisController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("/setRedis")
public String testRedis() {
redisTemplate.opsForValue().set("key", "value");
return "ok";
}
}
- 该controller的作用就是直接放一个值到redis中。
- 结果:
我们可以看到已经存储到redis中,并且没有出现乱码
使用自动装配功能
- 我们使用自动装配功能的时候,根本不需要大量的配置,只是通过简单的配置就行。
(1)导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
(2)导入yml配置文件
server:
port: 8889
# 配置redis的相关信息
spring:
redis:
host: 127.0.0.1
port: 6379
(3)写我们相关的配置类
package com.xiyou.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author 92823
*/
@Configuration
public class RedisConfig {
/**
* 设置格式化,避免key和value格式不对
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// 跟我们之前配置的beans.xml中的类似
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
我们先自己阐述几个注解
通过@Import注解来导入ImportSelector组件
- 写一个配置类在配置类上标注一个@import注解
- 在@Import注解的value值,写入自己需要导入的组件,这里我们value中指定的类实现了implements ImportSelector类,重写其selectImports方法,返回我们需要导入的类名的全路径
- 代码实现:
(1)配置类,在上面加上@Import注解
package com.xiyou.conf;
import com.xiyou.util.XiYouSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 测试注解
* @author 92823
*/
@Configuration
@Import(value = {XiYouSelector.class})
public class XiyouConfig {
}
(2)@Import中的类是XiYouSelector
package com.xiyou.util;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* 实现ImportSelector,重写selectImports方法 ,返回导入的类的全路径
*/
public class XiYouSelector implements ImportSelector {
/**
* 该方法返回需要导入的类的全路径
* @param annotationMetadata
* @return
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.xiyou.service.XiYouService"};
}
}
我们看到这里我们实现了ImportSelector 类,重写了selectImports方法,该方法返回一个全路径,Spring会加载这个全路径代表的类,将其放到Spring容器中。
(3)XiYouService
package com.xiyou.service;
/**
* @author 92823
* 这里不需要注入到Spring中,就是不需要@Service注解,我们这里用@Import导入的
*/
public class XiYouService {
/**
* 测试方法
*/
public void testService() {
System.out.println("我是通过ImportSelector方法导入进来的Service");
}
}
注意:这里我们并没有在该Service上写@Service注解,因为这里我们是利用ImportSelector将其注入进去的。
(4)写一个Controller,测试
package com.xiyou.controller;
import com.xiyou.service.XiYouService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 92823
*/
@RestController
public class XiYouController {
@Autowired
private XiYouService xiYouService;
@GetMapping("/testService")
public String testService() {
xiYouService.testService();
return "ok";
}
}
(5)结果:正常打印,我们发现,该Service对象已经注入进去,正常调用其方法
通过@Import注解来导入ImportBeanDefinitionRegistrar 从而导入组件
- 跟上面导入ImportSelector组件类似
- 实现ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions方法,这里有点不同,ImportSelector返回的是一个类的全路径,而这里是直接将其注入到容器中
- 代码实现:
(1)配置类:
package com.xiyou.conf;
import com.xiyou.util.TestImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 测试ImportBeanDefinitionRegistrar 导入组件
* @author 92823
*/
@Configuration
@Import(value = {TestImportBeanDefinitionRegistrar.class})
public class TestConfig {
}
我们在上面导入了TestImportBeanDefinitionRegistrar类
(2)TestImportBeanDefinitionRegistrar:
package com.xiyou.util;
import com.xiyou.service.TestService;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
* 测试导入类 实现ImportBeanDefinitionRegistrar类,重写registerBeanDefinitions方法
* 主动将类对象注入进Spring容器中
* @author 92823
*/
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* 该方法用来将我们需要的对象注入进Spring容器中去
* @param annotationMetadata
* @param beanDefinitionRegistry
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// 将我们的类定义成一个Bean
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestService.class);
// 注入到Spring容器中 名字是testService
beanDefinitionRegistry.registerBeanDefinition("testService", rootBeanDefinition);
}
}
这个类和ImportSelector的区别就是一个返回的是全路径,一个是直接将其加载完成。ImportBeanDefinitionRegistrar 的作用是直接将我们需要的对象自己放到容器中,然后规定姓名放到Spring中。
(3)TestService,这个类也没有写@Service,我们利用ImportBeanDefinitionRegistrar 直接注入
package com.xiyou.service;
/**
* 利用ImportBeanDefinitionRegistrar 来导入组件
*/
public class TestService {
public void sayHello() {
System.out.println("我已经被ImportBeanDefinitionRegistrar成功导入进去");
}
}
(4)写一个Controller来测试
package com.xiyou.controller;
import com.xiyou.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 92823
*/
@RestController
public class TestController {
@Autowired
private TestService testService;
@GetMapping("/sayHello")
public String testHello() {
testService.sayHello();
return "ok";
}
}
这里我们利用@Autowrited注入对象,如果我们ImportBeanDefinitionRegistrar 导入成功,我们就可以注入进该对象
(5)结果演示
spring的底层条件装配的原理@Conditional
- 举例说明:比如我有两个组件,一个是Log,一个是Aspect,而Log依赖于Aspect,只有当容器中已经加载了Aspect的时候才会加载Log
(1)这里就可以用Condition类。我们继承这个类,然后重写matched方法
(2)利用注解@Conditional实现功能 - 代码实现:
(1)先定义两个Service,注意这里的Service我们不在上面写@Service,一会用@Bean注入
LogService
package com.xiyou.service;
import org.springframework.stereotype.Service;
/**
* @author 92823
*/
public class LogService {
}
AspectService
package com.xiyou.service;
import org.springframework.stereotype.Service;
/**
* @author 92823
*/
public class AspectService {
}
(2)自定义注解,该注解的作用是当前容器中有名字是aspectService的bean的时候,才允许执行
package com.xiyou.conf;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 自定义条件注解,这里不用写@Component,因为我们利用@Conditional注解注入进去
*/
public class MyConditional implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 如果当前的容器中存在名字是aspectService的bean,则返回true,否则返回false
if (conditionContext.getBeanFactory().containsBean("aspectService")) {
return true;
}
return false;
}
}
(3)测试:
下面两个对象都可以创建
package com.xiyou.conf;
import com.xiyou.service.AspectService;
import com.xiyou.service.LogService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
*
* @author 92823
*/
@Configuration
public class ConditionConfig {
/**
* 注意:这个是有顺序的,如果Log的先放执行,则不会创建,因为创建Log的时候,Aspect还不在容器中
* @return
*/
@Bean
public AspectService aspectService() {
System.out.println("AspectService...");
return new AspectService();
}
@Bean
// 只有当容器中有AspectService中的对象的时候,才会创建(返回true)
@Conditional(value = MyConditional.class)
public LogService logService() {
System.out.println("LogService....");
return new LogService();
}
}
这是因为我们在创建LogService之前,先创建好了AspectService,注意这里写的时候也应该注意顺序,先写AspectService,再写LogService
下面无法创建LogService对象,因为容器中没有AspectService(上面没写@Bean,没有将其注入)
package com.xiyou.conf;
import com.xiyou.service.AspectService;
import com.xiyou.service.LogService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
*
* @author 92823
*/
@Configuration
public class ConditionConfig {
public AspectService aspectService() {
System.out.println("AspectService...");
return new AspectService();
}
@Bean
// 只有当容器中有AspectService中的对象的时候,才会创建(返回true)
@Conditional(value = MyConditional.class)
public LogService logService() {
System.out.println("LogService....");
return new LogService();
}
}
下面无法创建LogService对象,因为容器中创建LogService的时候,还没有创建AspectService
package com.xiyou.conf;
import com.xiyou.service.AspectService;
import com.xiyou.service.LogService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
*
* @author 92823
*/
@Configuration
public class ConditionConfig {
@Bean
// 只有当容器中有AspectService中的对象的时候,才会创建(返回true)
@Conditional(value = MyConditional.class)
public LogService logService() {
System.out.println("LogService....");
return new LogService();
}
/**
* 注意:这个是有顺序的,如果Log的先放执行,则不会创建,因为创建Log的时候,Aspect还不在容器中
* @return
*/
@Bean
public AspectService aspectService() {
System.out.println("AspectService...");
return new AspectService();
}
}
分析SpringBot自动注解原理
- 上图是我们的@SpringBootApplication的主要包含组件,其中@EnableAutoConfiguration非常重要,该注解下面的@AutoConfigurationImportSelector是我们自动注入的核心。
- 详细查看AutoConfigurationImportSelector注解:
- 分析@EnableAutoConfiguration
上面是该注解的代码,我们可以发现该注解上加入了@Import注解,点进里面的类 - 观察@Import中的AutoConfigurationImportSelector
- 如我们之前提到的这个注解,该注解中的类会重写selectImports方法,返回一堆类的全路径,我们会将这些类加载进Spring容器中。如下所示,关键步骤的解析,观察上面的注释:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 去META-INF/spring.factories文件中,查询EnableAutoConfiguration的值
// 就是把所有的AutoConfiguration的类取出来
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 去除重复的配置的类,避免我们自己写了重复的starter
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 根据maven导入的启动器过滤出需要导入的配置类
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回类加载的全路径,一个字符串数组
return StringUtils.toStringArray(configurations);
}
- 我们可以随便看一个META-INF下的spring.factories文件
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer
# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
- 看一下加载的配置
- 我们以Redis的自动配置为例,分析进去
/*
* Copyright 2012-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.redis;
import java.net.UnknownHostException;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Christian Dupuis
* @author Christoph Strobl
* @author Phillip Webb
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Marco Aust
* @author Mark Paluch
*/
@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;
}
}
- 该类主要做了配置RedisTemplate
- 观察该类上面的注解:
(1)@ConditionalOnClass(RedisOperations.class)
(2)@EnableConfigurationProperties(RedisProperties.class)
(3)@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
@ConditionalOnClass(RedisOperations.class)
- 该注解的意思就是当我们Spring容器中有RedisOperations类的时候,我们才会生效。这个类在我们导入redis的相关依赖的时候就已经导入了。所以这个是满足的
@EnableConfigurationProperties(RedisProperties.class)
- @EnableConfigurationProperties注解的作用是开启@ConfigurationProperties注解
- 观察RedisProperties类
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.redis;
import java.time.Duration;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for Redis.
*
* @author Dave Syer
* @author Christoph Strobl
* @author Eddú Meléndez
* @author Marco Aust
* @author Mark Paluch
* @author Stephane Nicoll
*/
@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();
public int getDatabase() {
return this.database;
}
public void setDatabase(int database) {
this.database = database;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public String getHost() {
return this.host;
}
public void setHost(String host) {
this.host = host;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public int getPort() {
return this.port;
}
public void setPort(int port) {
this.port = port;
}
public boolean isSsl() {
return this.ssl;
}
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
public void setTimeout(Duration timeout) {
this.timeout = timeout;
}
public Duration getTimeout() {
return this.timeout;
}
public Sentinel getSentinel() {
return this.sentinel;
}
public void setSentinel(Sentinel sentinel) {
this.sentinel = sentinel;
}
public Cluster getCluster() {
return this.cluster;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
public Jedis getJedis() {
return this.jedis;
}
public Lettuce getLettuce() {
return this.lettuce;
}
/**
* Pool properties.
*/
public static class Pool {
/**
* Maximum number of "idle" connections in the pool. Use a negative value to
* indicate an unlimited number of idle connections.
*/
private int maxIdle = 8;
/**
* Target for the minimum number of idle connections to maintain in the pool. This
* setting only has an effect if it is positive.
*/
private int minIdle = 0;
/**
* Maximum number of connections that can be allocated by the pool at a given
* time. Use a negative value for no limit.
*/
private int maxActive = 8;
/**
* Maximum amount of time a connection allocation should block before throwing an
* exception when the pool is exhausted. Use a negative value to block
* indefinitely.
*/
private Duration maxWait = Duration.ofMillis(-1);
public int getMaxIdle() {
return this.maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMinIdle() {
return this.minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxActive() {
return this.maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public Duration getMaxWait() {
return this.maxWait;
}
public void setMaxWait(Duration maxWait) {
this.maxWait = maxWait;
}
}
/**
* Cluster properties.
*/
public static class Cluster {
/**
* Comma-separated list of "host:port" pairs to bootstrap from. This represents an
* "initial" list of cluster nodes and is required to have at least one entry.
*/
private List<String> nodes;
/**
* Maximum number of redirects to follow when executing commands across the
* cluster.
*/
private Integer maxRedirects;
public List<String> getNodes() {
return this.nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
public Integer getMaxRedirects() {
return this.maxRedirects;
}
public void setMaxRedirects(Integer maxRedirects) {
this.maxRedirects = maxRedirects;
}
}
/**
* Redis sentinel properties.
*/
public static class Sentinel {
/**
* Name of the Redis server.
*/
private String master;
/**
* Comma-separated list of "host:port" pairs.
*/
private List<String> nodes;
public String getMaster() {
return this.master;
}
public void setMaster(String master) {
this.master = master;
}
public List<String> getNodes() {
return this.nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
/**
* Jedis client properties.
*/
public static class Jedis {
/**
* Jedis pool configuration.
*/
private Pool pool;
public Pool getPool() {
return this.pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
}
/**
* Lettuce client properties.
*/
public static class Lettuce {
/**
* Shutdown timeout.
*/
private Duration shutdownTimeout = Duration.ofMillis(100);
/**
* Lettuce pool configuration.
*/
private Pool pool;
public Duration getShutdownTimeout() {
return this.shutdownTimeout;
}
public void setShutdownTimeout(Duration shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
public Pool getPool() {
return this.pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
}
}
- 我们可以发现这个类的作用就是写一堆属性,然后上面加上了@ConfigurationProperties(prefix = “spring.redis”),这个类的作用就是将yml文件中以spring.redis开头的配置自动注入进去,这里就是为什么我们在yml中写了redis的密码等可以自动配置进去的原因
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
- 写到这里我们可能会有疑问,我们在xml中还配置了redis的工厂以及redis连接池,为什么这里我们没有配置?
- 别慌,这里的@Import导入的类的作用就是导Redis的工厂和连接池的
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.redis;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import org.apache.commons.pool2.impl.GenericObjectPool;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.util.StringUtils;
/**
* Redis connection configuration using Jedis.
*
* @author Mark Paluch
* @author Stephane Nicoll
*/
@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();
}
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();
}
private JedisClientConfigurationBuilder applyProperties(
JedisClientConfigurationBuilder builder) {
if (this.properties.isSsl()) {
builder.useSsl();
}
if (this.properties.getTimeout() != null) {
Duration timeout = this.properties.getTimeout();
builder.readTimeout(timeout).connectTimeout(timeout);
}
return builder;
}
private void applyPooling(RedisProperties.Pool pool,
JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
builder.usePooling().poolConfig(jedisPoolConfig(pool));
}
private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(pool.getMaxActive());
config.setMaxIdle(pool.getMaxIdle());
config.setMinIdle(pool.getMinIdle());
if (pool.getMaxWait() != null) {
config.setMaxWaitMillis(pool.getMaxWait().toMillis());
}
return config;
}
private void customizeConfigurationFromUrl(
JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
if (connectionInfo.isUseSsl()) {
builder.useSsl();
}
}
private void customize(
JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
for (JedisClientConfigurationBuilderCustomizer customizer : this.builderCustomizers) {
customizer.customize(builder);
}
}
}