详谈springboot启动类的@SpringBootApplication注解
前几天我们学会了如何创建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注解的更多相关文章
- springboot系列(三) 启动类中关键注解作用解析
一.Springboot:请求入口 @SpringBootApplication @EnableAspectJAutoProxy @EnableScheduling @EnableTransactio ...
- springboot 启动类CommandLineRunner(转载)
在Spring boot项目的实际开发中,我们有时需要项目服务启动时加载一些数据或预先完成某些动作.为了解决这样的问题,Spring boot 为我们提供了一个方法:通过实现接口 CommandLin ...
- springboot 启动类启动跳转到前端网页404问题的两个解决方案
前段时间研究springboot 发现使用Application类启动的话, 可以进入Controller方法并且返回数据,但是不能跳转到WEB-INF目录下网页, 前置配置 server: port ...
- Springboot启动类及注解说明
Spring boot的启动是基于main方法的,其主要注解为: 1. @springBootApplication:项目的启动注解,是一个组合注解,包含@SpringbootConfiguratio ...
- Springboot测试类之@RunWith注解
@runWith注解作用: --@RunWith就是一个运行器 --@RunWith(JUnit4.class)就是指用JUnit4来运行 --@RunWith(SpringJUnit4ClassRu ...
- SpringBoot学习之@SpringBootApplication注解
下面是我们经常见到SpringBoot启动类代码: @SpringBootApplicationpublic class DemoApplication extends SpringBootServl ...
- SpringBoot启动tomcat源码解读
一.SpringBoot自动拉起Tomcat 原文链接:http://www.studyshare.cn/blog-front/blog/details/1136 SpringBoot框架是当前比较流 ...
- Springboot中使用自定义参数注解获取 token 中用户数据
使用自定义参数注解获取 token 中User数据 使用背景 在springboot项目开发中需要从token中获取用户信息时通常的方式要经历几个步骤 拦截器中截获token TokenUtil工具类 ...
- SpringBoot启动嵌入式tomcat源码解读
一.SpringBoot自动拉起Tomcat SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后 ...
随机推荐
- 逆向libbaiduprotect(三)- 移植python操作dalvik虚拟机c++函数,配合gdb控制程序运行流程
python编译移植到测试机,并且移植ctypes模块.利用ctypes代替c程序,利用dalvik内部c++函数,在运行过程中手动命令操控dalvik虚拟机,并结合gdb进行调试.绕过zygote和 ...
- windows系统两个网卡如何同时使用?内网和外网共用,配置教程
有些企业的网络分为内网和外网,一般内网不能访问Internet,但是又不提供外网,自己有上外网的需求,所以可能会用到双网卡,无线网卡+有线网卡或者有线+有线... 本文就是为了解决此问题,主要的做法就 ...
- UCloud 云服务内容审核 Java 版本实现
前言 最近不少小伙伴反映上传小黄图偶尔性的异常,并且不能上传动态图片,很是苦恼!无她,鉴黄API还没有这么智能,毕竟是自己训练的,不是那么专业!为了更好的服务广大网友,撸主决定接入更加智能快速的鉴黄服 ...
- JAVA _____Scanner用法
今天就来说一说Scanner用法,以前我在学C的时候记得第一天学的是很普遍的HelloWord的输出,JAVA中的输出是这样子的, public class ScannerWriter { publi ...
- Python常见字符串方法函数
1.大小写转换 S.lower() S.upper() 前者将S字符串中所有大写字母转为小写,后者相反 S.title() S.capitalize() 前者返回S字符串中所有单词首字母大写且其他字母 ...
- PL真有意思(八):其它程序设计模型
前言 在之前几篇我们讨论的语法.语义.命名.类型和抽象适用于所有语言.然而我们的注意力都主要集中在命令式语言上,现在这篇来看看其它范式的语言.函数式和逻辑式语言是最主要的非命令式语言. 函数式语言 命 ...
- 概率分布的python实现
接上篇概率分布,这篇文章讲概率分布在python的实现. 文中的公式使用LaTex语法,即在\begin{equation}至\end{equation}的内容可以在https://www.codec ...
- Java基础-Java基本语法
注释: 1:多行 /* */ 2:单行 // 3:文档 /** */ 基本数据类型: 1:整形 byte(1) short(2) int(4) long(8)(一般申明lo ...
- java学习引言
Java学习之路:不走弯路,就是捷径 0.引言 软件开发之路是充满荆棘与挑战之路,也是充满希望之路.Java学习也是如此,没有捷径可走.梦想像<天龙八部>中虚竹一样被无崖子醍醐灌顶而轻松获 ...
- python_网络编程
网络ISO(国际标准化组织)--->网络体系结构标准(OSI模型)OSI: 网络信息传输比较复杂需要很多功能协同-->将功能分开,降低耦合度,让每个模块完成一定的功能-->将这些模块 ...