Spring boot 的启动原理

Spring boot的启动原理

一.我们开发任何spring boot的项目,都要用到如下类:

 @SpringBootApplication
 public class Application {
   
     public static void main(String[] args) {
   
         SpringApplication.run(Application.class, args);
     }
 }

从上面的代码中可以看到
Application 类上面的注解 @SpringBootApplication
和main方法中的 SpringApplication.run(Application.class, args);
这两位最显眼,我们可以从这两入手,从而了解springboot

二、 @SpringBootApplication原理解析

  1. @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) })

这些注解虽然看起来很多,但是除去元注解,真正起作用的注解只有以下三个注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

那这三个注解是有啥用?其实在Spring Boot 1.2版之前,或者我们初学者刚开始接触springboot时,都还没开始使用@SpringBootApplication这个注解,而是使用以上三个注解启动项目。如果有兴趣的,也可以手动敲敲代码,就会发现这样也可以正常启动项目!

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class SpringBoot {
   
	public static void main(String[] args) {
   
		SpringApplication.run(SpringBoot.class, args);
	}
}

所以说这三个注解才是背后的大佬,@SpringBootApplication只是个空壳子。接下来,我来说明下这三个注解各自的作用。

2. @SpringBootConfiguration

同样,我们跟踪下@SpringBootConfiguration的源代码,看下他由哪些注解组成

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration

可以看到,除去元注解,剩下的@Configuration注解我相信大家应该都很熟了!我们springboot为什么可以去除xml配置,靠的就是@Configuration这个注解。所以,它的作用就是将当前类申明为配置类,同时还可以使用@bean注解将类以方法的形式实例化到spring容器,而方法名就是实例名,看下代码你就懂了!

@Configuration
public class TokenAutoConfiguration {
   
	@Bean
	public TokenService tokenService() {
   
		return new TokenService();
	}
}

作用等同于xml配置文件的

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:cache="http://www.springframework.org/schema/cache" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
	 					http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd 
	 					http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
	 					http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
	 					http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd ">
    <!--实例化bean-->
  <bean id="tokenService" class="TokenService"></bean>
</beans>

3. @ComponentScan

我们先说下@ComponentScan作用。他的作用就是扫描当前包以及子包,将有@Component,@Controller,@Service,@Repository等注解的类注册到容器中,以便调用。
注:大家第一眼见到@ComponentScan这个注解的时候是否有点眼熟?之前,一些传统框架用xml配置文件配置的时候,一般都会使用context:component-scan来扫描包。以下两中写法的效果是相同的`

@Configuration
@ComponentScan(basePackages="XXX")
public class SpringBoot {
   

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:cache="http://www.springframework.org/schema/cache" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
	 					http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd 
	 					http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
	 					http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
	 					http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd ">

	<!-- 扫描需要被调用的注解文件包 -->
	<context:component-scan base-package="XXX"></context:component-scan>
</beans>

注:如果@ComponentScan不指定basePackages,那么默认扫描当前包以及其子包,而@SpringBootApplication里的@ComponentScan就是默认扫描,所以我们一般都是把springboot启动类放在最外层,以便扫描所有的类。

4. @EnableAutoConfiguration

这里先总结下@EnableAutoConfiguration的工作原理,大家后面看的应该会更清晰:
它主要就是通过内部的方法,扫描classpath的META-INF/spring.factories配置文件(key-value),将其中的
org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项实例化并且注册到spring容器。

ok,我们同样打开@EnableAutoConfiguration源码,可以发现他是由以下几个注解组成的

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

除去元注解,主要注解就是@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)
我们springboot项目为什么可以自动载入应用程序所需的bean?就是因为这个神奇的注解@Import。那么这个@Import怎么这么牛皮?没关系!我们一步一步的看下去!
首先我们先进入AutoConfigurationImportSelector类,可以看到他有一个方法selectImports(),

在这里插入图片描述

继续跟踪,进入getAutoConfigurationEntry()方法

List configurations = getCandidateConfigurations(annotationMetadata, attributes);

可以看到这里有个List集合,那这个List集合又是干嘛的?没事,我们继续跟踪getCandidateConfigurations()方法!

可以看到这里有个方法

SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
1
这个方法的作用就是读取classpath下的META-INF/spring.factories文件的配置,将key为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项读取出来,通过反射机制实例化为配置文件,然后注入spring容器。

注:假如你想要实例化一堆bean,可以通过配置文件先将这些bean实例化到容器,等其他项目调用时,在spring.factories中写入这个配置文件的路径即可!我前面的文章有这个例子https://blog.csdn.net/weixin_40496191/article/details/109065430
主要是实现自己创建的starter依赖包,然后由其他项目引入使用,这是我的spring.factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=zzk.config.TokenAutoConfiguration
然后直接在SpringFactoriesLoader.loadFactoryNames;这个方法后面打个断点,可以在返回的集合里找到我们自定义的配置文件路径!
在这里插入图片描述

说明成功引入我们自定义的依赖包!

三、 SpringApplication.run()原理解析
SpringApplication.run()原理相对于前面注解的原理,会稍微麻烦点,为了方便我会适当贴出一些注解代码。
首先我们点击查看run方法的源码

可以看出,其实SpringApplication.run()包括两个部分,一部分就是创建SpringApplicaiton实例,另一部分就是调用run()方法,那他们又是怎么运行的?

  1. 创建SpringApplicaiton
    继续跟踪SpringApplication实例的源码

继续跟踪进入,到如下这个方法中

@SuppressWarnings({
    "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值