SpringBoot---(21)核心原理:自动化配置2

上一篇,继续分析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如何利用条件化配置实现自动配置。

至此,完毕。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值