前几天我们学会了如何创建springboot项目今天我们说一下他是怎么运行的为什么不需要我们再去编写繁重的配置文件的

@SpringBootApplication

首先我们看一下这个注解,他是用来标注在主程序的,表明他是一个springboot项目

@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注解后我们重点关注最后三个注解

@ComponentScan(包扫描)
  • component是组件,scan是扫描,所以这个注解的含义就是用来扫描组件的,

  • componentScan就是扫描所标注的类所在包下的所有需要注入的组件,将其注入,这里他是在@SpringBootApplication 中体现的,所以这个注解会自动注入所有在主程序所在包下的组件

  • 以前在ssm项目中我们需要去配置我们的包扫描

  
<context:component-scan base-package="com.xxx"></context:component-scan>
@EnableAutoConfiguration(开启自动装配)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

String[] excludeName() default {};
}

这里我们关注两个注解

一、@AutoConfigurationPackage

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

在这个注解中,主要是获取我们注解所在包下的组件去进行注册

  
 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}

//metadata是我们注解所在的元信息
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//看这里 将我们注解所在包下所有的组件去进行注册
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}

public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}

register方法:通过AutoConfigurationPackages去对包下的组件进行注册

  
 private static final String BEAN = AutoConfigurationPackages.class.getName();

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//先判断整个BEAN有没有被注册
if (registry.containsBeanDefinition(BEAN)) {
//获取bean的定义
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
//通过bean获取构造函数的参数值
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
//添加参数值,
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
} else {
//创建一个新的bean的定义
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
//设置bean的类型为AutoConfigurationPackages类型
beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(2);
//进行bean的注册
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}

二、@Import({AutoConfigurationImportSelector.class})

装配我们的 自动配置导入选择器

我们点进去这个类,下面有一个方法getAutoConfigurationEntry 获取自动装配的入口、

  
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}

这个方法他主要是获取了configurations的一个集合在这我们点进去getCandidateConfigurations方法

  
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}

我们首先看他这个红色报错信息:在META-INF/spring.factories中没有找到自动配置类。根据他的错误信息我们可以得出他是从META-INF/spring.factories获取我们的自动配置信息的

我们也可以在点进这个loadFactoryNames这个方法

   public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
//调用了下面的方法loadSpringFactories
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
//根据类加载器去获取配置文件里的信息
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();

while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
//配置信息经过多次转换最终成为properties形式
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
//循环获取所有的配置信息
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;

for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}

cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}

而这个配置文件在哪里呢

在我们项目的External Libraries中找到我们的spring-boot-autoconfigure中

这样就完成我们默认的一些自动装配了

@SpringBootConfiguration

这个方法就比较简单了,表明了这是一个配置类

  @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
 
到这我们三个注解就讲完了,有什么问题请大佬们指明谢谢。

有过挣扎,有过失败,但仍然会爬起来继续战斗

详谈springboot启动类的@SpringBootApplication注解的更多相关文章

  1. springboot系列(三) 启动类中关键注解作用解析

    一.Springboot:请求入口 @SpringBootApplication @EnableAspectJAutoProxy @EnableScheduling @EnableTransactio ...

  2. springboot 启动类CommandLineRunner(转载)

    在Spring boot项目的实际开发中,我们有时需要项目服务启动时加载一些数据或预先完成某些动作.为了解决这样的问题,Spring boot 为我们提供了一个方法:通过实现接口 CommandLin ...

  3. springboot 启动类启动跳转到前端网页404问题的两个解决方案

    前段时间研究springboot 发现使用Application类启动的话, 可以进入Controller方法并且返回数据,但是不能跳转到WEB-INF目录下网页, 前置配置 server: port ...

  4. Springboot启动类及注解说明

    Spring boot的启动是基于main方法的,其主要注解为: 1. @springBootApplication:项目的启动注解,是一个组合注解,包含@SpringbootConfiguratio ...

  5. Springboot测试类之@RunWith注解

    @runWith注解作用: --@RunWith就是一个运行器 --@RunWith(JUnit4.class)就是指用JUnit4来运行 --@RunWith(SpringJUnit4ClassRu ...

  6. SpringBoot学习之@SpringBootApplication注解

    下面是我们经常见到SpringBoot启动类代码: @SpringBootApplicationpublic class DemoApplication extends SpringBootServl ...

  7. SpringBoot启动tomcat源码解读

    一.SpringBoot自动拉起Tomcat 原文链接:http://www.studyshare.cn/blog-front/blog/details/1136 SpringBoot框架是当前比较流 ...

  8. Springboot中使用自定义参数注解获取 token 中用户数据

    使用自定义参数注解获取 token 中User数据 使用背景 在springboot项目开发中需要从token中获取用户信息时通常的方式要经历几个步骤 拦截器中截获token TokenUtil工具类 ...

  9. SpringBoot启动嵌入式tomcat源码解读

    一.SpringBoot自动拉起Tomcat SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后 ...

随机推荐

  1. lqb 入门训练 Fibonacci数列 (循环 PS:提柜要栈溢出)

    入门训练 Fibonacci数列 时间限制:1.0s   内存限制:256.0MB     问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时, ...

  2. 领扣(LeetCode)两数之和II - 输入有序数组 个人题解

    给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数. 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2. 说明: 返回的下标值 ...

  3. fiddler工具使用大全

    Fiddler基础知识 Fiddler是强大的抓包工具,它的原理是以web代理服务器的形式进行工作的,使用的代理地址是:127.0.0.1,端口默认为8888,我们也可以通过设置进行修改. 代理就是在 ...

  4. Nginx下HTTP强制重定向至HTTPS

    Nginx下HTTP强制重定向至HTTPS 对于nginx来说,配置http强制重定向至https有多种多样的写法.可以直接rewrite,也可以用301重定向.但是直接拷贝网上的配置往往会出现问题, ...

  5. 如何在maven中下载jar包

    1.进入maven网址  https://mvnrepository.com/ 2.搜索你想要的包的名字 3.找到列表中你想要的包 4.点击一个版本 5.点击 view all 6.找到你想要的包,点 ...

  6. 组长组——“多彩夕阳”APP评价

    基于NABCD评论,及改进建议 一.组长组NABCD: N-需求背景: (1)我国老龄化日益加重,已突破两亿.目前人数还在增加,解决老年福祉成为社会关注的民生话题. (2)智能手机越来越普遍,老年人已 ...

  7. 【集训Day2 哈希表】【NHOI2015】【Luogu P2421】差

    LuoguP2421 原题来自NHOI2015 [解题思路] 本题的解题方法有三种,一种为枚举减数,二分查找被减数.第二种为利用数据单调性用尺取法进行查找,第三种为运用哈希表以快速查找数据. [解题反 ...

  8. JavaScript笔记四

    1.运算符 逻辑运算符 ! - 非运算可以对一个布尔值进行取反,true变false false边true - 当对非布尔值使用!时,会先将其转换为布尔值然后再取反 - 我们可以利用!来将其他的数据类 ...

  9. JavaScript基础目录

    一.JavaScript简介 1.JavaScript用途 2.JavaScript的诞生 3.JavaScript从丑小鸭到金凤凰 4.JavaScript非常好学 5.学习方法 二.Hello W ...

  10. python核心编程笔记(转)

    解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找python路径 1.4 –v   冗 ...