1. 自动配置工作流程
1.1 bean的加载方式
- xml+<bean>(
配置文件)
- xml:context+注解(@Component+4个@Bean)(
注解扫描)
- 配置类+扫描+注解(@Component+4个@Bean)(
配置类)
- @Bean定义FactoryBean接口(
可以在变成bean之前做点事)
- @ImportResource(
导入配置)
- @Configuration注解的proxyBeanMethods属性(
代理对象,默认多个)
- @Import导入bean的类(
在配置类上注入class)
- @Import导入配置类(
在配置类上注入class)
- AnnotationConfigApplicationContext调用register方法(
容器创建之后注册bean)
- @Import导入ImportSelector接口(
选择 bean 的方式)
- @Import导入ImportBeanDefinitionRegistrar接口(
控制 bean 的相关属性)
- @Import导入BeanDefinitionRegistryPostProcessor接口(
bean的最终裁决)
最初级的bean的加载方式其实可以直击spring管控bean的核心思想,就是提供类名,然后spring就可以管理了
所以第一种方式就是给出bean的类名,至于内部嘛就是反射机制加载成class
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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="cat" class="Cat"/> <bean class="Dog"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/> <bean class="com.alibaba.druid.pool.DruidDataSource"/> </beans>
|
1 2 3 4 5 6 7 8 9
| ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationCOntext1.xml");
String[] names = ctx.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); }
|
由于方式一种需要将spring管控的bean全部写在xml文件中,对于程序员来说非常不友好
所以就有了第二种方式。哪一个类要受到spring管控加载成bean,就在这个类的上面加一个注解,还可以顺带起一个bean的名字(id)。
这里可以使用的注解有@Component以及三个衍生注解@Service、@Controller、@Repository。
1 2 3
| @Component("tom") public class Cat { }
|
由于我们无法在第三方提供的技术源代码中去添加上述4个注解
因此当你需要加载第三方开发的bean的时候可以使用下列方式定义注解式的bean
@Bean定义在一个方法上方,当前方法的返回值就可以交给spring管控
记得这个方法所在的类一定要定义在@Component修饰的类中
1 2 3 4 5 6 7 8
| @Component public class DbConfig { @Bean public DruidDataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); return ds; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <context:component-scan base-package="cn.jyw.bean,cn.jyw.config"/> </beans>
|
1 2 3 4 5
| ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationCOntext2.xml"); String[] names = ctx.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); }
|
使用java类替换掉xml的配置
定义一个类并使用@ComponentScan替代原始xml配置中的包扫描这个动作,其实功能基本相同
1 2 3 4 5 6 7
| @ComponentScan({"cn.jyw.bean","cn.jyw.config"}) public class SpringConfig3 { @Bean public DogFactoryBean dog(){ return new DogFactoryBean(); } }
|
1 2 3 4 5 6
| ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig3.class); String[] names = ctx.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); }
|
使用FactroyBean接口
造出来的bean并不是DogFactoryBean,而是Dog
可以在对象初始化前做一些事情,下例中的注释位置就是让你自己去扩展要做的其他事情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class DogFactoryBean implements FactoryBean<Dog> { @Override public Dog getObject() throws Exception { Dog d = new Dog(); return d; } @Override public Class<?> getObjectType() { return Dog.class; } @Override public boolean isSingleton() { return true; } }
|
1 2 3 4 5 6 7
| @ComponentScan({"cn.jyw.bean","cn.jyw.config"}) public class SpringConfig3 { @Bean public DogFactoryBean dog(){ return new DogFactoryBean(); } }
|
注解格式导入XML格式配置的bean
@ImportResource,在配置类上直接写上要被融合的xml配置文件名
就可以融合配置文件
1 2 3 4
| @Configuration @ImportResource("applicationContext1.xml") public class SpringConfig32 { }
|
1 2 3 4 5
| ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig32.class); String[] names = ctx.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); }
|
proxyBeanMethods属性
前面的例子中用到了@Configuration这个注解,当我们使用AnnotationConfigApplicationContext加载配置类的时候,配置类可以不添加这个注解。但是这个注解有一个更加强大的功能,它可以保障配置类中使用方法创建的bean的唯一性。为@Configuration注解设置proxyBeanMethods属性值为true即可,由于此属性默认值为true,所以很少看见明确书写的,除非想放弃此功能。
1 2 3 4 5 6 7
| @Configuration(proxyBeanMethods = true) public class SpringConfig33 { @Bean public Cat cat(){ return new Cat(); } }
|
下面通过容器再调用上面的cat方法时,得到的就是同一个对象了。注意,必须使用spring容器对象调用此方法才有保持bean唯一性的特性。
1 2 3 4 5 6 7 8 9 10 11
| ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig33.class); String[] names = ctx.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } System.out.println("-------------------------"); SpringConfig33 springConfig33 = ctx.getBean("springConfig33", SpringConfig33.class); System.out.println(springConfig33.cat()); System.out.println(springConfig33.cat()); System.out.println(springConfig33.cat()); }
|
使用@Import注解一种精准制导的加载方式
直接指定类
1 2 3
| @Import({Dog.class,DbConfig.class}) public class SpringConfig4 { }
|
1 2 3 4 5
| ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig4.class); String[] names = ctx.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); }
|
前面介绍的加载bean的方式都是在容器启动阶段完成bean的加载,下面这种方式就比较特殊了,可以在容器初始化完成后手动加载bean。通过这种方式可以实现编程式控制bean的加载。
1 2 3 4 5 6 7 8
| AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); ctx.register(Mouse.class); ctx.registerBean("tom", Cat.class,0); ctx.registerBean("tom", Cat.class,1); ctx.registerBean("tom", Cat.class,2);
|
容器初始化过程中进行控制
一种控制bean加载的方式,或者说是选择bean的方式
1 2 3 4 5 6 7 8 9 10 11
| public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata metadata) { boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Configuration"); if(flag){ return new String[]{"cn.jyw.bean.Dog"}; } return new String[]{"cn.jyw.bean.Cat"}; } }
|
1 2 3 4 5
| @Configuration
@Import(MyImportSelector.class) public class SpringConfig6 { }
|
1 2 3 4 5
| ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig6.class); String[] names = ctx.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); }
|
spring中定义了一个叫做BeanDefinition的东西,它才是控制bean初始化加载的核心
BeanDefinition接口中给出了若干种方法,可以控制bean的相关属性
说个最简单的,创建的对象是单例还是非单例,在BeanDefinition中定义了scope属性就可以控制这个
1 2 3 4 5 6 7 8
| public class MyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition(); registry.registerBeanDefinition("bookService",beanDefinition); } }
|
BeanDefinition意思是bean定义,Registry注册的意思,Post后置,Processor处理器,全称bean定义后处理器
对容器中的bean进行最终裁定
1 2 3 4 5 6 7 8
| public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl4.class).getBeanDefinition(); registry.registerBeanDefinition("bookService",beanDefinition); } }
|
后面加载的会覆盖前面加载的bean, 但是继承BeanDefinitionRegistryPostProcessor的类有最终裁定权
1 2 3
| @Import({BookServiceImpl1.class, MyPostProcessor.class, MyRegistrar2.class, MyRegistrar.class}) public class SpringConfig8 { }
|
1 2 3
| ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig8.class); BookSerivce bookService = ctx.getBean("bookService", BookSerivce.class); bookService.check();
|
总结
- bean的定义由前期xml配置逐步演化成注解配置,本质是一样的,都是通过反射机制加载类名后创建对象,对象就是spring管控的bean
- @Import注解可以指定加载某一个类作为spring管控的bean,如果被加载的类中还具有@Bean相关的定义,会被一同加载
- spring开放出了若干种可编程控制的bean的初始化方式,通过分支语句由固定的加载bean转成了可以选择bean是否加载或者选择加载哪一种bean
1.2 bean的加载控制
企业级开发中不可能在spring容器中进行bean的饱和式加载的
应该是用什么技术就加载什么bean
所以在spring容器中,通过判定是否加载了某个类来控制某些bean的加载是一种常见操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { try { Class<?> clazz = Class.forName("com.itheima.bean.Mouse"); if(clazz != null) { return new String[]{"com.itheima.bean.Cat"}; } } catch (ClassNotFoundException e) {
return new String[0]; } return null; } }
|
注解方式
比如可以直接写(.class) 也可以写(name=”全路径名”)
| 注解 |
作用 |
| @ConditionalOnClass |
有某个class才加载bean |
| @ConditionalOnMissingClass |
没有哪个class才加载bean |
| @ConditionalOnBean |
有这个bean才加载 |
| @ConditionalOnWebApplication |
是Web环境才加载 |
| @ConditionalOnNotWebApplication |
不是Web环境才加载 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Bean @ConditionalOnClass(name = "cn.jyw.bean.Wolf") @ConditionalOnMissingClass("cn.jyw.bean.Mouse") public Cat tom(){ return new Cat(); }
@Bean @ConditionalOnBean(name="jerry") public Cat tom(){ return new Cat(); }
@Bean @ConditionalOnClass(name="com.mysql.jdbc.Driver") public DruidDataSource dataSource(){ return new DruidDataSource(); }
|
1.3 bean的依赖属性配置管理
bean在运行的时候,实现对应的业务逻辑时有可能需要开发者提供一些设置值,有就是属性了
如果使用构造方法将参数固定,灵活性不足
需要灵活的配置
先定义配置文件
1 2 3 4 5 6 7
| cartoon: cat: name: "图多盖洛" age: 5 mouse: name: "泰菲" age: 1
|
定义一个封装属性的专用类,加载配置属性,读取对应前缀相关的属性值
1 2 3 4 5 6
| @ConfigurationProperties(prefix = "cartoon") @Data public class CartoonProperties { private Cat cat; private Mouse mouse; }
|
在使用的位置注入对应的配置即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @EnableConfigurationProperties(CartoonProperties.class) @Data public class CartoonCatAndMouse{ private Cat cat; private Mouse mouse;
private CartoonProperties cartoonProperties; public CartoonCatAndMouse(CartoonProperties cartoonProperties){ this.cartoonProperties = cartoonProperties; cat = new Cat(); cat.setName(cartoonProperties.getCat()!=null && StringUtils.hasText(cartoonProperties.getCat().getName()) ? cartoonProperties.getCat().getName() : "tom"); cat.setAge(cartoonProperties.getCat()!=null && cartoonProperties.getCat().getAge()!=null ? cartoonProperties.getCat().getAge() : 3); mouse = new Mouse(); mouse.setName(cartoonProperties.getMouse()!=null && StringUtils.hasText(cartoonProperties.getMouse().getName()) ? cartoonProperties.getMouse().getName() : "jerry"); mouse.setAge(cartoonProperties.getMouse()!=null && cartoonProperties.getMouse().getAge()!=null ? cartoonProperties.getMouse().getAge() : 4); } public void play(){ System.out.println(cat.getAge()+"岁的"+cat.getName()+"和"+mouse.getAge()+"岁的"+mouse.getName()+"打起来了"); }
|
建议在业务类上使用@EnableConfigurationProperties声明bean,这样在不使用这个类的时候,也不会无故加载专用的属性配置类CartoonProperties,减少spring管控的资源数量。
执行
1 2 3 4 5 6 7
| public class App { public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(App.class); CartoonCatAndMouse bean = ctx.getBean(CartoonCatAndMouse.class); bean.play(); } }
|
总结
- bean的运行如果需要外部设置值,建议将设置值封装成专用的属性类* * * * Properties
- 设置属性类加载指定前缀的配置信息
- 在需要使用属性类的位置通过注解@EnableConfigurationProperties加载bean,而不要直接在属性配置类上定义bean,减少资源加载的数量,因需加载而不要饱和式加载。
1.4 自动配置原理
整体过程分为2个阶段
阶段一:准备阶段
收集Spring开发者的编程习惯,整理开发过程使用的常用技术列表——>(技术集A)
收集常用技术(技术集A)的使用参数,整理开发过程中每个技术的常用设置列表——>(设置集B)
阶段二:加载阶段
- 初始化SpringBoot基础环境,加载用户自定义的bean和导入的其他坐标,形成
初始化环境
- 将
技术集A包含的所有技术都定义出来,在Spring/SpringBoot启动时默认全部加载
- 将
技术集A中具有使用条件的技术约定出来,设置成按条件加载,由开发者决定是否使用该技术(与初始化环境比对 )
- 将
设置集B作为默认配置加载(约定大于配置),减少开发者配置工作量
1 2 3 4
| public final class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; }
|
总结
- 先开发若干种技术的标准实现
- SpringBoot启动时加载所有的技术实现对应的自动配置类
- 检测每个配置类的加载条件是否满足并进行对应的初始化
- 切记是先加载所有的外部资源,然后根据外部资源进行条件比对
1.5 变更自动配置
自定义自动配置(META-INF/spring.factories) 自动bean化
1 2 3
| # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.jyw.bean.CartoonCatAndMouse
|
控制SpringBoot内置自动配置类加载
- 配置文件控制
1 2 3 4 5
| spring: autoconfigure: exclude: - org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration - org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
|
- 注解控制
1
| @EnableAutoConfiguration(excludeName = "",exclude = {})
|
变更自动配置:去除tomcat自动配置(条件激活),添加jetty自动配置(条件激活)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> </dependencies>
|
总结
- 通过配置文件exclude属性排除自动配置
- 通过注解@EnableAutoConfiguration属性排除自动配置项
- 启用自动配置只需要满足自动配置条件即可
- 可以根据需求开发自定义自动配置项
2. 自定义starter开发
2.1 基本实现
业务功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class IpCountService { private Map<String,Integer> ipCountMap = new HashMap<>(); @Autowired private HttpServletRequest httpServletRequest;
public void count(){ String ip = httpServletRequest.getRemoteAddr(); System.out.println("-----------------------------"+ip); ipCountMap.merge(ip, 1, Integer::sum); } }
|
自动配置类
1 2 3 4 5 6
| public class IpAutoConfiguration { @Bean public IpCountService ipCountService(){ return new IpCountService(); } }
|
配置
在resources/META-INF/spring.factories中
1 2 3
| # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.jyw.autoconfig.IpAutoConfiguration
|
使用
clean和install之后导入pom依赖即可
1 2 3 4 5 6 7 8 9 10 11
| @RestController public class MyController {
@Autowired private IpCountService ipCountService;
@GetMapping("/test/{id}") public User test(@PathVariable Integer id) { ipCountService.count(); } }
|
2.2 开启定时任务功能
在自动配置上加上springBoot的内置定时任务
1 2 3 4 5 6 7
| @EnableScheduling public class IpAutoConfiguration { @Bean public IpCountService ipCountService(){ return new IpCountService(); } }
|
在Service中开始任务
1 2 3 4 5 6 7 8 9 10 11
| @Scheduled(cron = "0/5 * * * * ?") public void print(){ System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+--num--+"); for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.printf("|%18s |%5d |%n",key,value); } System.out.println("+--------------------+-------+"); }
|
2.3 启用配置文件
定义配置文件
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
|
@Component("ipProperties") @ConfigurationProperties(prefix = "tools.ip") public class IpProperties {
private Long cycle = 5L;
private Boolean cycleReset = false;
private String model = LogModel.DETAIL.value;
public enum LogModel{ DETAIL("detail"), SIMPLE("simple"); private String value; LogModel(String value) { this.value = value; } public String getValue() { return value; } }
public Long getCycle() { return cycle; }
public void setCycle(Long cycle) { this.cycle = cycle; }
public Boolean getCycleReset() { return cycleReset; }
public void setCycleReset(Boolean cycleReset) { this.cycleReset = cycleReset; }
public String getModel() { return model; }
public void setModel(String model) { this.model = model; } }
|
自动配置类
1 2 3 4 5 6 7 8 9 10 11
| @EnableScheduling
@Import(IpProperties.class) public class IpAutoConfiguration {
@Bean public IpCountService ipCountService(){ return new IpCountService(); } }
|
服务类
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
| @Autowired private IpProperties ipProperties;
@Scheduled(cron = "0/#{ipProperties.cycle} * * * * ?") public void print(){
if(ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())){ System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+--num--+"); for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(String.format("|%18s |%5d |",key,value)); } System.out.println("+--------------------+-------+"); }else if(ipProperties.getModel().equals(IpProperties.LogModel.SIMPLE.getValue())){ System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+"); for (String key: ipCountMap.keySet()) { System.out.println(String.format("|%18s |",key)); } System.out.println("+--------------------+"); }
if(ipProperties.getCycleReset()){ ipCountMap.clear(); } }
|
配置
定义在要使用的项目中
1 2 3 4 5 6
| tools: ip: cycle: 10 cycleReset: false model: "detail"
|
2.4 配置拦截器
定义拦截器
在启用start时能对每次访问都执行
1 2 3 4 5 6 7 8 9 10 11
| public class IpCountInterceptor implements HandlerInterceptor {
@Autowired private IpCountService ipCountService;
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ipCountService.count(); return true; } }
|
开启拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration public class SpringMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(ipCountInterceptor()).addPathPatterns("/**"); }
@Bean public IpCountInterceptor ipCountInterceptor(){ return new IpCountInterceptor(); }
}
|
2.5 辅助功能开发(yml输入提示)
导入配置处理器坐标
这个坐标的作用是根据注释生成文件,但部分数据调整仍需手动
获得spring-configuration-metadata.json文件后放入yml同级即可
上线需要删掉此坐标
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
|
进行自定义提示功能开发
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
| { "groups": [ { "name": "tools.ip", "type": "cn.itcast.properties.IpProperties", "sourceType": "cn.itcast.properties.IpProperties" } ], "properties": [ { "name": "tools.ip.cycle", "type": "java.lang.Long", "description": "日志显示周期", "sourceType": "cn.itcast.properties.IpProperties", "defaultValue": 5 }, { "name": "tools.ip.cycle-reset", "type": "java.lang.Boolean", "description": "是否周期内重置数据", "sourceType": "cn.itcast.properties.IpProperties", "defaultValue": false }, { "name": "tools.ip.model", "type": "java.lang.String", "description": "日志输出模式 detail:详细模式 simple:极简模式", "sourceType": "cn.itcast.properties.IpProperties" } ], "hints": [ { "name": "tools.ip.model", "values": [ { "value": "detail", "description": "详细模式." }, { "value": "simple", "description": "极简模式." } ] } ] }
|
3. springboot程序启动流程
SpringBoot启动流程
- 初始化各种属性,加载成对象
- 读取环境属性(Environment)
- 系统配置(spring.factories)
- 参数(Arguments、application.properties)
- 创建Spring容器对象ApplicationContext,加载各种配置
- 在容器创建前,通过监听器机制,应对不同阶段加载数据、更新数据的需求
- 容器初始化过程中追加各种功能,例如统计时间、输出日志等
监听器类型
- 在应用运行但未进行任何处理时,将发送 ApplicationStartingEvent。
- 当Environment被使用,且上下文创建之前,将发送 ApplicationEnvironmentPreparedEvent。
- 在开始刷新之前,bean定义被加载之后发送 ApplicationPreparedEvent。
- 在上下文刷新之后且所有的应用和命令行运行器被调用之前发送 ApplicationStartedEvent。
- 在应用程序和命令行运行器被调用之后,将发出 ApplicationReadyEvent,用于通知应用已经准备处理请求。
- 启动时发生异常,将发送 ApplicationFailedEvent。
源码分析
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
| Springboot30StartupApplication【10】->SpringApplication.run(Springboot30StartupApplication.class, args); SpringApplication【1332】->return run(new Class<?>[] { primarySource }, args); SpringApplication【1343】->return new SpringApplication(primarySources).run(args); SpringApplication【1343】->SpringApplication(primarySources) SpringApplication【266】->this(null, primarySources); SpringApplication【280】->public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) SpringApplication【281】->this.resourceLoader = resourceLoader; SpringApplication【283】->this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); SpringApplication【284】->this.webApplicationType = WebApplicationType.deduceFromClasspath(); SpringApplication【285】->this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); SpringApplication【286】->setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); SpringApplication【287】->setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); SpringApplication【288】->this.mainApplicationClass = deduceMainApplicationClass(); SpringApplication【1343】->new SpringApplication(primarySources).run(args) SpringApplication【323】->StopWatch stopWatch = new StopWatch(); SpringApplication【324】->stopWatch.start(); SpringApplication【325】->DefaultBootstrapContext bootstrapContext = createBootstrapContext(); SpringApplication【327】->configureHeadlessProperty(); java.awt.headless=true SpringApplication【328】->SpringApplicationRunListeners listeners = getRunListeners(args); SpringApplication【329】->listeners.starting(bootstrapContext, this.mainApplicationClass); SpringApplication【331】->ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); SpringApplication【333】->ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); SpringApplication【333】->configureIgnoreBeanInfo(environment); SpringApplication【334】->Banner printedBanner = printBanner(environment); SpringApplication【335】->context = createApplicationContext(); SpringApplication【363】->context.setApplicationStartup(this.applicationStartup); SpringApplication【337】->prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); SpringApplication【338】->refreshContext(context); SpringApplication【339】->afterRefresh(context, applicationArguments); SpringApplication【340】->stopWatch.stop(); SpringApplication【341】->if (this.logStartupInfo) { SpringApplication【342】-> new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); SpringApplication【344】->listeners.started(context); SpringApplication【345】->callRunners(context, applicationArguments); SpringApplication【353】->listeners.running(context);
|