SpringBoot学习笔记四之【自动配置原理与举例】

版本2.1.1

springboot只有一个application的启动,我们就从这个启动开始分析,首先我们跟进注解@SpringBootApplication如下:

/** Class, interface (including annotation type), or enum declaration */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    // ...
}

这里我们看到开启了@EnableAutoConfiguration,通过字面意思我们就将要知道发生什么,下面我们看看@EnableAutoConfiguration干了什么

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // ...
}

他是通过AutoConfigurationImportSelector.class给容器导入了一些组件,让我们继续看下AutoConfigurationImportSelector到底做了些什么

/**
实现的若干个接口,有些一看名字就很熟悉,就是初始化Bean的那一套。。。
**/
public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {
    // 我们主要来看这个方法       
    @Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
        // 从META-INF/spring-autoconfigure-metadata.properties加载配置属性
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
        // 获取需要的自动配置类@
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
				autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
}

接@继续跟踪 getAutoConfigurationEntry方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 通过spring-autoconfigure-metadata.properties配置类属性获取自动配置类,如图1所示,我们继续跟进这个方法@@
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
        // 移除重复的配置类,这个版本好像没有排序那个方法了。。
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        // 检查时候有需要排除的,如果有直接干掉
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
        // 执行过滤执行配置了@OnClassCondition才生效,如图1.1所示
		configurations = filter(configurations, autoConfigurationMetadata);
        // 触发自动配置导入监听事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

图1

图1.1

接@@我们继续跟进getCandidateConfigurations,里面还有好几层,我就不一一展示,直接看最后方法

/**
这个方法会通过每个jar下的META-INF/spring.factories,获得所有的自动配置类,如图2所示
**/
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
            // FACTORIES_RESOURCE_LOCATION 的值为:META-INF/spring.factories
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

图2

下面我们找个具体配置类来具体看下,以HttpEncodingAutoConfiguration为例

// 标注为配置类,和之前spring的xml配置文件效果一样
@Configuration 
// 启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中,(prefix = "spring.http")我们在配置的key都是spring.http开始,如图3所示
@EnableConfigurationProperties(HttpProperties.class)
// 判断当前应用是否是web应用,如果是则生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 判断项目中有没有CharacterEncodingFilter这个类,编码过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)
// 如果2所示,判断配置文件中是否存在前缀为spring.http,encoding的配置
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

	private final HttpProperties.Encoding properties;

	public HttpEncodingAutoConfiguration(HttpProperties properties) {
		this.properties = properties.getEncoding();
	}

    // 给容器添加一个组件,组件的值从配置文件中获取,默认编码格式UTF-8
	@Bean
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
		return filter;
	}

	@Bean
	public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
		return new LocaleCharsetMappingsCustomizer(this.properties);
	}

	private static class LocaleCharsetMappingsCustomizer implements
			WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {

		private final HttpProperties.Encoding properties;

		LocaleCharsetMappingsCustomizer(HttpProperties.Encoding properties) {
			this.properties = properties;
		}

		@Override
		public void customize(ConfigurableServletWebServerFactory factory) {
			if (this.properties.getMapping() != null) {
				factory.setLocaleCharsetMappings(this.properties.getMapping());
			}
		}

		@Override
		public int getOrder() {
			return 0;
		}

	}

}

图3

【自定义自动配置举例】

配置类:

package com.swk.springboot.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
 */
@Component
@ConfigurationProperties(prefix = "start.info")
public class StartInfoConfig {

    private String port;

    private String name;

    public String getPort() {
        return port;
    }

    public void setPort(String port) {
        this.port = port;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "StartInfoConfig{" +
                "port='" + port + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

properties配置:

#自定义配置
start.info.port=8080
start.info.name=sunwukong

测试类:

package com.swk.springboot.springbootswk;

import com.swk.springboot.config.StartInfoConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootSwkApplicationTests {

    @Autowired
    private StartInfoConfig startInfoConfig;

    @Test
    public void test(){
        System.out.println(startInfoConfig.toString());
    }
}

执行结果:

猜你喜欢

转载自blog.csdn.net/fuyuwei2015/article/details/86662514
今日推荐