我们再来看下SpringBoot应用的启动类:

查看代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import java.util.HashMap;
import java.util.Map; @SpringBootApplication
public class DemoApplication { @RequestMapping("/test")
@ResponseBody
public Map<String,String> test(){
Map<String,String> map = new HashMap<>();
map.put("key","Test");
return map;
} public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
} }

这里有一个核心的注解@SpringBootApplication,我们跟进去看一下,会看到这个是在SpringBoot1.2.0版本引进的,其中注释是这样的:

Indicates a configuration class that declares one or more @Bean methods and also triggers auto-configuration and component scanning. This is a convenience annotation that is equivalent to declaring @Configuration, @EnableAutoConfiguration and @ComponentScan.

翻译一下:标示一个配置类,声明一个或多个@Bean方法,并触发自动配置和组件扫描。这是一个便利的注解,相当于声明@Configuration, @EnableAutoConfiguration和@ComponentScan。这其实是一个注解的集合,源码看了下相当于集中了

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

三个注解。下面分三个部分来介绍:

1、@SpringBootConfiguration注解

跟进源码看一下,这个是在SpingBoot 1.4.0引入进来的注解,其主要还是在于又引进了@Configuration注解,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration

再看下注释

Indicates that a class provides Spring Boot application @Configuration. Can be used as an alternative to the Spring's standard @Configuration annotation so that configuration can be found automatically (for example in tests).
Application should only ever include one @SpringBootConfiguration and most idiomatic Spring Boot applications will inherit it from @SpringBootApplication.

翻译:标识类提供Spring Boot应用程序@Configuration。可以作为Spring的标准@Configuration注释的替代品,这样可以自动找到配置(例如在测试中)。应用程序应该只包含一个@SpringBootConfiguration,大多数习惯用法的SpringBoot应用程序都会从@SpringBootApplication继承它。

这说明,这个来源于Spring原生的@Configuration,跟进去看确实是用到了Spring的Configuration注解,这个就不继续跟了,有兴趣可以详见后续的Spring源码解析。

其实,跟到这里,会发现这个注解的作用就是在于标识这个类是能够被注册到Spring容器中的,类似于声明为一个Spring Bean而已。

2、@EnableAutoConfiguration注解

这个注解是在SpringBoot1.0.0就被引入进来了,其父注解主要是@AutoConfigurationPackage,

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

同样,我们可以看下官网是怎么说的:

Enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined. For example, if you have tomcat-embedded.jar on your classpath you are likely to want a TomcatServletWebServerFactory (unless you have defined your own ServletWebServerFactory bean).
When using @SpringBootApplication, the auto-configuration of the context is automatically enabled and adding this annotation has therefore no additional effect.
Auto-configuration tries to be as intelligent as possible and will back-away as you define more of your own configuration. You can always manually exclude() any configuration that you never want to apply (use excludeName() if you don't have access to them). You can also exclude them via the spring.autoconfigure.exclude property. Auto-configuration is always applied after user-defined beans have been registered.
The package of the class that is annotated with @EnableAutoConfiguration, usually via @SpringBootApplication, has specific significance and is often used as a 'default'. For example, it will be used when scanning for @Entity classes. It is generally recommended that you place @EnableAutoConfiguration (if you're not using @SpringBootApplication) in a root package so that all sub-packages and classes can be searched.
Auto-configuration classes are regular Spring @Configuration beans. They are located using the SpringFactoriesLoader mechanism (keyed against this class). Generally auto-configuration beans are @Conditional beans (most often using @ConditionalOnClass and @ConditionalOnMissingBean annotations).

翻译:启用Spring Application Context的自动配置,尝试猜测和配置您可能需要的bean。自动配置类通常基于您的类路径和您定义的bean应用。例如,如果你的类路径中有tomcat-embed.jar,你可能需要一个TomcatServletWebServerFactory(除非你已经定义了自己的ServletWebServerFactory bean)。当使用@SpringBootApplication时,上下文的自动配置会被自动启用,因此添加这个注释不会有额外的效果。自动配置试图尽可能地智能,当您定义更多自己的配置时,自动配置就会失效。您总是可以手动排除()任何您不想应用的配置(如果您没有访问它们,则使用excludeName())。你也可以通过spring. autoconfiguration.exclude属性来排除它们。自动配置总是在注册了用户定义的bean之后应用。被@EnableAutoConfiguration注解的类的包,通常是通过@SpringBootApplication注释的,具有特定的意义,通常被用作“默认”。例如,它将在扫描@Entity类时使用。通常建议您将@EnableAutoConfiguration(如果您不使用@SpringBootApplication)放在根包中,以便可以搜索所有的子包和类。自动配置类是常规的Spring @Configuration bean。它们是使用springfactoryesloader机制来定位的(针对这个类)。通常自动配置bean是@条件bean(最常用的是@ConditionalOnClass和@ConditionalOnMissingBean注释)。

从这里我们可以总结出几点:

  • 开启自动配置功能
  • 以前使用Spring需要配置的信息,Spring Boot帮助自动配置;
  • @EnableAutoConfiguration通知SpringBoot开启自动配置功能,这样自动配置才能生效。

跟进去继续分解下,看一下@AutoConfigurationPackage

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

我们看到是用到了AutoConfigurationPackages.Registrar.class,这个默认将主配置类(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中。如下

查看代码

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//默认将会扫描@SpringBootApplication标注的主配置类所在的包及其子包下所有组件
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
} @Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
} }

另外看一下@Import(AutoConfigurationImportSelector.class)

其实,EnableAutoConfigurationImportSelector: 导入哪些组件的选择器,将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
查看代码

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

其中重点是这个

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

会给容器中注入众多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件。我们跟进去看看:

查看代码

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 final String FACTORIES_RESOURCE_LOCATION = "META-INF/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;
}

我们可以看到:SpringBoot启动的时候从类路径下的 META-INF/spring.factories中获取EnableAutoConfiguration指定的值,并将这些值作为自动配置类导入到容器中,自动配置类就会生效,最后完成自动配置工作。EnableAutoConfiguration默认在spring-boot-autoconfigure这个包中。注:关于自动配置又是一门学问了,另外开篇讲解

3、@ComponentScan注解

这个@ComponentScan是直接继承Spring的@ComponentScan注解的,主要是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中。

总结

本文详细分析了SpringBoot的启动类注解@SpringBootApplication,当然这还涉及到底层Spring的注解,有兴趣可以自行研究或者参考后续要推出的Spring5+的源码解析。

SpringBoot源码解读系列三——引导注解的更多相关文章

  1. Alamofire源码解读系列(三)之通知处理(Notification)

    本篇讲解swift中通知的用法 前言 通知作为传递事件和数据的载体,在使用中是不受限制的.由于忘记移除某个通知的监听,会造成很多潜在的问题,这些问题在测试中是很难被发现的.但这不是我们这篇文章探讨的主 ...

  2. SpringBoot源码解读系列——开篇

    什么是SpringBoot? 定义可以参考官网:SpringBoot官网,其定义通俗易懂,这里就不赘述. 官网也给出了一个通用的SpringBoot工程样例,其中包含了这么几个元素: 1.pom依赖 ...

  3. Alamofire源码解读系列(四)之参数编码(ParameterEncoding)

    本篇讲解参数编码的内容 前言 我们在开发中发的每一个请求都是通过URLRequest来进行封装的,可以通过一个URL生成URLRequest.那么如果我有一个参数字典,这个参数字典又是如何从客户端传递 ...

  4. Alamofire源码解读系列(五)之结果封装(Result)

    本篇讲解Result的封装 前言 有时候,我们会根据现实中的事物来对程序中的某个业务关系进行抽象,这句话很难理解.在Alamofire中,使用Response来描述请求后的结果.我们都知道Alamof ...

  5. Alamofire源码解读系列(六)之Task代理(TaskDelegate)

    本篇介绍Task代理(TaskDelegate.swift) 前言 我相信可能有80%的同学使用AFNetworking或者Alamofire处理网络事件,并且这两个框架都提供了丰富的功能,我也相信很 ...

  6. Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager)

    Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 本篇主要讲解iOS开发中的网络监控 前言 在开发中,有时候我们需要获取这些信息: 手机是否联网 ...

  7. Alamofire源码解读系列(八)之安全策略(ServerTrustPolicy)

    本篇主要讲解Alamofire中安全验证代码 前言 作为开发人员,理解HTTPS的原理和应用算是一项基本技能.HTTPS目前来说是非常安全的,但仍然有大量的公司还在使用HTTP.其实HTTPS也并不是 ...

  8. Alamofire源码解读系列(九)之响应封装(Response)

    本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...

  9. Alamofire源码解读系列(十)之序列化(ResponseSerialization)

    本篇主要讲解Alamofire中如何把服务器返回的数据序列化 前言 和前边的文章不同, 在这一篇中,我想从程序的设计层次上解读ResponseSerialization这个文件.更直观的去探讨该功能是 ...

随机推荐

  1. monkey介绍及常用命令

    前置准备: adb:用来连接安卓手机和PC端的桥梁,要有adb作为两者之间的维系,才能在电脑对手机进行全面的操作.(adb push 文件路径 到手机路径  adb pull 从手机拉取到电脑) mo ...

  2. Pytest_在jenkins中使用allure报告(13)

    一.安装allure插件 点击jenkins管理-->插件管理 点击Available,在搜索框中输入allure并安装 二.配置构建命令 三.构建配置allure插件 点击构建后置操作 pat ...

  3. MYSQL实现上一条下一条功能

    select id from(select *, (@i:=@i+1) as rownum from pre_bet_zhibo,(select @i:=0) as itwhere link_cone ...

  4. i++ 和 ++i 区别

    i++:是先把i拿出来使用,然后再+1: ++i :是先把i+1,然后再拿出来使用:

  5. java集合分类

    Java中的集合包括三大类,它们是Set.List和Map, Set(集) List(列表) Map(映射) 它们都处于java.util包中,Set.List和Map都是接口,它们有各自的实现类.( ...

  6. 机器学习&恶意代码动态检测

    目录 写在前面 1 基于API调用的统计特征 2 API序列特征 3 API调用图 4 基于行为的特征 references: 写在前面 对恶意程序动态检测方法做了概述, 关于方法1和2可以参考阿里云 ...

  7. linux系统Kibana安装 汉化

    Elasticsearch官方系列软件Kibana,在控制台管理维护Elasticsearch. 这里注意Elasticsearch和Kibana的版本一定要一致. 官网下载地址 https://ww ...

  8. 云计算实验二 Docker实验-docker安装

    一.实验目的  1.了解Docker服务安装: 2.掌握Docker镜像操作 二.实验内容 1.Docker服务安装 查看内核版本 uname -r 安装依赖环境: yum install -y yu ...

  9. [JavaWeb]反序列化分析(二)--CommonCollections1

    反序列化分析(二)--CommonCollections1 链子分析 首先新建一个TransformedMap,其中二三参数为可控,后续要用到 当TransformedMap执行put方法时,会分别执 ...

  10. django_templates模板与html页

    新建应用 上一篇通过"django-admin startproject helloworld"是创建项目,一个项目下可以有多个应用(app).打开cmd,cd到manage.py ...