Bean
加载方式
关于该部分的内容,很多都是我们在《15.Spring Framework [1/2]》 讨论过的,这里相当于做一个小结。
所举的很多例子,也都来自《15.Spring Framework [1/2]》 。
配置文件和bean标签
1 2 <bean id ="bookDao" class ="com.kakawanyifan.dao.impl.BookDaoImpl" /> <bean id ="bookService" class ="com.kakawanyifan.service.impl.BookServiceImpl" />
配置文件和注解
1 2 3 4 5 6 7 8 9 10 11 12 package com.kakawanyifan.dao.impl;import com.kakawanyifan.dao.BookDao;import org.springframework.stereotype.Component;@Component ("bookDao" )public class BookDaoImpl implements BookDao { public void save () { System.out.println("saving..." ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.kakawanyifan.service.impl;import com.kakawanyifan.dao.BookDao;import com.kakawanyifan.service.BookService;import org.springframework.stereotype.Component;@Component public class BookServiceImpl implements BookService { private BookDao bookDao; public void save () { System.out.println(this .bookDao); } }
1 2 3 4 5 6 7 8 9 10 11 12 <?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:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.kakawanyifan" /> </beans >
当然,由于我们无法在第三方提供包中添加注解,因此当我们需要加载第三方开发的Bean的时候,可以将@Bean
定义在一个方法上方,方法的返回值就交给Spring管控。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.kakawanyifan;import com.alibaba.druid.pool.DruidDataSource;import org.springframework.context.annotation.Bean;import javax.sql.DataSource;public class JdbcConfig { @Bean public DataSource dataSource () { DruidDataSource ds = new DruidDataSource(); ds.setUrl("jdbc:mysql://localhost:3306/s" ); ds.setUsername("root" ); ds.setPassword("MySQL@2022" ); return ds; } }
需要注意的是,这个包要能被Spring配置类加载到。而且在《15.Spring Framework [1/2]》 我们说,如果在"JdbcConfig"加注解的话,一定是@Configuration
注解,不能是@Component
等其他注解。
因为@Configuration
注解中有一个属性proxyBeanMethods
,且该值默认为true
。
我们来看例子。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.kakawanyifan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration (proxyBeanMethods = true )@ComponentScan ("com.kakawanyifan" )public class SpringConfig { @Bean public Cat cat () { return new Cat(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.kakawanyifan;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; SpringConfig springConfig = ctx.getBean("springConfig" , SpringConfig.class ) ; System.out.println(springConfig.cat()); System.out.println(springConfig.cat()); System.out.println(springConfig.cat()); } }
运行结果:
1 2 3 com.kakawanyifan.Cat@240237d2 com.kakawanyifan.Cat@240237d2 com.kakawanyifan.Cat@240237d2
我们看到,我们执行了三次springConfig.cat()
,居然都是同一个对象。
如果我们把proxyBeanMethods
设置为false
,即@Configuration(proxyBeanMethods = false)
,则三次是三个不同的对象。 如果我们不采用@Configuration
注解,三次是三个不同的对象。 如果我们把proxyBeanMethods
设置为true
,但是我们注释掉cat()
方法上的@Bean
注解,三次也是三个不同的对象,因为此时没有方法的返回值Cat的Bean对象交给Spring管理。 如果是一个数据库连接的对象,因为在程序中不宜频繁的创建数据库连接的对象,所以一定要用@Configuration
注解。
注解声明配置类
定义一个类并使用@ComponentScan
替代原来XML配置中的包扫描这个动作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.kakawanyifan;import com.alibaba.druid.pool.DruidDataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;@Configuration @ComponentScan ("com.kakawanyifan" )public class SpringConfig {}
如果需要多组,采取数组的形式:
1 @ComponentScan ({"com.kakawanyifan.service" ,"com.kakawanyifan.dao" })
由于早期开发的系统大部分都是采用XML的形式配置Bean,现在基本上都是使用注解。如果我们要对原系统进行改造,做二次开发。可以考虑@ImportResource
这个注解,在配置类上直接写上要被融合的XML配置文件名即可。
1 2 3 4 5 @Configuration @ImportResource ("applicationContext.xml" )public class SpringConfig {}
FactroyBean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.kakawanyifan;import com.kakawanyifan.service.BookService;import com.kakawanyifan.service.impl.BookServiceImpl;import org.springframework.beans.factory.FactoryBean;public class BookServiceFactoryBean implements FactoryBean <BookService > { public BookService getObject () throws Exception { System.out.println("public BookService getObject" ); return new BookServiceImpl(); } public Class<?> getObjectType() { return BookService.class ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.kakawanyifan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;@ComponentScan ("com.kakawanyifan" )public class SpringConfig { @Bean public BookServiceFactoryBean bookService () { return new BookServiceFactoryBean(); } }
或者XML方式:
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="bookService" class ="com.kakawanyifan.BookServiceFactoryBean" /> </beans >
通过这种方法,虽然我们配置的是类是BookServiceFactoryBean
,但所得到的对象,是BookServiceImpl
类。
具体我们可以验证一下,示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 package com.kakawanyifan;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; System.out.println(ctx.getBean("bookService" )); } }
运行结果:
1 com.kakawanyifan.service.impl.BookServiceImpl@24a35978
有什么用呢?可以在对象初始化前,即BookServiceFactoryBean
的getObject()
方法中,做一些事情。例如,我们创建一个数据库的连接对象,可以在其中检查数据库是否成功启动。
@Import注解注入Bean
如果我们要加载的Bean无法添加@Component
注解,而且是第三方的Bean,我们可以使用@Import
。
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.kakawanyifan;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Configuration @ComponentScan ("com.kakawanyifan" )@Import (JdbcConfig.class ) public class SpringConfig {}
ImportSelector
可以有各种条件的判定,最后决定是否装载指定的Bean。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.kakawanyifan;import org.springframework.context.annotation.ImportSelector;import org.springframework.core.type.AnnotationMetadata;public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { boolean flag = annotationMetadata.hasAnnotation("org.springframework.context.annotation.Configuration" ); if (flag && annotationMetadata.getClassName().equals("com.kakawanyifan.SpringConfig" )){ return new String[]{"com.kakawanyifan.Cat" }; } return new String[0 ]; } }
1 2 3 4 5 6 7 8 9 10 package com.kakawanyifan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Configuration @Import (MyImportSelector.class ) public class SpringConfig {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.kakawanyifan;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String iter:beanDefinitionNames) { System.out.println(iter); } } }
运行结果:
1 2 3 4 5 6 7 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory springConfig com.kakawanyifan.Cat
因为我们是@Import(MyImportSelector.class)
,所以在selectImports(AnnotationMetadata annotationMetadata)
方法中,annotationMetadata.getClassName()
的值是com.kakawanyifan.SpringConfig
ImportBeanDefinitionRegistrar
在Spring中定义了BeanDefinition
,这个控制Bean初始化加载的核心。BeanDefinition接口中给出了若干种方法,可以控制Bean的相关属性,例如创建的对象是单例还是非单例,在BeanDefinition中定义了scope属性就可以控制这个。
实现ImportBeanDefinitionRegistrar接口,我们可以根据条件,控制Bean的属性。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.kakawanyifan;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;import org.springframework.core.type.AnnotationMetadata;import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions (AnnotationMetadata metadata, BeanDefinitionRegistry registry) { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class ).getBeanDefinition () ; if (metadata.getClassName().equals("com.kakawanyifan.SpringConfig-EEEEEEEE" )){ registry.registerBeanDefinition("tom" ,beanDefinition); }else { registry.registerBeanDefinition("tomcat" ,beanDefinition); beanDefinition.setScope(SCOPE_PROTOTYPE); } } }
1 2 3 4 5 6 7 8 9 10 package com.kakawanyifan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Configuration @Import (MyImportBeanDefinitionRegistrar.class ) public class SpringConfig {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.kakawanyifan;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String iter:beanDefinitionNames) { System.out.println(iter); } System.out.println(ctx.getBean("tomcat" )); System.out.println(ctx.getBean("tomcat" )); System.out.println(ctx.getBean("tomcat" )); } }
运行结果:
1 2 3 4 5 6 7 8 9 10 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory springConfig tomcat com.kakawanyifan.Cat@4e41089d com.kakawanyifan.Cat@32a068d1 com.kakawanyifan.Cat@33cb5951
最终裁定
在上文,我们讨论了很多种Bean的加载方式,但是这里有一个BUG。这么多种方式,如果之间有冲突怎么办?谁能有最终裁定权?
BeanDefinitionRegistryPostProcessor
,这个有最终裁定权。
其实看名字就知道,BeanDefinition是Bean定义,Registry是注册,Post是后置,Processor是处理器,全称bean定义后处理器。即在所有Bean注册都折腾完后,它进行最后的处理。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.kakawanyifan;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_SINGLETON;public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException { registry.getBeanDefinition("tomcat" ).setScope(SCOPE_SINGLETON); } @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { } }
1 2 3 4 5 6 7 8 9 10 package com.kakawanyifan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Configuration @Import ({MyImportBeanDefinitionRegistrar.class ,MyBeanDefinitionRegistryPostProcessor .class }) public class SpringConfig {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.kakawanyifan;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String iter:beanDefinitionNames) { System.out.println(iter); } System.out.println(ctx.getBean("tomcat" )); System.out.println(ctx.getBean("tomcat" )); System.out.println(ctx.getBean("tomcat" )); } }
运行结果:
1 2 3 4 5 6 7 8 9 10 11 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory springConfig com.kakawanyifan.MyBeanDefinitionRegistryPostProcessor tomcat com.kakawanyifan.Cat@365c30cc com.kakawanyifan.Cat@365c30cc com.kakawanyifan.Cat@365c30cc
手工加载Bean
上文讨论的加载Bean的方式都是在容器启动阶段完成Bean的加载,这种方法可以在容器初始化完成后手动加载Bean。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.kakawanyifan;public class Cat { int age; public Cat () { } public Cat (int age) { this .age = age; } @Override public String toString () { return "Cat{" + "age=" + age + '}' ; } }
1 2 3 4 5 6 7 8 9 10 11 package com.kakawanyifan;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; ctx.registerBean("tom" , Cat.class ) ; System.out.println(ctx.getBean("tom" )); } }
运行结果:
(为什么age
的值是0
呢,我们在《1.基础语法》 讨论过,整数,默认值:0
。)
如果容器中已经有了某种Bean,再加载会覆盖。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.kakawanyifan;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; ctx.registerBean("tom" , Cat.class ) ; ctx.registerBean("tom" , Cat.class ,1) ; Cat tom = ctx.getBean("tom" , Cat.class ) ; tom.age = 100 ; ctx.registerBean("tom" , Cat.class ,2) ; System.out.println(ctx.getBean("tom" )); } }
运行结果:
那么,BeanDefinitionRegistryPostProcessor
和手工加载Bean
,谁能最后定义Bean呢? 当然是手工加载Bean
,因为这种方法是在容器初始化完成后进行的。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.kakawanyifan;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; ctx.registerBean("tomcat" , Cat.class ,2) ; String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String iter:beanDefinitionNames) { System.out.println(iter); } System.out.println(ctx.getBean("tomcat" )); System.out.println(ctx.getBean("tomcat" )); System.out.println(ctx.getBean("tomcat" )); } }
运行结果:
1 2 3 4 5 6 7 8 9 10 11 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory springConfig com.kakawanyifan.MyBeanDefinitionRegistryPostProcessor tomcat Cat{age=2} Cat{age=2} Cat{age=2}
加载控制
@Conditional注解
上文,我们讨论了ImportSelector
,ImportBeanDefinitionRegistrar
等,都可以控制Bean的加载。
Spring把这些操作给我们做了封装,提供了一个@Conditional
注解,注解中需要我们填我们对于"Condition"接口的实现类。
不过,SpringBoot给我们提供了更多的封装好的注解,这样我们只需要填条件。
@Conditional注解的衍生注解
我们需要导入SpringBoot的依赖,在这里我们只导入spring-boot-starter
。
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > <version > 2.7.7</version > </dependency >
(为了避免冲突,我们可以删去之前导入的Spring的依赖。)
然后,我们可以看到有各种各样的@Conditional
注解的衍生注解。
例子
如果有类com.kakawanyifan.App
,并且没有类com.kakawanyifan.AAAApp
,并且有Beancom.kakawanyifan.Mouse
,才加载Cat。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.kakawanyifan;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Configuration @Import (Mouse.class ) public class SpringConfig { @Bean @ConditionalOnClass (name = "com.kakawanyifan.App" ) @ConditionalOnMissingClass ("com.kakawanyifan.AAAApp" ) @ConditionalOnBean (name = "com.kakawanyifan.Mouse" ) public Cat cat () { return new Cat(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.kakawanyifan;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App { public static void main (String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class ) ; String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); for (String iter:beanDefinitionNames) { System.out.println(iter); } } }
运行结果:
1 2 3 4 5 6 7 8 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory springConfig com.kakawanyifan.Mouse cat
注意,通过@Import
注解导入的Bean,名字是全限定名。
自动配置
分析
我们从@SpringBootApplication
这个注解开始,其内容如下:
1 2 3 4 5 6 7 8 9 @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 {
@ComponentScan
的参数是两个扫描排除类,其他的一些属于元注解,我们主要关注:
@SpringBootConfiguration
@EnableAutoConfiguration
@SpringBootConfiguration
@SpringBootConfiguration
的内容如下:
1 2 3 4 5 6 @Target (ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)@Documented @Configuration @Indexed public @interface SpringBootConfiguration {
其中的@Configuration
和@Indexed
,实际没有值得分析的内容:
1 2 3 4 5 @Target ({ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Documented @Component public @interface Configuration {
1 2 3 4 @Target ({ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Documented public @interface Indexed {
@EnableAutoConfiguration
@EnableAutoConfiguration
的内容如下:
1 2 3 4 5 6 7 @Target (ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)@Documented @Inherited @AutoConfigurationPackage @Import (AutoConfigurationImportSelector.class ) public @interface EnableAutoConfiguration {
我们主要关注:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
的类AutoConfigurationImportSelector
。
@AutoConfigurationPackage
的内容如下:
1 2 3 4 5 6 @Target (ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)@Documented @Inherited @Import (AutoConfigurationPackages.Registrar.class ) public @interface AutoConfigurationPackage {
我们主要关注@Import({AutoConfigurationPackages.Registrar.class})
的AutoConfigurationPackages.Registrar
。
综上所述,我们主要关注:
AutoConfigurationImportSelector
AutoConfigurationPackages.Registrar
Registrar
我们点进AutoConfigurationPackages.Registrar
,Registrar
是AutoConfigurationPackages
这个抽象类的一个静态内部类,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package org.springframework.boot.autoconfigure;【部分代码略】 public abstract class AutoConfigurationPackages { 【部分代码略】 static class Registrar implements ImportBeanDefinitionRegistrar , DeterminableImports { @Override public void registerBeanDefinitions (AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0 ])); } @Override public Set<Object> determineImports (AnnotationMetadata metadata) { return Collections.singleton(new PackageImports(metadata)); } } 【部分代码略】 }
Registrar
实现了ImportBeanDefinitionRegistrar
接口,这个接口我们在上文讨论过,可以根据条件,控制Bean的属性。
我们在register
方法这一行打断点,通过"执行表达式",查看第二个参数的内容,是com.kakawanyifan
。
@AutoConfigurationPackage
注解的主要作用是获取项目主程序启动类所在根目录,从而指定后续组件扫描器要扫描的包位置。
因此在定义项目包结构时,要求定义的包结构非常规范,项目主程序启动类要定义在最外层的根目录位置,然后在根目录位置内部建立子包和类进行业务开发,这样才能够保证定义的类能够被组件扫描器扫描。
EnableAutoConfiguration
EnableAutoConfiguration
的重点是AutoConfigurationImportSelector
。
selectImports
我们点开AutoConfigurationImportSelector
,发现其实现了DeferredImportSelector
,再点开DeferredImportSelector
,发现其继承了ImportSelector
。根据我们上文关于ImportSelector
的讨论,我们主要关注其selectImports
方法。内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package org.springframework.boot.autoconfigure;【部分代码略】 public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware , ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered { 【部分代码略】 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } 【部分代码略】 }
点进!isEnabled(annotationMetadata)
,内容如下:
1 2 3 4 5 6 7 8 String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; protected boolean isEnabled (AnnotationMetadata metadata) { if (getClass() == AutoConfigurationImportSelector.class ) { return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class , true ) ; } return true ; }
即isEnabled(annotationMetadata)
的含义是,检查自动配置是否开启,默认是开启的。
getAutoConfigurationEntry
点进getAutoConfigurationEntry(annotationMetadata)
,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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); }
if
之后的第一行代码,AnnotationAttributes attributes = getAttributes(annotationMetadata);
的作用是获取@EnableAutoConfiguration
注解的属性,有exclude
和excludeName
两个。
if
之后的最后一行代码,点进new AutoConfigurationEntry(configurations, exclusions)
,内容如下:
1 2 3 4 AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) { this .configurations = new ArrayList<>(configurations); this .exclusions = new HashSet<>(exclusions); }
只是组装成一个实体类进行返回。
其他代码,可以分为四个部分:
加载自动配置组件:1 2 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations);
排除指定组件:1 2 3 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);
过滤自动配置组件:1 configurations = getConfigurationClassFilter().filter(configurations);
事件注册:1 fireAutoConfigurationImportEvents(configurations, exclusions);
加载自动配置组件
1 2 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations);
getCandidateConfigurations
点进getCandidateConfigurations
方法:
1 2 3 4 5 6 7 8 9 protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = new ArrayList<>( SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader())); ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you " + "are using a custom packaging, make sure that file is correct." ); return configurations; }
SpringFactoriesLoader.loadFactoryNames
方法,有两个参数
第一个是自动装配类:1 2 3 protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class ; }
第二个是类加载器:1 2 3 protected ClassLoader getBeanClassLoader () { return this .beanClassLoader; }
也就是说,loadFactoryNames
只会读取针对自动配置的注册类。
点进SpringFactoriesLoader.loadFactoryNames
方法:
1 2 3 4 5 6 7 8 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()); }
点进loadSpringFactories
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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()); } } } 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; }
这一段代码很长,大致的含义就是加载指定的ClassLoader下面的所有FACTORIES_RESOURCE_LOCATION
位置的文件,并将文件解析内容保存在一个Map<String, List<String>>
的对象中,最后返回该对象。
我们点进FACTORIES_RESOURCE_LOCATION
:
1 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;
找一个Spring相关的Jar包看看META-INF/spring.factories
文件内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 org.springframework.boot.logging.LoggingSystemFactory =\ org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\ org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\ org.springframework.boot.logging.java.JavaLoggingSystem.Factory org.springframework.boot.env.PropertySourceLoader =\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader org.springframework.boot.context.config.ConfigDataLocationResolver =\ org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\ org.springframework.boot.context.config.StandardConfigDataLocationResolver 【部分代码略】
还有一行代码
1 ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
作用是,从需要自动装配的Jar包的META-INF/spring/%s.imports
下找到对应的文件信息,把他加入到configurations
集合中。
removeDuplicates
1 configurations = removeDuplicates(configurations);
因为程序默认加载的是ClassLoader下面的所有META-INF/spring.factories
文件中的配置,难免在不同的Jar包中出现重复的配置。
所以,还有一个方法,removeDuplicates
,去重:
1 2 3 protected final <T> List<T> removeDuplicates (List<T> list) { return new ArrayList<>(new LinkedHashSet<>(list)); }
去重,在我们的开发工作中,是一个很常见的现象。而SpringBoot的去重方法,非常值得我们参考。
排除指定组件
1 2 3 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);
getExclusions
通过"加载自动配置组件",我们获取了spring.factories
文件中注册的自动加载组件,但如果在实际应用的过程中并不需要某些组件,可以排除。
可以通过配置@EnableAutoConfiguration
的注解属性exclude
或excludeName
进行有针对性的排除。
可以通过配置文件进行排除。
我们点进getExclusions
:
1 2 3 4 5 6 7 protected Set<String> getExclusions (AnnotationMetadata metadata, AnnotationAttributes attributes) { Set<String> excluded = new LinkedHashSet<>(); excluded.addAll(asList(attributes, "exclude" )); excluded.addAll(asList(attributes, "excludeName" )); excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; }
其中excluded.addAll(asList(attributes, "exclude"));
和excluded.addAll(asList(attributes, "excludeName"));
实现了:
通过@EnableAutoConfiguration
的注解属性exclude
或excludeName
进行有针对性的排除
再点进getExcludeAutoConfigurationsProperty
:
1 2 3 4 5 6 7 8 9 10 11 12 13 protected List<String> getExcludeAutoConfigurationsProperty () { Environment environment = getEnvironment(); if (environment == null ) { return Collections.emptyList(); } if (environment instanceof ConfigurableEnvironment) { Binder binder = Binder.get(environment); return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList) .orElse(Collections.emptyList()); } String[] excludes = environment.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class ) ; return (excludes != null ) ? Arrays.asList(excludes) : Collections.emptyList(); }
该段代码实现了:
checkExcludedClasses
点进checkExcludedClasses
方法:
1 2 3 4 5 6 7 8 9 10 11 private void checkExcludedClasses (List<String> configurations, Set<String> exclusions) { List<String> invalidExcludes = new ArrayList<>(exclusions.size()); for (String exclusion : exclusions) { if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) { invalidExcludes.add(exclusion); } } if (!invalidExcludes.isEmpty()) { handleInvalidExcludes(invalidExcludes); } }
点进handleInvalidExcludes
方法:
1 2 3 4 5 6 7 8 9 protected void handleInvalidExcludes (List<String> invalidExcludes) { StringBuilder message = new StringBuilder(); for (String exclude : invalidExcludes) { message.append("\t- " ).append(exclude).append(String.format("%n" )); } throw new IllegalStateException(String.format( "The following classes could not be excluded because they are not auto-configuration classes:%n%s" , message)); }
checkExcludedClasses
方法用来确保被排除的类存在于当前的ClassLoader中,并且包含在spring.factories
注册的集合中。如果不满足以上条件,调用handlefnvalidExcludes
方法拋出异常。
configurations.removeAll
1 configurations.removeAll(exclusions);
该方法从自动配置集合中移除被排除集合的类。
configurations
的类型是List<String>
exclusions
的类型是Set<String>
从某个集合中剔除一些内容,在我们的开发工作中,是一个很常见的现象。而SpringBoot的剔除方法,非常值得我们参考。
过滤自动配置组件
1 configurations = getConfigurationClassFilter().filter(configurations);
getConfigurationClassFilter
点进getConfigurationClassFilter
方法:
1 2 3 4 5 6 7 8 9 10 private ConfigurationClassFilter getConfigurationClassFilter () { if (this .configurationClassFilter == null ) { List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters(); for (AutoConfigurationImportFilter filter : filters) { invokeAwareMethods(filter); } this .configurationClassFilter = new ConfigurationClassFilter(this .beanClassLoader, filters); } return this .configurationClassFilter; }
再点进List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
的getAutoConfigurationImportFilters()
:
1 2 3 protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters () { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class , this .beanClassLoader ) ; }
即,获取spring.factories
中Key为AutoConfigurationImportFilter
的Filters列表。
AutoConfigurationImportFilter
的相关内容:
1 2 3 4 5 org.springframework.boot.autoconfigure.AutoConfigurationImportFilter =\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
默认配置了三个筛选条件:OnBeanCondition
、OnClassCondition
、和OnWebApplicationCondition
。
再点进this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
,这是一个构造方法:
1 2 3 4 5 6 7 8 9 10 11 private static class ConfigurationClassFilter { private final AutoConfigurationMetadata autoConfigurationMetadata; private final List<AutoConfigurationImportFilter> filters; ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) { this .autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader); this .filters = filters; } }
即,上文的三个筛选条件,最后作为了ConfigurationClassFilter
对象的成员变量filters
。
filter
点进filter
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 List<String> filter (List<String> configurations) { long startTime = System.nanoTime(); String[] candidates = StringUtils.toStringArray(configurations); boolean skipped = false ; for (AutoConfigurationImportFilter filter : this .filters) { boolean [] match = filter.match(candidates, this .autoConfigurationMetadata); for (int i = 0 ; i < match.length; i++) { if (!match[i]) { candidates[i] = null ; skipped = true ; } } } if (!skipped) { return configurations; } List<String> result = new ArrayList<>(candidates.length); for (String candidate : candidates) { if (candidate != null ) { result.add(candidate); } } if (logger.isTraceEnabled()) { int numberFiltered = configurations.size() - result.size(); logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms" ); } return result; }
遍历成员变量filters
,并且参与到boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
。
filter.match
方法有两个参数,candidates
就来自入参configurations
,还有一个入参是this.autoConfigurationMetadata
。
autoConfigurationMetadata
来自ConfigurationClassFilter
的构造方法:
1 2 3 4 5 6 7 8 9 10 11 12 private static class ConfigurationClassFilter { private final AutoConfigurationMetadata autoConfigurationMetadata; private final List<AutoConfigurationImportFilter> filters; ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) { this .autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader); this .filters = filters; } 【部分代码略】 }
再点进this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
:
1 2 3 4 5 protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties" ;static AutoConfigurationMetadata loadMetadata (ClassLoader classLoader) { return loadMetadata(classLoader, PATH); }
即,autoConfigurationMetadata
,来自spring-autoconfigure-metadata.propertics
中配置对应实体类。
我们找一个spring-autoconfigure-metadata.propertics
看看:
1 2 3 4 5 6 7 8 9 10 11 12 org.springframework.boot.autoconfigure.AutoConfiguration =org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration =org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration.AutoConfigureAfter =org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration =org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration.ConditionalOnClass =org.springframework.amqp.rabbit.annotation.EnableRabbit org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration =org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$MessagingTemplateConfiguration =org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$MessagingTemplateConfiguration.ConditionalOnClass =org.springframework.amqp.rabbit.core.RabbitMessagingTemplate org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass =com.rabbitmq.client.Channel,org.springframework.amqp.rabbit.core.RabbitTemplate org.springframework.boot.autoconfigure.amqp.RabbitStreamConfiguration =【部分代码略】
过滤条件为该列表中自动配置类的注解得包含在OnBeanCondition
、OnClassCondition
和OnWebApplicationCondition
中指定的注解,即,得包含@ConditionalOnBean
、@ConditionalOnClass
和@ConditionalOnWebApplication
。
事件注册
1 fireAutoConfigurationImportEvents(configurations, exclusions);
点进fireAutoConfigurationImportEvents
方法:
1 2 3 4 5 6 7 8 9 10 private void fireAutoConfigurationImportEvents (List<String> configurations, Set<String> exclusions) { List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners(); if (!listeners.isEmpty()) { AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this , configurations, exclusions); for (AutoConfigurationImportListener listener : listeners) { invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event); } } }
点进getAutoConfigurationImportListeners
方法:
1 2 3 protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners () { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class , this .beanClassLoader ) ; }
过程如下:
通过SpringFactoriesLoader
类提供的loadFactories
方法将spring.factories
中配置的接口AutoConfigurationImportListener
的实现类加载出来。
将筛选出的自动配置类集合和被排除的自动配置类集合封装成AutoConfigurationimportEvent
事件对象,并传人该事件对象通过监听器提供的onAutoConfigurationImportEvent
方法。
进行事件广播。
构造流程
分析
我们从一个引导类的main
方法开始,示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.kakawanyifan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class , args ) ; } }
点进其中的run
方法:
1 2 3 4 5 6 7 8 public static ConfigurableApplicationContext run (Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
这里有两个run
方法,我们可以打上断点,会先在第一个上暂停,然后在第二个上暂停。
点进第二个的new SpringApplication(primarySources)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public SpringApplication (Class<?>... primarySources) { this (null , primarySources); } @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)); this .webApplicationType = WebApplicationType.deduceFromClasspath(); this .bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class )) ; setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class )) ; setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class )) ; this .mainApplicationClass = deduceMainApplicationClass(); }
也有两个,也是会先在第一个上暂停,然后在第二个上暂停。
重点关注第二个方法的如下几行:
赋值成员变量resourceLoader
:1 this .resourceLoader = resourceLoader;
赋值成员变量primarySources
:1 this .primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
推断Web应用类型:1 this .webApplicationType = WebApplicationType.deduceFromClasspath();
加载并初始化ApplicationContextInitializer
及其相关实现类:1 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class )) ;
加载并初始化ApplicationListener
及其相关实现类:1 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class )) ;
推断main
方法:1 this .mainApplicationClass = deduceMainApplicationClass();
推断Web应用类型
点进WebApplicationType.deduceFromClasspath()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet" , "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet" ;private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler" ;private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer" ;static WebApplicationType deduceFromClasspath () { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null )) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null )) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
deduceFromClasspath
的推断逻辑如下:
如果DispatcherHandler
存在,并且DispatcherServlet
和ServleContainer
都不存在:
否则:
如果SERVLET
或ConfigurableWebApplicationContext
任何一个不存在:
否则:
说明当前应用为SERVLET应用,返回SERVLET
。
ApplicationContextInitializer
1 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class )) ;
该部分其实有两个方法:setInitializers
和getSpringFactoriesInstances
。
getSpringFactoriesInstances
点进getSpringFactoriesInstances
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 private <T> Collection<T> getSpringFactoriesInstances (Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
其中SpringFactoriesLoader.loadFactoryNames(type, classLoader)
获取的是spring.factories
中注册的对应配置。
我们可以打断点看一下,type的值:
1 org.springframework.context.ApplicationContextInitializer
找一个spring.factories
看一下,内容如下:
1 2 3 4 5 6 7 org.springframework.context.ApplicationContextInitializer =\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
点进createSpringFactoriesInstances
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private <T> List<T> createSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); } } return instances; }
重点关注如下几行:
获取Class:1 Class<?> instanceClass = ClassUtils.forName(name, classLoader);
获取构造方法:1 Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
生成对象:1 T instance = (T) BeanUtils.instantiateClass(constructor, args);
setInitializers
在完成获取配置类集合和实例化操作之后,调用setinitializers
方法将实例化的集合添加到SpringApplication
的成员变量initializers
中。
1 2 3 public void setInitializers (Collection<? extends ApplicationContextInitializer<?>> initializers) { this .initializers = new ArrayList<>(initializers); }
ApplicationListener
1 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class )) ;
ApplicationListener
的整个配置和加载流程与ApplicationContextlnitializer
完全一致,也是先通过SpringFactoriesLoader
的loadFactoryNames
方法获取spring.factories
中对应配置,然后再进行实例化,最后将获得的结果集合添加到SpringApplication
的成员
变量listeners
中。
我们可以点击方法看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 private <T> Collection<T> getSpringFactoriesInstances (Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
推断main方法
1 this .mainApplicationClass = deduceMainApplicationClass();
点进deduceMainApplicationClass()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main" .equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { } return null ; }
main
方法的推断逻辑如下:
创建一个运行时异常,然后获得栈数组
遍历栈数组:
判断类的方法中是否包含main方法,第一个被匹配的类会通过Class.forName
方法创建对象,并将其被返回
最后,会通过this.mainApplicationClass = deduceMainApplicationClass();
,将对象赋值给SpringApplication
的成员变量mainApplicationClass
。
运行流程
分析
同样,我们从一个引导类的main
方法开始,其中很多内容,我们在上文已经讨论过了。
1 2 3 public static ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
这次重点关注其中的run(args)
,点进run(args)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public ConfigurableApplicationContext run (String... args) { long startTime = System.nanoTime(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null ; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this .mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); context.setApplicationStartup(this .applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this .logStartupInfo) { new StartupInfoLogger(this .mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } listeners.started(context, timeTakenToStartup); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null ); throw new IllegalStateException(ex); } return context; }
重点关注如下方法:
获得监听器数组:1 SpringApplicationRunListeners listeners = getRunListeners(args);
初始化ApplicationArguments
:1 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
初始化ConfigurableEnvironment
:1 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
应用上下文的创建:1 context = createApplicationContext();
应用上下文的准备:1 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
应用上下文的刷新:1 refreshContext(context);
执行所有运行器:1 callRunners(context, applicationArguments);
获得监听器数组
1 SpringApplicationRunListeners listeners = getRunListeners(args);
为什么说是数组?
我们查看SpringApplicationRunListeners
的构造方法:
1 2 3 4 5 6 SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners, ApplicationStartup applicationStartup) { this .log = log; this .listeners = new ArrayList<>(listeners); this .applicationStartup = applicationStartup; }
配置与加载
点进getRunListeners(args)
方法:
1 2 3 4 5 6 private SpringApplicationRunListeners getRunListeners (String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class , types , this , args ), this .applicationStartup ) ;}
关于SpringApplicationRunListeners
构造方法,我们不再讨论。主要关注上文的getSpringFactoriesInstances
方法:
1 2 3 4 5 6 7 8 private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
其中的SpringFactoriesLoader.loadFactoryNames
方法,我们在上文已经讨论了很多遍。这里打个断点,看看type
的值:
1 org.springframework.boot.SpringApplicationRunListener
找一个spring.factories
看一下,内容如下:
1 2 3 org.springframework.boot.SpringApplicationRunListener =\ org.springframework.boot.context.event.EventPublishingRunListener
关于createSpringFactoriesInstances
方法,与之前讨论的几乎一致,这里不赘述。
SpringApplicationRunListener接口
我们再来简单的分析一下SpringApplicationRunListener
接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 package org.springframework.boot;import java.time.Duration;import org.springframework.context.ApplicationContext;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.core.env.ConfigurableEnvironment;import org.springframework.core.io.support.SpringFactoriesLoader;public interface SpringApplicationRunListener { default void starting (ConfigurableBootstrapContext bootstrapContext) { } default void environmentPrepared (ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { } default void contextPrepared (ConfigurableApplicationContext context) { } default void contextLoaded (ConfigurableApplicationContext context) { } default void started (ConfigurableApplicationContext context, Duration timeTaken) { started(context); } @Deprecated default void started (ConfigurableApplicationContext context) { } default void ready (ConfigurableApplicationContext context, Duration timeTaken) { running(context); } @Deprecated default void running (ConfigurableApplicationContext context) { } default void failed (ConfigurableApplicationContext context, Throwable exception) { } }
根据注释,run
方法与SpringApplicationRunListener
的方法之间的关系如下:
EventPublishingRunListener
在上文,我们还提到了位于spring.factories
的如下内容:
1 2 3 org.springframework.boot.SpringApplicationRunListener =\ org.springframework.boot.context.event.EventPublishingRunListener
EventPublishingRunListener
是对于SpringApplicationRunListener
的唯一实现类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package org.springframework.boot.context.event;【部分代码略】 public class EventPublishingRunListener implements SpringApplicationRunListener , Ordered { private final SpringApplication application; private final String[] args; private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener (SpringApplication application, String[] args) { this .application = application; this .args = args; this .initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this .initialMulticaster.addApplicationListener(listener); } } @Override public int getOrder () { return 0 ; } @Override public void starting (ConfigurableBootstrapContext bootstrapContext) { this .initialMulticaster .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this .application, this .args)); } @Override public void started (ConfigurableApplicationContext context, Duration timeTaken) { context.publishEvent(new ApplicationStartedEvent(this .application, this .args, context, timeTaken)); ailabilityChangeEvent.publish(context, LivenessState.CORRECT); } 【部分代码略】 }
我们观察其构造方法:
application
:类型为SpringApplication
,是当前运行的SpringApplication
实例。
args
:启动程序时的命令参数。
initialMulticaster
:类型为SimpleApplicationEventMulticaster
,事件广播器。
最后,还有一段代码,作用是:遍历ApplicationListener
并关联SimpleApplicationEventMulticaster
。
再观察起监听作用的代码,starting
和started
,其逻辑是:
程序启动到某个步骤后,调用EventPublishingRunListener
的某个方法(例如,starting
)。
EventPublishingRunListener
的具体方法将application
参数和args
参数封装到对应的事件中。这里的事件均为SpringApplicationEvent
的实现类。
通过成员变量initialMulticaster
的multicastEvent
方法对事件进行广播,或通过该方法的ConfigurableApplicationContext
的参数的publishEvent
方法来对事件进行发布
对应的ApplicationListener
被触发,执行相应的业务逻辑。
ApplicationArguments
1 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ApplicationArguments
的初始化过程非常简单,只是调用了其实现类DefaultApplicationArguments
的构造方法,并传人了main
方法中的args
参数。
ConfigurableEnvironment
1 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
prepareEnvironment
点进prepareEnvironment
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix" ), "Environment prefix cannot be set via properties." ); bindToSpringApplication(environment); if (!this .isCustomEnvironment) { EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader()); environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
我们重点关注:
获取或创建环境:1 ConfigurableEnvironment environment = getOrCreateEnvironment();
配置环境:1 configureEnvironment(environment, applicationArguments.getSourceArgs());
我们还会看到有这么一行
1 listeners.environmentPrepared(bootstrapContext, environment);
点进去的话,就是SpringApplicationRunListener
接口的实现类的environmentPrepared
方法。这也印证了我们上文:说的run
方法与SpringApplicationRunListener
的方法之间的关系。
获取或创建环境
点进getOrCreateEnvironment();
的方法:
1 2 3 4 5 6 7 8 9 10 private ConfigurableEnvironment getOrCreateEnvironment () { if (this .environment != null ) { return this .environment; } ConfigurableEnvironment environment = this .applicationContextFactory.createEnvironment(this .webApplicationType); if (environment == null && this .applicationContextFactory != ApplicationContextFactory.DEFAULT) { environment = ApplicationContextFactory.DEFAULT.createEnvironment(this .webApplicationType); } return (environment != null ) ? environment : new ApplicationEnvironment(); }
逻辑:
配置环境
点进configureEnvironment
的方法:
1 2 3 4 5 6 7 protected void configureEnvironment (ConfigurableEnvironment environment, String[] args) { if (this .addConversionService) { environment.setConversionService(new ApplicationConversionService()); } configurePropertySources(environment, args); configureProfiles(environment, args); }
转换服务
if (this.addConversionService)
,判断是否需要添加转换服务。
我们点进ApplicationConversionService()
,看看到底什么是转换服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public ApplicationConversionService () { this (null ); } public ApplicationConversionService (StringValueResolver embeddedValueResolver) { this (embeddedValueResolver, false ); } private ApplicationConversionService (StringValueResolver embeddedValueResolver, boolean unmodifiable) { if (embeddedValueResolver != null ) { setEmbeddedValueResolver(embeddedValueResolver); } configure(this ); this .unmodifiable = unmodifiable; }
点进configure(this)
方法:
1 2 3 4 5 6 public static void configure (FormatterRegistry registry) { DefaultConversionService.addDefaultConverters(registry); DefaultFormattingConversionService.addDefaultFormatters(registry); addApplicationFormatters(registry); addApplicationConverters(registry); }
例如,点进其中的DefaultConversionService.addDefaultConverters(registry)
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void addDefaultConverters (ConverterRegistry converterRegistry) { addScalarConverters(converterRegistry); addCollectionConverters(converterRegistry); converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new StringToTimeZoneConverter()); converterRegistry.addConverter(new ZoneIdToTimeZoneConverter()); converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter()); converterRegistry.addConverter(new ObjectToObjectConverter()); converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new FallbackObjectToStringConverter()); converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry)); }
即,是各种数据类型的转换。
1 configurePropertySources(environment, args);
点进configurePropertySources
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 protected void configurePropertySources (ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (!CollectionUtils.isEmpty(this .defaultProperties)) { DefaultPropertiesPropertySource.addOrMerge(this .defaultProperties, sources); } if (this .addCommandLineProperties && args.length > 0 ) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource( new SimpleCommandLinePropertySource("springApplicationCommandLineArgs" , args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } }
这段代码需要重点看一下参数的优先级处理和默认参数与命令参数之间的关系:
如果存在默认属性配置,则将默认属性配置放置在最后,也就是说优先级最低。
如果存在命令参数:
如果命令的参数已经存在于属性配置中
如果命令的参数并不存在于属性配置中
(这也印证了我们在《21.SpringBoot [1/3]》 说的,命令行的优先级最高。)
1 configureProfiles(environment, args);
在2.7.7版本的SpringBoot中,这个方法是空的。
1 2 protected void configureProfiles (ConfigurableEnvironment environment, String[] args) {}
但在更早的版本中,其中应该是有内容的。
应用上下文的创建
1 context = createApplicationContext();
点进createApplicationContext()
方法:
1 2 3 protected ConfigurableApplicationContext createApplicationContext () { return this .applicationContextFactory.create(this .webApplicationType); }
点进create
方法,我们会发现这是一个接口类中的方法。
具体我们不讨论,总之就是根据我们的应用类型,创建上下文。
(在《未分类【计算机】:IDEA中Debug的方法和技巧》 中,我们讨论过方法断点,这里我们直接在方法上打断点,然后一步一步,看其逻辑。)
应用上下文的准备
1 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
prepareContext
点进prepareContext
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private void prepareContext (DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); bootstrapContext.close(context); if (this .logStartupInfo) { logStartupInfo(context.getParent() == null ); logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } if (beanFactory instanceof AbstractAutowireCapableBeanFactory) { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this .allowCircularReferences); if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this .allowBeanDefinitionOverriding); } } if (this .lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context)); Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); load(context, sources.toArray(new Object[0 ])); listeners.contextLoaded(context); }
我们主要关注:
对context
设置environment
:1 context.setEnvironment(environment);
应用上下文后置处理:1 postProcessApplicationContext(context);
初始化context
:1 applyInitializers(context);
获得ConfigurableListableBeanFactory,并注册单例对象1 2 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments);
获取全部配置源:1 Set<Object> sources = getAllSources();
将配置源加载到context
:1 load(context, sources.toArray(new Object[0 ]));
其中,有些方法很简单,甚至就一行代码。我们挑选部分逻辑复杂一些的进行讨论。
应用上下文后置处理
1 postProcessApplicationContext(context);
点进postProcessApplicationContext
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected void postProcessApplicationContext (ConfigurableApplicationContext context) { if (this .beanNameGenerator != null ) { context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this .beanNameGenerator); } if (this .resourceLoader != null ) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context).setResourceLoader(this .resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context).setClassLoader(this .resourceLoader.getClassLoader()); } } if (this .addConversionService) { context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService()); } }
postProcessApplicationContext
方法,完成操作包括beanNameGenerator
、resourceLoader
、ClassLoader
和ConversionService
的设置。
获取全部配置源
在进入该逻辑之前,获得了ConfigurableListableBeanFactory
,并注册单例对象,并根据逻辑,设置了一些属性。
1 Set<Object> sources = getAllSources();
1 2 3 4 5 6 7 8 9 10 public Set<Object> getAllSources () { Set<Object> allSources = new LinkedHashSet<>(); if (!CollectionUtils.isEmpty(this .primarySources)) { allSources.addAll(this .primarySources); } if (!CollectionUtils.isEmpty(this .sources)) { allSources.addAll(this .sources); } return Collections.unmodifiableSet(allSources); }
以上操作逻辑很简单,如果Set集合中不存在primarySources
配置源或sources
配置源,则将其添加人Set
中,最后将Set设置为不可修改。
primarySources
来自SpringApplication
的构造参数
sources
来自setResources
方法。
将配置源加载到context
load
1 load(context, sources.toArray(new Object[0 ]));
点进load
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected void load (ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this .beanNameGenerator != null ) { loader.setBeanNameGenerator(this .beanNameGenerator); } if (this .resourceLoader != null ) { loader.setResourceLoader(this .resourceLoader); } if (this .environment != null ) { loader.setEnvironment(this .environment); } loader.load(); }
createBeanDefinitionLoader
点进createBeanDefinitionLoader
方法:
1 2 3 protected BeanDefinitionLoader createBeanDefinitionLoader (BeanDefinitionRegistry registry, Object[] sources) { return new BeanDefinitionLoader(registry, sources); }
我们发现,该方法主要通过BeanDefinitionLoader
来完成配置资源的加载操作。
再点进BeanDefinitionLoader
方法:
1 2 3 4 5 6 7 8 9 10 BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { Assert.notNull(registry, "Registry must not be null" ); Assert.notEmpty(sources, "Sources must not be empty" ); this .sources = sources; this .annotatedReader = new AnnotatedBeanDefinitionReader(registry); this .xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null ); this .groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null ); this .scanner = new ClassPathBeanDefinitionScanner(registry); this .scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }
通过BeanDefinitionLoader
的构造方法我们可以看到BeanDefinitionLoader
支持基于AnnotatedBeanDefinitionReader
、XmlBeanDefinitionReader
、GroovyBeanDefinitionReader
等多种类型的加载操作。
最后返回loader
。
loader.load()
再点进loader.load()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void load () { for (Object source : this .sources) { load(source); } } private void load (Object source) { Assert.notNull(source, "Source must not be null" ); if (source instanceof Class<?>) { load((Class<?>) source); return ; } if (source instanceof Resource) { load((Resource) source); return ; } if (source instanceof Package) { load((Package) source); return ; } if (source instanceof CharSequence) { load((CharSequence) source); return ; } throw new IllegalArgumentException("Invalid source type " + source.getClass()); }
从以上代码可以看出,BeanDefinitionLoader
加载支持的范围包括:Class
、Resource
、Package
和CharSequence
四种。
应用上下文的刷新
1 refreshContext(context);
点进refreshContext
方法:
1 2 3 4 5 6 private void refreshContext (ConfigurableApplicationContext context) { if (this .registerShutdownHook) { shutdownHook.registerApplicationContext(context); } refresh(context); }
再点进refresh
方法:
1 2 3 protected void refresh (ConfigurableApplicationContext applicationContext) { applicationContext.refresh(); }
其中refresh
方法调用的是AbstractApplicationContext
中的refresh
方法,该类属于spring-context
包,方法内部具体逻辑,我们不作讨论。
执行所有运行器
1 callRunners(context, applicationArguments);
点进callRunners
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private void callRunners (ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class ).values ()) ; runners.addAll(context.getBeansOfType(CommandLineRunner.class ).values ()) ; AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } } private void callRunner (ApplicationRunner runner, ApplicationArguments args) { try { (runner).run(args); } catch (Exception ex) { throw new IllegalStateException("Failed to execute ApplicationRunner" , ex); } } private void callRunner (CommandLineRunner runner, ApplicationArguments args) { try { (runner).run(args.getSourceArgs()); } catch (Exception ex) { throw new IllegalStateException("Failed to execute CommandLineRunner" , ex); } }
过程:
从context
中获得类型为ApplicationRunner
和CommandLineRunmer
的Bean
,将它们放入List列表中并进行排序。
去重,同时遍历,依次执行。
最后,我们讨论一下,什么是Runner运行器。
Runner运行器分为两种:
其作用都是在SpringBoot应用程序启动后执行代码,可以使用这些接口在应用程序启动后立即执行一些操作。 两种除了参数不同,其它没区别。
应用程序运行器,实现ApplicationRunner
接口,重写其run
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.kakawanyifan;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class Application implements ApplicationRunner { public static void main (String[] args) { SpringApplication.run(Application.class , args ) ; } @Override public void run (ApplicationArguments args) throws Exception { System.out.println("Hello World from Application Runner" ); } }
运行结果:
1 2 2023-01-31 18:03:38.748 INFO 24269 --- [ restartedMain] com.kakawanyifan.Application : Started Application in 2.139 seconds (JVM running for 2.584) Hello World from Application Runner
命令行运行器,实现CommandLineRunner
接口,重写其run
方法:
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.kakawanyifan;import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class Application implements CommandLineRunner { public static void main (String[] args) { SpringApplication.run(Application.class , args ) ; } @Override public void run (String... args) throws Exception { System.out.println("Hello world from Command Line Runner" ); } }
运行结果:
1 2 2023-01-31 18:05:38.244 INFO 24300 --- [ restartedMain] com.kakawanyifan.Application : Started Application in 2.064 seconds (JVM running for 2.521) Hello world from Command Line Runner