关键词:
- 1. SpringBoot启动类注解@SpringBootApplication:
- 2. SpringBoot的启动:
- 2.1. getSpringFactoriesInstances:
- 2.2. SpringApplication#run:
- 2.3. AbstractApplicationContext#refresh:
- 2.4. invokeBeanFactoryPostProcessors:
- 2.5. parser.parse(candidates);
- 2.6. this.reader.loadBeanDefinitions(configClasses)
- 2.7. ServletWebServerApplicationContext#onRefresh
- 2.8. AbstractApplicationContext#finishBeanFactoryInitialization
- 3. 小结:
基础环境: spring-boot :2.3.3.RELEASE、jdk1.8
1. SpringBoot启动类注解@SpringBootApplication:
标注
@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
主要关注自动配置类注解
@EnableAutoConfiguration
,这也是个组合注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
这里主要关注
@AutoConfigurationPackage
和@Import(AutoConfigurationImportSelector.class)
`
对于@AutoConfigurationPackage,也是一个组合注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
至此,可以看见整个@SpringBootApplication会引入两个@Import,要留意下
AutoConfigurationImportSelector.class
2. SpringBoot的启动:
启动主配置类即可启动SpringBoot项目,启动过程是怎样的,下面来看看,先进入启动类的run方法,会调用到此处:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args)
return new SpringApplication(primarySources).run(args);
在构造函数中会进行一些参数赋值,值得注意的是,此时开始预解析META-INF/spring.factories文件的配置类名
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// web应用类型,通常默认SERVLET
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置用于容器初始化的ApplicationContextInitializer
// 初次调用时,getSpringFactoriesInstances会进行META-INF/spring.factories文件的配置类名的解析
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 同理是设置用于容器初始化的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
2.1. getSpringFactoriesInstances:
该方法会调用SpringFactoriesLoader.loadFactoryNames方法进行预解析配置类名
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)
// 会获取默认类加载器,Launcher类型
ClassLoader classLoader = getClassLoader();
// 获取类型为type的配置类名,本次为获取ApplicationContextInitializer类型的配置类名
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通过默认构造方法创建对应配置类名的实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 对实例进行调用排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
2.1.1. SpringFactoriesLoader.loadFactoryNames:
该方法会调用loadSpringFactories来解析META-INF/spring.factories文件的配置类名
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
// 参数传递过来的类的完全限定名,即ApplicationContextInitializer类的完全限定名
String factoryTypeName = factoryType.getName();
// loadSpringFactories方法来解析配置类名;getOrDefault获取key为factoryTypeName的配置类名
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)
// 从缓存cache(key:类加载器;value: result)中获取默认类加载器对应的value
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null)
return result;
// 初次启动调用,缓存获取不到值,开始解析META-INF/spring.factories文件的配置类名
try
// 类加载器不为null,取所有类路径下的META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 循环遍历有META-INF/spring.factories文件的jar包,将里面的键值对封装到result,结构相当于 Map<Stirng,LinkedList>
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();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue()))
result.add(factoryTypeName, factoryImplementationName.trim());
// 写入缓存,方便后续调用
cache.put(classLoader, result);
return result;
catch (IOException ex)
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
像自动配置类名就是从spring-boot-autoconfigure这个jar包META-INF/spring.factories文件解析出来
2.2. SpringApplication#run:
public ConfigurableApplicationContext run(String... args)
... ... ...
try
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 上面设置一些环境吧应该,没怎么关注
configureIgnoreBeanInfo(environment);
// 这里控制台会打印输出那个Spring启动图
Banner printedBanner = printBanner(environment);
// 创建ApplicationContext即ioc容器
context = createApplicationContext();
// 像上面2.1那样,会从缓存中获取SpringBootExceptionReporter类型实例
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] ConfigurableApplicationContext.class , context);
// 对context进行一些预处理,主要是一些赋值
// 这里会将主配置类封装成BeanDefinition再注册到ApplicationContext中,详情可参考文末参考链接1
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新context容器,重点方法
refreshContext(context);
... ...
2.3. AbstractApplicationContext#refresh:
上述refreshContext方法会刷新上下文,即context容器,方法最后会调用refresh方法:
public void refresh() throws BeansException, IllegalStateException
synchronized (this.startupShutdownMonitor)
// 刷新前准备,初始化properties等
prepareRefresh();
// 获取ApplicationContext中组合的BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 主要给beanFactory赋值,比如类加载器,ApplicationContextAwareProcessor后置处理器等等
prepareBeanFactory(beanFactory);
try
// 允许在上下文的子类中对bean factory进行后处理
postProcessBeanFactory(beanFactory);
// 调用Bean工厂的后置处理器,重点方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean的后置处理器
registerBeanPostProcessors(beanFactory);
// 初始化消息源
initMessageSource();
// 初始化事件广播
initApplicationEventMulticaster();
// 初始化特殊的Bean,比如tomcat
onRefresh();
// 注册监听器
registerListeners();
// 实例化所有的(non-lazy-init)单例Bean
finishBeanFactoryInitialization(beanFactory);
// 发布刷新完毕事件
finishRefresh();
... ... ...
2.4. invokeBeanFactoryPostProcessors:
通过委托模式调用PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
... ... ...
2.4.1. invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
- 这个方法会实例化和调用所有postProcessBeanDefinitionRegistry
- 还有其父类BeanFactoryPostProcessor的postProcessBeanFactory方法
- 注意:BeanDefinitionRegistryPostProcessor优先于BeanFactoryPostProcessor执行,BeanFactoryPostProcessor优先于bean实例化执行;
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors)
Set<String> processedBeans = new HashSet<>();
// ioc容器创建时beanFactory为DefaultListableBeanFactory
// DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,所以true
if (beanFactory instanceof BeanDefinitionRegistry)
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 循环已经注册过的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors)
// 如果是子类BeanDefinitionRegistryPostProcessor,就执行postProcessBeanDefinitionRegistry方法
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor)
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
else
regularPostProcessors.add(postProcessor);
// 用于保存本初要执行的BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 找出所有实现BeanDefinitionRegistryPostProcessor接口的Bean的beanName
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames)
// 判断这个ppName的bean是否实现了PriorityOrdered
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class))
// 满足条件会通过getBean获取对应ppName的实例,添加到currentRegistryProcessors
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
// 将ppName添加到processedBeans,表名这个ppName的bean调用过postProcessBeanDefinitionRegistry方法
processedBeans.add(ppName);
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 调用postProcessBeanDefinitionRegistry方法,重点方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
... ... ...
// 下面省略了两次查找postProcessorNames及调用postProcessBeanDefinitionRegistry方法的逻辑,与上面类似
// 也省略对父类BeanFactoryPostProcessor查找和调用的逻辑,也类似
// 省略的内容可以见文末参考链接2
- postProcessorNames查找其实是从beanDefinitionNames中查找符合类型的beanName
- 容器context创建后,默认的BeanFactory有五个内部托管的beanName,在prepareContext会将主配置类的beanName添加进去
- 这里实现BeanDefinitionRegistryPostProcessor的只有internalConfigurationAnnotationProcessor
这个beanName在getBean时会获取ConfigurationClassPostProcessor对象
2.4.2. invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
本次会调用ConfigurationClassPostProcessor的currentRegistryProcessors方法
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry)
// 循环遍历传过来的postProcessors,调用postProcessBeanDefinitionRegistry方法
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors)
postProcessor.postProcessBeanDefinitionRegistry(registry);
会通过registryId去重,重复则抛异常,反则调用processConfigBeanDefinitions
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId))
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
if (this.factoriesPostProcessed.contains(registryId))
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
2.4.3. processConfigBeanDefinitions(registry);
方法入参registry就是BeanFactory,就是默认的DefaultListableBeanFactory
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry)
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// 循环遍历candidateNames,也就是beanDefinitionNames,检查出是配置类的beanName
for (String beanName : candidateNames)
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null)
if (logger.isDebugEnabled())
logger.despringboot:springboot启动原理分析(代码片段)
文章目录SpringBoot启动原理分析一、依赖导入原理二、SpringBoot包扫描原理 三、SpringBoot自动配置原理SpringBoot启动原理分析一、依赖导入原理父项目版本控制ctrl+点击spring-boot-starter-parent进入继续点击,进入spring-boot-dependencie... 查看详情
springboot--springboot的启动整体过程|自动配置类解析注册过程|spring5源码解析(代码片段)
1.SpringBoot启动类注解@SpringBootApplication:2.SpringBoot的启动: 2.1.getSpringFactoriesInstances: 2.1.1.SpringFactoriesLoader.loadFactoryNames: 2.2.SpringApplication#run: 查看详情
springboot--springboot的启动整体过程|自动配置类解析注册过程|spring5源码解析(代码片段)
1.SpringBoot启动类注解@SpringBootApplication:2.SpringBoot的启动: 2.1.getSpringFactoriesInstances: 2.1.1.SpringFactoriesLoader.loadFactoryNames: 2.2.SpringApplication#run: 查看详情
springboot|springboot启动错误
ErrorstartingApplicationContext.Todisplaytheconditionsreportre-runyourapplicationwith‘debug‘enabled. 原因: springboot启动时会自动注入数据源和配置jpa 解决: 在@SpringBootApplication中排除其注入 exclude= 查看详情
学习springboot
什么是springboot?springboot是一款快速开发的框架,能够快速整合第三方框架,全部采用注解的方式,内置tomcat容器,springboot的web组件默认集成的是springMVC框架。优点:1.省去了各种繁琐的配置,开箱即用。2.可以打jar包直接使用ja... 查看详情
springboot#springboot项目启动时,打印端口号项目名访问地址(代码片段)
...应的信息打印在控制台解决方案:使用Environment类@SpringBootApplicationpublicclassApplicationpublicstaticvoi 查看详情
springboot
什么是SpringBoot? SpringBoot是一个框架,一种全新的编程规范,他的产生简化了框架的使用,所谓简化是指简化了Spring众多框架中所需的大量且繁琐的配置文件,所以SpringBoot是一个服务于框架的框架,服务范围是简化配置文件... 查看详情
java面试题springboot启动原理(代码片段)
SpringBoot启动原理1、什么是SpringBoot2、SpringBoot启动原理3、三个关键注解解析3.1、@SpringBootConfiguration注解解析3.2、@EnableAutoConfiguration注解解析3.3、SpringFactoriesLoader(幕后英雄)3.4、@ComponentScan注解解析1、 查看详情
Spring Boot + Spring Boot 安全启动错误
】SpringBoot+SpringBoot安全启动错误【英文标题】:Springboot+springbootsecuritystarterror【发布时间】:2020-08-2504:49:00【问题描述】:我正在使用springboot和springsecurity和jsp做一个MVC项目。我只是在训练我的弹簧,我有同样的项目在没有弹簧... 查看详情
springboot:springboot的底层运行原理解析(代码片段)
...器spring-boot-starter2.主启动类的注解1.默认的主启动类2.@SpringBootApplication3.@ComponentScan4.@SpringBootConfiguration5.SpringBootApplication注解6.spring.factories7.结论8.简单图解3.主启动类的方法1.SpringApplication2.run方法流程分析1.pom.xml1.父依赖... 查看详情
springboot入门基础
SpringBoot入门(一)HelloWorld一什么是springboot springboot是一个全新的框架,它设计的目的简化spring项目的初始环境的搭建和开发,主要有以下几个特点: 1、简化初始配置,可与主流框架集成; 2、内置Servlet容器,无需在打... 查看详情
springboot#springboot项目启动时,打印端口号项目名访问地址(代码片段)
...应的信息打印在控制台解决方案:使用Environment类@SpringBootApplicationpublicclassApplicationpublicstaticvoidmain(String[]args)ConfigurableApplicationContextconfigurableApplicationContext=SpringApplication.run(Application.class,args);Environmentenvironment=configurab... 查看详情
springboot返回xml数据,一分钟搞定!
SpringBoot返回XML数据,前提必须已经搭建了SpringBoot项目,所以这一块代码就不贴了,可以点击查看之前分享的SpringBoot返回JSON数据,一分钟搞定!。你所需具备的基础什么是SpringBoot?SpringBoot核心配置文件详解SpringBoot开启的2种方式... 查看详情
springboot实现一个监听用户请求的拦截器
...个拦截器来监听,并继续相应的日志记录 项目构建与SpringBoot,SpringBoot实现一个拦截器很容易。SpringBoot的核心启动类继承WebMvcConfigurerAdapter//增加拦截器@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){ 查看详情
springboot——springboot集成thymeleaf(代码片段)
文章目录:1.认识Thymeleaf2.详细步骤2.1创建一个SpringBoot项目2.2在pom.xml文件中会自动添加SpringBoot集成Thymeleaf的起步依赖2.3在核心配置文件中添加以下内容2.4写一个Controller控制层2.5写一个html页面2.6启动测试1.认识Thymeleaf Thymeleaf... 查看详情
微服务之springboot面试题(代码片段)
1,什么是SpringBoot?SpringBoot是Spring开源组织下的子项目,是Spring组件一站式解决方案,主要是简化了使用Spring的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。2,为什么要用SpringBoot?SpringBoot优点非常多,如... 查看详情
微服务之springboot面试题(代码片段)
1,什么是SpringBoot?SpringBoot是Spring开源组织下的子项目,是Spring组件一站式解决方案,主要是简化了使用Spring的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。2,为什么要用SpringBoot?SpringBoot优点非常多,如... 查看详情
springboot入门基础
1.什么是springboot? springboot是一个快速开发框架,快速整合第三方依赖 (原理:maven父子工程的方式),简化xml的配置,采用注解形式, 内置HTTP服务器(Jetty、Tomcat)最终以java应用程序执行。2.核心原理: <1>基于springMVC无... 查看详情