1.自动配置的概念
随着Ruby,Groovy等动态语言的流行,相比之下,Java的开发显得格外的笨重;配置很繁重,部署也很复杂,记得之前刚开始接触SpringMvc的时候xml文件好多好大,随着项目越来越大,xml文件越来越多,很容易出错;现在SpringBoot简化了Spring的搭建和开发过程,个人觉得SpringBoot最大的优势就是starter简化配置和Spring的自动配置;
使用starter简化依赖的配置:核心原理是Maven和Gradle的依赖传递,当在gradle中添加某个starter时,starter的依赖也会自动的传递依赖进来;
SpringBoot的自动配置:根据类路径中jar包,类,自动配置对应的bean,极大减少了配置的数量;简单说就是会根据gradle或者maven中的类,自动给你生成一些bean,加载到Spring的context中;自动配置充分利用了Spring 4.0的条件配置特性,能够自动配置特定的Spring Bean,启动某些特性;
2.SpringBoot中的自动配置原理
1>首先看SpringBoot应用程序入口
@SpringBootApplication是组合注解,其中包含内容如下
自动配置主要起作用的是@EnableAutoConfiguration;
2>EnableAutoConfiguration
3>import了EnableAutoConfigurationImportSelector,代码如下
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static final String[] NOIMPORTS = {};
private static final Log logger = LogFactory
.getLog(AutoConfigurationImportSelector.class);
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
这里重写了ImportSelector的selectImports方法,看注释这个方法的作用是返回哪些类应该被导入的
public interface ImportSelector {
Select and return the names of which class(es) should be imported based on
the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
来看下这里调用的getCandidateConfigurations方法代码
/
Return the auto-configuration class names that should be considered. By default
this method will load candidates using {@link SpringFactoriesLoader} with
{@link #getSpringFactoriesLoaderFactoryClass()}.
@param metadata the source metadata
@param attributes the {@link #getAttributes(AnnotationMetadata) annotation
attributes}
@return a list of candidate configurations
/_
protected List
AnnotationAttributes attributes) {
List
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
“No auto configuration classes found in META-INF/spring.factories. If you “
+ “are using a custom packaging, make sure that file is correct.”);
return configurations;
}
根据注释来看,这个方法返回自动配置的类名,默认这个方法会加载候选的类根据getSpringFactoriesLoaderFactoryClass()方法;关键的方法在这里了,我们看下loadFactoryNames方法:
public static List
String factoryClassName = factoryClass.getName();
try {
Enumeration
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException(“Unable to load [“ + factoryClass.getName() +
“] factories from location [“ + FACTORIES_RESOURCE_LOCATION + “]”, ex);
}
}
这里的FACTORIES_RESOURCE_LOCATION内容如下所示:
public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;
我们来看下这个文件对应的内容
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
# 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,\
………………….
这个就是自动配置的列表,也就是SpingBoot默认会加载这些自动配置的类;下面我们以redis为例来说明,具体是怎么自动配置的;
根据这个,我们来对应的目录找到RedisAutoConfiguration
这里截取部分代码如图所示
@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
/**
* Redis connection configuration.
*/
@Configuration
@ConditionalOnClass(GenericObjectPool.class)
protected static class RedisConnectionConfiguration {
private final RedisProperties properties;
private final RedisSentinelConfiguration sentinelConfiguration;
private final RedisClusterConfiguration clusterConfiguration;
public RedisConnectionConfiguration(RedisProperties properties,
ObjectProvider
ObjectProvider
this.properties = properties;
this.sentinelConfiguration = sentinelConfiguration.getIfAvailable();
this.clusterConfiguration = clusterConfiguration.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory()
throws UnknownHostException {
return applyProperties(createJedisConnectionFactory());
}
这个类进行了简单的Spring配置,声明了Redis所需典型Bean,和其它很多类一样,重度依赖于Spring Boot注释:
1)@ConditionOnClass**一个配置,当类路径中存在这个类时才会配置该类
2)@EnableConfigurationProperties自动映射一个POJO到Spring Boot配置文件(默认是application.properties文件)的属性集。
3)@ConditionalOnMissingBean启用一个Bean定义,但必须是这个Bean之前未定义过才有效。
4>这里了解下condition的几个注解
1、@ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean
2、@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean),该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类
3、@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean
4、@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean,该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean,可以给该注解传入参数例如@ConditionOnMissingBean(name = “example”),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行
5>Properties系列注释
@EnableConfigurationProperties
@ConfigurationProperties(prefix = “may”)
在需要注入配置的类上加上这个注解,prefix的意思是,以该前缀打头的配置,以下是例子
@ConfigurationProperties(prefix = “may”)
public class User {
private String name;
private String gender;
}
application.yml中的配置
may
name: youjie
gender: man
