【SpringBoot】SpringBoot自动装配原理

JavaWeb进阶学习路线 》》点击免费获取

一、SpringBoot配置原理

SpringBoot是个脚手架,本质还是基于Spring,只不过它把Spring的复杂配置,通过注解的方式,化繁为简,达到开箱即用的目的。

springboot通过@enableautoconfiguration注解开启自动配置,对jar包下的spring.factories文件进行扫描,这个文件中包含了可以进行自动配置的类,当满足@condition注解指定的条件时,便在依赖的支持下进行实例化,注册到spring容器中。

通俗的来讲,我们之前在写ssm项目时候,配置了大量坐标和配置内容,搭环境的过程在项目开发中占据了大量时间,SpringBoot的最大的特点就是简化了各种xml配置内容,所以springboot的自动配置就是用注解来对一些常规的配置做默认配置,简化xml配置内容,使项目能够快速运行。

springboot核心配置原理:

  • 自动配置类都存放在spring-boot-autoconfigure-版本号.jar下的org.springframework.boot.autoconfigure中

  • 当我们在application.properties中配置debug=true后启动容器。可以看到服务器初始化的初始化配置

  • DispatcherServletAutoConfigratio注册前端控制器

  • EmbeddedServletContainerAutoConfiguration注册容器类型

  • HttpMessageConvertersAutoConfiguration注册json或者xml处理器

  • JacksonAutoConfiguration注册json对象解析器

  • 如果加入其他功能的依赖,springBoot还会实现这些功能的自动配。

springboot的自动配置的原理

Spring Boot的自动配置是一个非常强大的功能,它允许开发者通过包含一个特定的Spring Boot依赖,就可以快速地配置和启动一个生产级别的应用程序。其核心在于@EnableAutoConfiguration注解,它开启了自动配置功能,自动配置的实现主要依赖于以下几个关键点:

@EnableAutoConfiguration注解:
开启自动配置功能,通过@Import注解导入AutoConfigurationImportSelector类,
它会加载META-INF/spring.factories文件中的自动配置类。

spring.factories文件:
存放在META-INF目录下,Spring Boot的自动配置类都在这里声明。

条件注解:
如@ConditionalOnClass,@ConditionalOnMissingBean等,这些注解用来根据不同的条件决定是否要实例化某个Bean。

配置属性:
通过application.properties或application.yml文件,可以覆盖默认的配置属性。

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

2.1 SpringBootApplication注解

一个SpringBoot工程如果想运行成功,就必须有一个主程序类,即有个main函数的启动类,被@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 {

通过以上可以看出,SpringBootApplication 这个注解是个复合注解,由三大注解组成

1,@SpringBootConfiguration:
继承自Configuration,声明当前类为一个配置类,支持JavaConfig的方式进行配置。

2,@EnableAutoConfiguration:
本文重点讲解,主要用于开启自动配置,它本身也是一个复合注解。

3,@ComponentScan:
自动扫描组件,默认扫描该类所在包及其子包下所有带有指定注解的类,
将它们自动装配到bean容器中,会被自动装配的注解,
包括@Controller、@Service、@Component、@Repository等。也可以指定扫描路径。

2.2 EnableAutoConfiguration  

这个注解是帮助我们自动加载默认配置的,它里面有两个关键注解@AutoConfigurationPackage和@Import。

具体源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
​
    /**
     * Environment property that can be used to override when auto-configuration is
     * enabled.
     */
    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 {};
​
}

2.3 @Import注解

@Import(AutoConfigurationImportSelector.class)注解,导入AutoConfigurationImportSelector类。

这个类中有一个非常重要的方法——selectImports(),它几乎涵盖了组件自动装配的所有处理逻辑,包括获得候选配置类、配置类去重、排除不需要的配置类、过滤等,最终返回符合条件的自动配置类的全限定名数组。

2.3.1 selectImports()方法的内容
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   // 判断SpringBoot是否开启自动配置
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
  AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);        

  //加载自动配置类
  AutoConfigurationEntry autoConfigurationEntry = 
  getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);
   
  return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected boolean isEnabled(AnnotationMetadata metadata) {
   if (getClass() == AutoConfigurationImportSelector.class) {
      // 若调用该方法的类是AutoConfigurationImportSelector,
      //那么就获取EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY的值,默认为true
      
  return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
   }
   return true;
}
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY是什么?
我们看下它的定义:
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

看到这里,我们可以猜到这就是在配置文件application.yml或者application.properties中的配置。因此,我们可以在配置文件中来决定SpringBoot是否开启自动配置。当我们没有配置的时候,默认就是开启自动配置的。

2.3.2 getAutoConfigurationEntry()

该方法主要,获取需要自动配置的bean信息。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
   // 判断是否开启自动配置
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   // 获取@EnableAutoConfiguration注解的属性
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   // 从spring.factories文件中获取配置类的全限定名数组
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   // 去重
   configurations = removeDuplicates(configurations);
   // 获取注解中exclude或excludeName排除的类集合
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   // 检查被排除类是否可以实例化,是否被自动配置所使用,否则抛出异常
   checkExcludedClasses(configurations, exclusions);
   // 去除被排除的类
   configurations.removeAll(exclusions);
   // 使用spring.factories配置文件中配置的过滤器对自动配置类进行过滤
   configurations = getConfigurationClassFilter().filter(configurations);
   // 抛出事件
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

在这里,我们只需要知道这个getAutoConfigurationEntry()方法是用来获取需要自动配置的bean信息,以及里面每个方法做了什么,有个大概的印象就可以了。

下面会对每个方法作更近一步的讲解。

2.3.3 getAttributes()方法

获取@EnableAutoConfiguration注解属性。

protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
   String name = getAnnotationClass().getName();
   AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
   Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
         + " annotated with " + ClassUtils.getShortName(name) + "?");
   return attributes;
}
2.3.4 getCandidateConfigurations()

从spring.factories文件获取需要配置的bean.

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

getCandidateConfigurations()方法通过SpringFactoriesLoaderloadFactoryNames()方法从所有的spring.factories文件中获取需要配置的bean全限定名列表。

2.3.5 SpringFactoriesLoader

spring-core包里定义了SpringFactoriesLoader类,SpringFactoriesLoader会扫描所有jar包类路径下的META-INF/spring.factories文件,并获取指定接口的配置。

在这个类中定义了两个对外的方法:

  • loadFactories根据接口类获取其实现类的实例,这个方法返回的是对象列表。

  • loadFactoryNames根据接口获取其接口类的名称,这个方法返回的是类名的列表。

上面的两个方法的关键都是从指定的ClassLoader中获取spring.factories文件,并解析得到类名列表,具体代码如下:

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

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

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
}
每个jar包类路径下 META-INF/spring.factories,位置如下:

spring.factories文件本质上与properties文件相似,其中包含一组或多组键值对。

其中key的取值是接口的全限定名,value的取值是接口实现类的全限定名。

一个接口可以设置多个实现类,不同实现类之间使用,隔开。

具体源码如下:

# 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.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
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.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

2.4 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 {};
​
}

继续分析看到导入了一个类:AutoConfigurationPackages.Registrar.class,这个类中有一个核心方法:registerBeanDefinitions,这个方法主要是加载用户自己包下的Bean。(在启动的时候可以自行Debug)

在Registrar中的registerBeanDefinitions方法,首先获取到注解所标识的类,然后将这个类所在的包以及子包的名称放入到一个String数组当中,再将String数组中的包的所有组件导入到容器当中。 

自动装配的Bean分为两部分:

一个是用户自己定义的Bean,另一个是用户在pom文件中引入的依赖Bean。
 /**
  * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
  * configuration.
  */
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
​
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //启动的时候这里的metadata就是主启动类的全类名,会扫描与主启动类同级的包、
        //将这些包下的Bean注册到Bean容器中
            register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
        }
​
        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }
​
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

常生果

喜欢我,请支持我

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值