我们再来看下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. javascript中逻辑运算(||,&&,!)

    作为一个后端开发的程序员,一直就对JavaScript情有独钟,作为一门前后端通吃的语言,必须赞一下.而且之前很长一段时间都有在做JavaScript,一路都是和ie8死磕,磕完又找低版本的谷歌磕,坑 ...

  2. MATLAB 设置示波器颜色和行列

    设置颜色 设置行列和图例 放大缩小显示

  3. Kafka和RabbitMQ有哪些区别,各自适合什么场景?

    经常有人问我 有个 xx 需求,我应该用 Kafka 还是 RabbitMQ ? 这个问题很常见,而且很多人对二者的选择也把握不好. 所以我决定写篇文章来详细说一下:Kafka 和 RabbitMQ ...

  4. spring boot pom.xml 提示 ignored 具体解决

    1.出现这个情况 2. 进入设置,找到 去掉勾选即可

  5. [Docker] 在CentOS6.8 安装 Docker

    运行docker Linux内核版本需要在3.8以上,针对centos6.5 内核为2.6的系统需要先升级内核.不然会特别卡,退出容器. # 查看当前版本: cat /etc/issue # 导入pu ...

  6. Go 中实现用户的每日限额(比如一天只能领三次福利)

    如果你写一个 bug 管理系统,用了这个 PeriodLimit 你就可以限制每个测试人员每天只能给你提一个 bug.工作是不是就轻松很多了? 如今微服务架构大行其道本质原因是因为要降低系统的整体复杂 ...

  7. css中两种居中方式text-align:center和margin:0 auto 的使用场景

    关于使用text-align:center和margin:0 auto 两种居中方式的比较 前言:最近由于要学习后端,需要提前学习一部分前端知识,补了补css知识,发现狂神在讲这一部分讲的不是特别清楚 ...

  8. NPOI Excel导入Invalid header signature

    excel是从网页下载或者其他第三方软件导出的解决方法:使用excel打开,另存为2003版的excel,再导入就好了或者保存为 xlsx

  9. spring 事务传播性

    一.什么是事务传播性 大白话讲就是,方法之间互相调用的时候,事务如何传播,比如A()调用B(),B()的事务是和A()共用一个事务(失败一起提交)? 还是新事务(两者事务互不影响)?,还是说B()不需 ...

  10. for in 遍历对象

    如果直接写car.key会输出undefined,下面的注释是浏览器运行的原理,浏览器最终都是以car["key"]的方式来查找数据的