Spring Boot的自动化配置解析

Spring自动配置

Spring Boot会根据类路径中的jar包、类,为jar包里的类自动配置,这样可以极大的减少配置的数量。简单点说就是它会根据定义在classpath下的类,自动的给你生成一些Bean,并加载到Spring的Context中。自动配置充分的利用了spring 4.0的条件化配置特性,能够自动配置特定的Spring bean,用来启动某项特性。

条件注解

@Conditional注解表示在满足某种条件后才初始化一个bean或者启用某些配置。它一般用在由 @Component、 @Service、 @Configuration等注解标识的类上面,或者由 @Bean标记的方法上。如果一个 @Configuration类标记了 @Conditional,则该类中所有标识了 @Bean的方法和 @Import注解导入的相关类将遵从这些条件。

条件注解配置生效条件
@ConditionalOnBean仅在当前上下文中存在某个Bean时
@ConditionalOnClass某个class在类加载器中存在
@ConditionalOnExpression当表达式为true时
@ConditionalOnMissingBean仅仅在当前上下文中不存在某个Bean时
@ConditionalOnMissingClass在类加载器中不存在对应的类
@ConditionalOnWebApplication是web应用
@ConditionalOnNotWebApplication不是web应用
@ConditionalOnProperty应用环境中的属性存在
@ConditionalOnResource存在指定的资源文件
@ConditionalOnSingleCandidate容器中只存在一个对应的实例

SpringFactoriesLoader详解

通过SpringFactoriesLoader.loadFactoryNames()读取了ClassPath下面的META-INF/spring.factories文件。

public abstract class SpringFactoriesLoader {

/**
	 * The location to look for factories.
	 * <p>Can be present in multiple JAR files.
	 */
 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


  public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		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 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);
		}
	}


}

简单看一下spring.factories文件,它是一个典型的java properties文件,配置的格式为Key = Value形式。

spring-boot-autoconfigure-1.4.3.RELEASE.jar中的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 Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\

………………………………省略很多

其中的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值都是Spring Boot中的自动配置相关类;在启动过程中会解析对应类配置信息。每个Configuation都定义了相关bean的实例化配置。都说明了哪些bean可以被自动配置,什么条件下可以自动配置,并把这些bean实例化出来。

如果我们新定义了一个starter的话,也要在该starter的jar包中提供spring.factories文件,并且为其配置org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置类。

Spring Boot应用的启动入口

SpringBootApplication

Spring Boot的启动类说起。Spring Boot应用通常有一个入口类,入口类中有一个main方法,在main方法中使用SpringApplication.run()来启动整个应用。这个入口类要使用@SpringBootApplication注解声明。@SpringBootApplication是Spring Boot的核心注解,是一个组合注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    Class<?>[] exclude() default {};

    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

@SpringBootApplication注解主要包含 @SpringBootConfiguration@EnableAutoConfiguration等几个注解。

EnableAutoConfiguration

@EnableAutoConfiguration注解可以让Spring Boot根据类路径中的jar包依赖为当前项目进行自动配置。


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}


在注解中Import了EnableAutoConfigurationImportSelector,这就是Spring Boot自动化配置真实原因。

EnableAutoConfigurationImportSelector

@Import 注解可以普通类导入到 IoC容器中。

ImportSelector : 返回需要导入的组件的全类名数组。

EnableAutoConfigurationImportSelector会去扫描classpath下的所有spring.factories文件,然后进行bean的自动化配置。

public interface DeferredImportSelector extends ImportSelector {
}


public class EnableAutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {
		
		
		@Override
	public String[] selectImports(AnnotationMetadata metadata) {
		if (!isEnabled(metadata)) {
			return NO_IMPORTS;
		}
		try {
			AnnotationAttributes attributes = getAttributes(metadata);
			//获取自动配置的类
			List<String> configurations = getCandidateConfigurations(metadata,
					attributes);
			configurations = removeDuplicates(configurations);
			Set<String> exclusions = getExclusions(metadata, attributes);
			configurations.removeAll(exclusions);
			configurations = sort(configurations);
			recordWithConditionEvaluationReport(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}
	
	
	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;
	}
		
}




EnableAutoConfigurationImportSelector.selectImports()会在容器启动过程中执行,AbstractApplicationContext.refresh()。

Configuation

从spring-boot-autoconfigure-1.4.3.RELEASE.jar中的spring.factories文件随便找一个Configuration,看看他是如何自动加载bean的。




@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
	public static class CglibAutoProxyConfiguration {

	}

}


上面AopAutoConfiguration 在决定对哪些bean进行自动化配置的时候,使用了两个条件注解:ConditionalOnProperty和ConditionalOnClass。

只有满足这种条件的时候,对应的bean才会被创建。这样可以保证某些bean在没满足特定条件的情况下就可以不必初始化,避免在bean初始化过程中由于条件不足,导致应用启动失败。

selectImports何时执行

SpringBoot应用启动过程中解析添加@Configuration的配置类时,如果发现注解中存在@Import(ImportSelector)的情况,就会创建相应的ImportSelector对象, 并调用方法 public String[] selectImports(AnnotationMetadata annotationMetadata)。

待处理的任务

有时间可以写一篇@Configuration注解解析的相关文章,其解析器ConfigurationClassParser。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值