浅谈SpringBoot自动配置

SpringBoot最大的特点就是利用自动配置来代替过去在SpringMVC中大量使用的繁琐的XML配置模式,使我们能更加方便快捷的编写部署应用,避免我们在XML配置之中花费大量不必要的时间,专注于编码实现。微服务概念的流行,更让SpringBoot有了更为重要的角色。如图所示微服务网络中,为了让服务更细化(贯彻微服务的理念),一个大的服务背后可能由数十个甚至数百个服务为其提供支持。如何快捷的搭建部署众多的小服务呢?如果还使用“古老”的SpringMVC,大概先会被那些XML的配置搞得精疲力竭吧。而SpringBoot的自动配置,为我们提供了一个解决该问题的新方法。所谓的自动配置,顾名思义就是让框架帮助我们做一些配置,这些配置会应用框架预先定义好的默认值。例如SpringBoot 使用到了内嵌容器Tomcat,其默认端口号是8080。下面我们就通过SpringBoot的源码来浅谈下SpringBoot如何实现自动配置。
微服务网络

SpringBoot应用启动时,会通过主配置类的 main 方法进行启动。

@SpringBootApplication
public class SpringBootLoggingApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootLoggingApplication.class, args);
    }
}

主配置类上的注解 @SpringBootApplication 是SpringBoot中新增的注解,其由七个注解合并而成

@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 注解,其由注解合并而成

@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...

其中, @AutoConfigurationPackage 通过阅读源码发现其主要作用是扫描主配置类 @SpringBootApplication 所在包下及子包下所有组件,将这些组件添加到Spring容器中

@EnableAutoConfiguration 注解的核心点@Import(EnableAutoConfigurationImportSelector.class) 。这个注解利用 @Import 给Spring容器导入了 EnableAutoConfigurationImportSelector.class 这个类,查看这个类的源码其主要是继承了其父类(AutoConfigurationImportSelector)的作用,查看 AutoConfigurationImportSelector 的源码,其中与自动配置相关的主要为

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    try {
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                                                                 attributes);
        configurations = removeDuplicates(configurations);
        configurations = sort(configurations, autoConfigurationMetadata);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        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);
    }
}

其中 getCandidateConfigurations() 字面理解为获取候选配置,这个配置在下面几行代码中发挥了重要作用。因此继续深入查看 getCandidateConfigurations() 的源码

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        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()源码
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

继续查看 loadFactoryNames() 源码

//The location to look for factories.
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();//org.springframework.boot.autoconfigure.EnableAutoConfiguration
		try {
			Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			List<String> result = new ArrayList<String>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
				String propertyValue = properties.getProperty(factoryClassName);
				for (String factoryName : StringUtils.commaDelimitedListToStringArray(propertyValue)) {
					result.add(factoryName.trim());
				}
			}
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

从这里就看到,loadFactoryNames()的主要作用就是去扫描所有jar包下的 META-INF/spring.factories 的文件,将其中配置的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值加入到容器中。至此自动配置类就会生效,帮助我们进行自动配置工作,以前我们需要手动配置的功能自动配置类中都会帮我们自动配置完成。如下图所示,为spring-boot-autoconfigure包中spring.factories所配置的自动配置项
spring.factories
下面以 HttpEncodingAutoConfiguration 这个自动配置类来解释SpringBoot自动配置类功能,其部分源码

@Configuration
@EnableConfigurationProperties(HttpEncodingProperties.class)
@ConditionalOnWebApplication
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {...
  1. @Configuration表明 HttpEncodingAutoConfiguration 自动配置类是一个Spring的配置类

  2. 三个 @Conditional 是SpringBoot的条件注解,表明在符合条件下才会使用这个自动配置类

  3. @EnableConfigurationProperties 表示启用指定类的 @ConfigurationProperties功能。@ConfigurationProperties是SpringBoot新增的外部化配置功能的重要注解,能将配置文件直接映射绑定到JavaBean,方便配置。@EnableConfigurationProperties(HttpEncodingProperties.class)表示启用了HttpEncodingProperties这个类的配置功能,查看HttpEncodingProperties源码

    @ConfigurationProperties(prefix = "spring.http.encoding")
    public class HttpEncodingProperties {
    	public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
        ...
    

    @ConfigurationProperties(prefix = “spring.http.encoding”) 表示将配置文件中以spring.http.encoding为前缀的配置项绑定到HttpEncodingProperties的属性上。如果配置文件中没有该前缀的配置项,则使用HttpEncodingProperties中默认定义好的,例如DEFAULT_CHARSET=UTF-8

至此,我们大概分析了SpringBoot整个自动配置的过程。总结为:

SpringBoot主配置类的 @EnableAutoConfiguration 注解将扫描所有jar包下 META-INF/spring.factories 文件,将其中配置的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 值加入到容器中。每一个这样的XXXAutoConfigration类都是容器中的一个组件,用来实现自动配置。且每一个 XXXAutoConfigration 都会对应有XXXProperties.class,利用 @ConfigurationProperties 将配置文件中指定的配置项绑定到自动配置类上,实现默认配置和自定义配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值