SpringBoot自动配置详解与源码分析

一、SpringBoot的自动配置原理

简介

我们都知道SpringBoot使用起来非常方便,简单的一个@SpringBootApplication注解和一个applicatin.yml文件就能使一个web项目运行起来。那么我们有没有思考过这其中的原理呢?它是如何做到的呢?

我们知道,一些常用的配置如RestTemplate等,我们竟然可以直接使用@Autowired注解,从Spring容器中拿来使用。那么SpringBoot中又是如何实现自动配置的呢?

所以今天来我们来了解一下SpringBoot是如何实现自动配置的。

自动配置流程图

https://www.processon.com/view/link/5fc0abf67d9c082f447ce49b

自动配置原理

。。。

二、SpringBoot自动配置原理源码解析

我们知道,SpringBoot中最终要的注解应该就是@SpringBootApplication注解,接下来我们通过源码详细的了解一下这个注解。

@SpringBootApplication注解

@SpringBootApplication注解是一个复合注解,里面包含了很多个其他注解:

@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 {
	......
}
  • @Target(ElementType.TYPE):用于描述注解的使用范围,TYPE表示用于描述类、接口(包括注解类型) 或enum声明
  • @Retention(RetentionPolicy.RUNTIME):当注解标注的类编译以什么方式保留,RetentionPolicy.RUNTIME表明注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
  • @Documented:java doc 会生成注解信息
  • @Inherited:@Inherited
  • @SpringBootConfiguration:Spring Boot的配置类,标注在某个类上,表示这是一个Spring Boot的配置类
  • @EnableAutoConfiguration开启自动配置功能。以前我们需要配置的东西,Spring Boot帮我们自动配置。@EnableAutoConfiguration告诉SpringBoot开启自动配置,会帮我们自动去加载自动配置类
  • @ComponentScan:扫描包 。相当于在spring.xml 配置中< context:comonent-scan> 但是并没有指定basepackage,如果没有指定spring底层会自动扫描当前配置类所有在的包

TypeExcludeFilter:springboot对外提供的扩展类, 可以供我们去按照我们的方式进行排除
AutoConfigurationExcludeFilter:排除所有配置类并且是自动配置类中里面的其中一个

这里比较重要的是@SpringBootConfiguration注解和@EnableAutoConfiguration注解。下面我们来重点看一下这两个注解。

@EnableAutoConfiguration注解深入理解

@EnableAutoConfiguration注解从字面意思可以看出来,这是一个开启自动配置的注解。我们打开这个注解会发现该该注解也是一个复合注解:

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

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

@AutoConfigurationPackage

我们继续跟进去,打开该注解的源码实现:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

	/**
	 * Base packages that should be registered with {@link AutoConfigurationPackages}.
	 * <p>
	 * Use {@link #basePackageClasses} for a type-safe alternative to String-based package
	 * names.
	 * @return the back package names
	 * @since 2.3.0
	 */
	String[] basePackages() default {};

	/**
	 * Type-safe alternative to {@link #basePackages} for specifying the packages to be
	 * registered with {@link AutoConfigurationPackages}.
	 * <p>
	 * Consider creating a special no-op marker class or interface in each package that
	 * serves no purpose other than being referenced by this attribute.
	 * @return the base package classes
	 * @since 2.3.0
	 */
	Class<?>[] basePackageClasses() default {};

}

可以看到,@AutoConfigurationPackage注解的主要作用是扫描配置的包路径(如果不写,默认是扫描该注解所在包路径),并将其中加了Spring相关注解的类解析注入成对应的Bean。

就是注册了一个保存当前配置类所在包的一个Bean。

@Import(AutoConfigurationImportSelector.class)

在了解这个注解整体作用之前,我们先来了解一下AutoConfigurationImportSelector这个类。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	......	 

可以看到AutoConfigurationImportSelector这个类实现了接口DeferredImportSelector接口(延迟导入选择器)。我们再来看一下这个DeferredImportSelector接口究竟是什么:

public interface DeferredImportSelector extends ImportSelector {
    @Nullable
    default Class<? extends DeferredImportSelector.Group> getImportGroup() {
        return null;
    }

    public interface Group {
        void process(AnnotationMetadata var1, DeferredImportSelector var2);

        Iterable<DeferredImportSelector.Group.Entry> selectImports();
	......

可以看到,DeferredImportSelector 也是一个接口,而且这个接口也实现了ImportSelector接口。

现在接口有点多,我们先理清关系:

AutoConfigurationImportSelector <— DeferredImportSelector <— ImportSelector

所以说我们现在要先搞清楚这个ImportSelector的作用。

1、理解ImportSelector

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);

    @Nullable
    default Predicate<String> getExclusionFilter() {
        return null;
    }
}

可以看到,ImportSelector接口中定义有一个selectImports方法和getExclusionFilter方法。从方法名上我们先猜测一下他们的作用。

selectImports 方法返回结果为一个String数组,很有可能是返回要选择注册成为Bean的类全限定名称数组。
getExclusionFilter 方法感觉像是获取排除过滤器,即某些特定类可以通过这种过滤的方式不被注册到Spring容器中。

接下来我们看看DeferredImportSelector 这个接口具体是怎么实现这两个方法的。

2、 理解DeferredImportSelector

DeferredImportSelector从接口名称可以知道这是一个延迟导入选择器。下面的实现内容有点多,可以先简单过一下,不必都搞清楚每一个点。

public interface DeferredImportSelector extends ImportSelector {
    @Nullable
    default Class<? extends DeferredImportSelector.Group> getImportGroup() {
        return null;
    }

	// 这里又定义了一个Group接口,
    public interface Group {
       .....
    }    
}

简单看来,首先是内部又自己定义了一个Group接口,外面还有一个getImportGroup方法。

我们继续再来研究最终的AutoConfigurationImportSelector类。

3、理解AutoConfigurationImportSelector

AutoConfigurationImportSelector,从名称看来就是自动配置导入器的意思。下面来看看它是如何实现上面的DeferredImportSelector接口的:

由于内容较多,我们只看重点方法。

3.1 selectImports中getAutoConfigurationEntry的方法1
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

selectImports我们上面说到可能是返回一些需要引入的类的全限定名集合:[com.test.entity, com.test.service…]。我们来验证一下是否正确。

我们来看一下getAutoConfigurationEntry这个方法的实现:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	// 获取所有的候选配置类
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = getConfigurationClassFilter().filter(configurations);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

<==============================================================================================>
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;
}

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    // 这里loadSpringFactories方法就会从"META-INF/spring.factories"这个配置文件中获取写好的候选配置类
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}


private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            HashMap result = new HashMap();

            try {
            	// 解析META-INF/spring.factories中的配置,解析key=value的形式。最终将其封装成一个Map<String, List<String>>
            	// 即一个key可能对应多个value
                Enumeration urls = classLoader.getResources("META-INF/spring.factories");

                while(urls.hasMoreElements()) {
                ........
                }
}                
<==============================================================================================>

总结来看,AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames() 方法 将 META-INF/spring.factories 中的配置信息扫描解析成一个Map<key, List> 。

总结来说就是获取到了spring.factories这个配置文件中的所有自动配置类的信息。

这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:
在这里插入图片描述

META-INF/spring.factories文件中列出了常用的自动配置类。这样我们引入第三方starter之后,即使自己没有配置相关的Bean,也可以直接使用@Autowired注解来使用一些Bean。

比如下面的这一段配置:key就是org.springframework.boot.autoconfigure.EnableAutoConfiguration,List就是等于号后面的所有自动配置类。


org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\



我们可以看到有一个RabbitMQ的自动配置类。也就是说我们只要引入amqp这个starer之后,不需要自己配置就可以直接使用@Autowired注入一个RabbitTemplate来直接使用。因为SpringBoot已经帮助我们自动注入了一个默认的配置。我们可以进入到org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration配置类中验证:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingBean(ConnectionFactory.class)
	protected static class RabbitConnectionFactoryCreator {

		@Bean
		public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties properties,
				ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
				ObjectProvider<CredentialsRefreshService> credentialsRefreshService,
				ObjectProvider<ConnectionNameStrategy> connectionNameStrategy) throws Exception {
			CachingConnectionFactory factory = new CachingConnectionFactory(getRabbitConnectionFactoryBean(properties,
					resourceLoader, credentialsProvider, credentialsRefreshService).getObject());
			PropertyMapper map = PropertyMapper.get();
			map.from(properties::determineAddresses).to(factory::setAddresses);
			map.from(properties::getAddressShuffleMode).whenNonNull().to(factory::setAddressShuffleMode);
			map.from(properties::isPublisherReturns).to(factory::setPublisherReturns);
			map.from(properties::getPublisherConfirmType).whenNonNull().to(factory::setPublisherConfirmType);
			RabbitProperties.Cache.Channel channel = properties.getCache().getChannel();
			map.from(channel::getSize).whenNonNull().to(factory::setChannelCacheSize);
			map.from(channel::getCheckoutTimeout).whenNonNull().as(Duration::toMillis)
					.to(factory::setChannelCheckoutTimeout);
			RabbitProperties.Cache.Connection connection = properties.getCache().getConnection();
			map.from(connection::getMode).whenNonNull().to(factory::setCacheMode);
			map.from(connection::getSize).whenNonNull().to(factory::setConnectionCacheSize);
			map.from(connectionNameStrategy::getIfUnique).whenNonNull().to(factory::setConnectionNameStrategy);
			return factory;
		}

		private RabbitConnectionFactoryBean getRabbitConnectionFactoryBean(RabbitProperties properties,
				ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
				ObjectProvider<CredentialsRefreshService> credentialsRefreshService) {
			RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
			factory.setResourceLoader(resourceLoader);
			PropertyMapper map = PropertyMapper.get();
			map.from(properties::determineHost).whenNonNull().to(factory::setHost);
			map.from(properties::determinePort).to(factory::setPort);
			map.from(properties::determineUsername).whenNonNull().to(factory::setUsername);
			map.from(properties::determinePassword).whenNonNull().to(factory::setPassword);
			map.from(properties::determineVirtualHost).whenNonNull().to(factory::setVirtualHost);
			map.from(properties::getRequestedHeartbeat).whenNonNull().asInt(Duration::getSeconds)
					.to(factory::setRequestedHeartbeat);
			map.from(properties::getRequestedChannelMax).to(factory::setRequestedChannelMax);
			RabbitProperties.Ssl ssl = properties.getSsl();
			if (ssl.determineEnabled()) {
				factory.setUseSSL(true);
				map.from(ssl::getAlgorithm).whenNonNull().to(factory::setSslAlgorithm);
				map.from(ssl::getKeyStoreType).to(factory::setKeyStoreType);
				map.from(ssl::getKeyStore).to(factory::setKeyStore);
				map.from(ssl::getKeyStorePassword).to(factory::setKeyStorePassphrase);
				map.from(ssl::getTrustStoreType).to(factory::setTrustStoreType);
				map.from(ssl::getTrustStore).to(factory::setTrustStore);
				map.from(ssl::getTrustStorePassword).to(factory::setTrustStorePassphrase);
				map.from(ssl::isValidateServerCertificate)
						.to((validate) -> factory.setSkipServerCertificateValidation(!validate));
				map.from(ssl::getVerifyHostname).to(factory::setEnableHostnameVerification);
			}
			map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
					.to(factory::setConnectionTimeout);
			map.from(properties::getChannelRpcTimeout).whenNonNull().asInt(Duration::toMillis)
					.to(factory::setChannelRpcTimeout);
			map.from(credentialsProvider::getIfUnique).whenNonNull().to(factory::setCredentialsProvider);
			map.from(credentialsRefreshService::getIfUnique).whenNonNull().to(factory::setCredentialsRefreshService);
			factory.afterPropertiesSet();
			return factory;
		}

	}

	@Configuration(proxyBeanMethods = false)
	@Import(RabbitConnectionFactoryCreator.class)
	protected static class RabbitTemplateConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public RabbitTemplateConfigurer rabbitTemplateConfigurer(RabbitProperties properties,
				ObjectProvider<MessageConverter> messageConverter,
				ObjectProvider<RabbitRetryTemplateCustomizer> retryTemplateCustomizers) {
			RabbitTemplateConfigurer configurer = new RabbitTemplateConfigurer();
			configurer.setMessageConverter(messageConverter.getIfUnique());
			configurer
					.setRetryTemplateCustomizers(retryTemplateCustomizers.orderedStream().collect(Collectors.toList()));
			configurer.setRabbitProperties(properties);
			return configurer;
		}

		// 可以看到,SpringBoot帮我们自动配置好了一个rabbitTemplate!!!
		@Bean
		@ConditionalOnSingleCandidate(ConnectionFactory.class)
		@ConditionalOnMissingBean(RabbitOperations.class)
		public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
			RabbitTemplate template = new RabbitTemplate();
			configurer.configure(template, connectionFactory);
			return template;
		}
	}
.......	

可以看到,SpringBoot帮我们自动配置好了一个rabbitTemplate!!!

总结:

至此为止,我们应该弄清楚了,SpringBoot是如何实现自动配置的,也搞清楚了整个过程。现在我们可以明确,所有的自动配置类的信息都是存储在/META-INFO/spring.factories这个配置文件中的。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。其实其他很多的starter中都是存在这个spring.factories配置文件的。

这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。

我想有一点需要再次阐明一下:我们此时只是解析到了所有的配置类信息到一个Map<key,List> 中,并没有将这些类都注入到Spring容器中去。

我们自己其实也可以思考到,这里足足有127个自动配置类,SpringBoot也不会一股脑的将所有配置类都注册成一个Bean吧。而且,我们实际在项目中使用的时候,根本也不会使用到这么多的配置类。我们一般只会使用我们在maven的pom文件中引入的starter依赖的配置类才对。 比如我的项目中此时除了SpringBoot只引入了一个rabbitMQ的starter,那么SpringBoot应当只帮助我们创建一个rabbitMQ对应的自动配置类即可!

事实上,SpringBoot就是这样做的,那我们就好奇了,它是如何实现的这个功能呢?

3.2 理解SpringBoot是如何按需注册自动配置类到容器中的呢?

不知道大家有没有注意到上面RabbitAutoConfiguration 这个类中有一些特殊的注解:

@ConditionalOnClass({ RabbitTemplate.class, Channel.class })

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
....

@ConditionalOnMissingBean(ConnectionFactory.class)

@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean(RabbitOperations.class)
public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
	RabbitTemplate template = new RabbitTemplate();
	configurer.configure(template, connectionFactory);
	return template;
}

每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:

@ConditionalOnBean:当容器里有指定的bean的条件下。

@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。

@ConditionalOnClass:当类路径下有指定类的条件下。

@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。

@ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。

这些注解会在Spring处理完用户自定义的@Component、@Service、@Controller、@Configuration等注解后再生效,这里体现了延迟的特点,所以使用的是DeferredImportSelector,而不是默认的ImportSelector。

< ========================== 思路理解 ================================== >
SpringBoot中为何要定义一些这样的注解呢?

不知道大家有没有思考过这样一个问题:SpringBoot帮助我们添加了很多的自动配置类到容器中去,我们的确是可以直接拿来使用的!但是往往的情况是,我们都需要自己配置一个定制的配置类,可能并不会使用SpringBoot中提供的这个自动配置类。那么也就是说,我们自己实现的配置类应当要覆盖掉SpringBoot中存在的自动配置类,或者说当Spring容器中已经存在了一个我们自己配置的配置类之后,SpringBoot就不需要再将自己的自动配置类注册到容器中去了!!!

所以说SpringBoot是需要一些类似于判断容器中是否已有开发者配置的某个Bean、项目中是否已存在某个类(开发者在pom中是否引入了依赖)等这样的约束,所以Spring创建了一些这样条件判断的注解来帮助实现这个功能!!!

这个具体实现我们后面在源码中再来探究!

======================================================================>

那么以上面的RabbitMQ的自动配置类RabbitAutoConfiguration 为例。

类上加了 @ConditionalOnClass({ RabbitTemplate.class, Channel.class }) 注解,说明只有类路径下存在RabbitTemplate.class和Channel.class的时候,这个自动配置类才会生效!

思考这个意思,类路径下何时才会有RabbitTemplate.class呢?SpringBoot下默认肯定是没有整个类的,也就是说只有我们引入了rabbitMQ的starter第三方依赖,才有可能在类路径下存在RabbitTemplate.class这个类。
也就是说,这个RabbitAutoConfiguration自动配置类,只有当我们的pom文件中引入了amqp这个依赖的时候才会生效!

这也正好复合我们上面的问题猜想,SpringBoot按需加载自动配置类才是正确合理的!!!而不是直接加载所有,因为那时候项目中根本就没有一些类存在,所以也根本不可能完成加载。除非是一个必须要用到的类,SpringBoot自己会实现这些类,可以直接注入到容器中。

我们继续分析,RabbitAutoConfiguration类上还有一个 @EnableConfigurationProperties(RabbitProperties.class) 注解,这又是什么意思?

@EnableConfigurationProperties表示开启配置属性,而它后面的参数是一个RabbitProperties类:

@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {

	private static final int DEFAULT_PORT = 5672;

	private static final int DEFAULT_PORT_SECURE = 5671;

	/**
	 * RabbitMQ host. Ignored if an address is set.
	 */
	private String host = "localhost";

	/**
	 * RabbitMQ port. Ignored if an address is set. Default to 5672, or 5671 if SSL is
	 * enabled.
	 */
	private Integer port;

	/**
	 * Login user to authenticate to the broker.
	 */
	private String username = "guest";

	/**
	 * Login to authenticate against the broker.
	 */
	private String password = "guest";

......

这个类中我们看到了一个非常熟悉的注解:@ConfigurationProperties(prefix = “spring.rabbitmq”)。我想我们都应该知道这个注解的意思:从配置文件中获取前缀为spring.rabbitmq的配置信息。看到这里是不是感觉一下子就明白了,原来我们在配置文件中的配置属性其实都是对应一个配置类的。比如我们要配置的rabbitMQ的host,port等都对应类中的一个属性。而且,当我们不再配置文件中配置的时候,它们还都有一个默认值!

@ConfigurationProperties,它的作用就是从配置文件中绑定属性到对应的bean上,而@EnableConfigurationProperties负责导入这个已经绑定了属性的bean到spring容器中。那么所有其他的和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。

至此,我们大致可以了解。在全局配置的属性如:rabbitmq.host等,通过 @ConfigurationProperties 注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过 @EnableConfigurationProperties 注解导入到Spring容器中。

而诸多的XxxxAutoConfiguration自动配置类,就是Spring容器的JavaConfig形式,作用就是为Spring 容器导入bean,而所有导入的bean所需要的属性都通过xxxxProperties的bean来获得。


我们继续分析RabbitAutoConfiguration中RabbitTemplate上的注解:

@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean(RabbitOperations.class)
public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
	RabbitTemplate template = new RabbitTemplate();
	configurer.configure(template, connectionFactory);
	return template;
}
  • @ConditionalOnSingleCandidate(ConnectionFactory.class):表明spring容器中要有一个单例的ConnectionFactory类。
  • @ConditionalOnMissingBean(RabbitOperations.class):表明spring容器中要没有RabbitOperations.class对应的Bean的时候,这个javaConfig类型的Bean才会生效!

思考:为何要有@ConditionalOnMissingBean这个注解?

因为当我们开发者已经配置了并注册了一个配置Bean到容器中的时候,SpringBoot就不需要再将自己默认的自动配置类再注册到容器中去了!

三、总结

Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

参考文章:https://blog.csdn.net/u014745069/article/details/83820511

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值