接上一篇,继续分析SpringBoot自动配置原理
接下来的方法是:
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
追个方法--->
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
继续追SpringFactoriesLoader类中的loadFactoryNames这个方法--->
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(urls.hasMoreElements()) {
URL 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 var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
可以看到这个方法是在,是在读取META-INF/spring.factories文件的内容,然后将其以list的形式返回过来:
我们看一下spring.factories文件的内容:
下一个方法,见名知意,是去除重复的类,然后再排序--->
configurations = this.removeDuplicates(configurations);
configurations = this.sort(configurations, autoConfigurationMetadata);
下一个方法,获取需要排除的类--->
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
下一个方法,核对一下,然后把该排除的移除掉--->
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
下一个方法,过滤--->
configurations = this.filter(configurations, autoConfigurationMetadata);
追一下filter方法--->
private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = configurations.toArray(new String[configurations.size()]);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<String>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
+ " ms");
}
return new ArrayList<String>(result);
}
这里面有个关键方法:getAutoConfigurationImportFilters,追一下--->
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
this.beanClassLoader);
}
这里传入了一个参数AutoConfigurationImportFilter类,是个过滤器,追一下--->
package org.springframework.boot.autoconfigure;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
/**
* Filter that can be registered in {@code spring.factories} to limit the
* auto-configuration classes considered. This interface is designed to allow fast removal
* of auto-configuration classes before their bytecode is even read.
* @author Phillip Webb
* @since 1.5.0
*/
public interface AutoConfigurationImportFilter {
/**
* Apply the filter to the given auto-configuration class candidates.
* @param autoConfigurationClasses the auto-configuration classes being considered.
* Implementations should not change the values in this array.
* @param autoConfigurationMetadata access to the meta-data generated by the
* auto-configure annotation processor
* @return a boolean array indicating which of the auto-configuration classes should
* be imported. The returned array must be the same size as the incoming
* {@code autoConfigurationClasses} parameter. Entries containing {@code false} will
* not be imported.
*/
boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata);
}
我们看一下这个类的作用:
这是一个可以在spring.factories文件中注册的过滤器,用来限制自动配置类;该接口的设计目的是允许在读取字节码之前快速删除自动配置类。
在上面的spring.factories文件中,我们可以看到:
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
这里注册了过滤的条件:OnClassCondition,意思是当Classpath里有指定的类。
Spring Boot提供的条件化注解有以下几种,当一个类上有以下某种注解时,需要满足此种注解时,才会自动配置。
原来96个,过滤完还剩下20个了
下一个方法,触发自动导入配置的事件--->
this.fireAutoConfigurationImportEvents(configurations, exclusions);
追一下这个方法--->
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
if(!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
Iterator var5 = listeners.iterator();
while(var5.hasNext()) {
AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
this.invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
这个fireAutoConfigurationImportEvents方法,就把前面读出来之后经过处理的需要自动配置的类都配置了。
最后,再详细分析一下,这个所谓的"约定大于配置";
在前面导入spring.factories中,我们随便复制一段自动配置类代码:
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
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.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
我们看一下其中的
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
我们找到这个目录,然后打开,会发现里面有几个类:
我们截取这个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<RedisSentinelConfiguration> sentinelConfiguration,
ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {
this.properties = properties;
this.sentinelConfiguration = sentinelConfiguration.getIfAvailable();
this.clusterConfiguration = clusterConfiguration.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory()
throws UnknownHostException {
return applyProperties(createJedisConnectionFactory());
}
}
我们看到有如下的注解:
@Configuration
这个注解,表明它从其他配置类里导入了一些额外配置。
@EnableConfigurationProperties(RedisProperties.class)
这个表明,可以从这个RedisProperties中自动获取配置,这个文件中是一些默认的配置参数。
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
这个注解,表明:这个自动化配置起作用的条件是当Classpath中必须存在JedisConnection.class, RedisOperations.class, Jedis.class 这三个类,否则,不会自动化配置。
@ConditionalOnMissingBean(RedisConnectionFactory.class)
这个注解,表明:当不存在这个RedisConnectionFactory bean时,才会创建一个Bean。
Spring Boot提供的其他自动配置类也有很多知识没有提到。但这已经足以说明Spring Boot如何利用条件化配置实现自动配置。
至此,完毕。