Spring 启动过程
spring boot启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)承担着主要的角色,从以上入手进行分析。
@SpringBootApplication
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
虽然定义使用了多个Annotation进行了原信息标注,但实际上重要的只有三个Annotation:
- @Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)
- @EnableAutoConfiguration
- @ComponentScan
即 @SpringBootApplication = (默认属性)@Configuration + @EnableAutoConfiguration + @ComponentScan。
所以,如果我们使用如下的SpringBoot启动类,整个SpringBoot应用依然可以与之前的启动类功能对等:
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Configuration
JavaConfig形式的Spring Ioc容器的配置类。
-
@Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。
-
@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
@ComponentScan
自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
@EnableAutoConfiguration
借助@Import的支持,收集和注册特定场景相关的bean定义。
-
@EnableAutoConfiguration借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器!
-
@EnableAutoConfiguration会根据类路径中的jar依赖为项目进行自动配置,如:添加了spring-boot-starter-web依赖,会自动添加Tomcat和Spring MVC的依赖,Spring Boot会对Tomcat和Spring MVC进行自动配置。
-
借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置
SpringFactoriesLoader详解
SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置。
配合@EnableAutoConfiguration使用,行使配置查找的功能支持。
- 从classpath中搜寻所有的META-INF/spring.factories配置文件
- 并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类
- 汇总为一个并加载到IoC容器
SpringApplication.run()
创建SpringApplication对象实例
SpringApplication实例初始化的时候,它会提前做几件事情:
- 根据classpath里面是否存在某个特征类(ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
执行run方法
方法执行伊始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
创建并配置Environment
包括配置要使用的PropertySource以及Profile。
SpringApplicationRunListener.environmentPrepared()
遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,
告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
打印banner
如果SpringApplication的showBanner属性被设置为true,则打印banner。
ApplicationContext使用配置好的环境
根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。
initialize
ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContextInitializer,然后遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。
SpringApplicationRunListener.contextPrepared()
遍历调用所有SpringApplicationRunListener的contextPrepared()方法。
加载进ApplicationContext
核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
遍历所有SpringApplicationRunListener.contextLoaded()
调用ApplicationContext.refresh()方法
CommandLineRunner
查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
SpringApplicationRunListener.finished()
正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)
Spring启动初始化
启动加载
启动成功后可以通过以下方法运行自己的初始代码
@PostConstruct注解
修饰初始化后,依赖关系注入完成之后需要执行的方法, 也可以理解为在spring容器启动的时候执行,可作为一些数据的常规化加载,比如数据字典之类的
- 执行顺序为:构造方法 > @Autowired > @PostConstruct
@Component
public class StartUpInit {
@Autowired
private SomeService service;
@PostConstruct
public void init(){
// ...
}
}
@Configuration和@Bean来初始化资源
初始化资源,配置文件通过@Value进行注入。
- @Configuration:用于定义配置类,可替换xml配置文件,被注解的类内部一般是包含了一个或者多个@Bean注解的方法。
- @Bean:产生一个Bean对象,然后将Bean对象交给Spring管理,被注解的方法是会被AnnotationConfigApplicationContext或者AnnotationConfgWebApplicationContext扫描,用于构建bean定义,从而初始化Spring容器。产生这个对象的方法Spring只会调用一次,之后Spring就会将这个Bean对象放入自己的Ioc容器中。
补充@Configuration加载Spring:
@Configuration配置spring并启动spring容器
@Configuration启动容器+@Bean注册Bean
@Configuration启动容器+@Component注册Bean
使用 AnnotationConfigApplicationContext 注册 AppContext 类的两种方法
配置Web应用程序(web.xml中配置AnnotationConfigApplicationContext)
CommandLineRunner
创建自定义类实现 CommandLineRunner接口,重写run()方法。springboot启动之后会默认去扫描所有实现了CommandLineRunner的类,该接口中的Component会在所有Spring的Beans都初始化之后,在SpringApplication的run()完成之前执行。
@Component
@Order(2) //通过order值的大小来决定启动的顺序
public class AskForLeave implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
askForLeave();
}
public void askForLeave(){
System.out.println("项目启动了,执行了方法");
}
}
ApplicationRunner接口
@Component
@Order(3)
public class Hello implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
hello();
}
public void hello(){
System.out.println("项目又启动了,这次使用的是:继承 ApplicationRunner");
}
}
链接: Spring Boot 入门 - 进阶篇(6)- 启动加载(CommandLineRunner) .
https://blog.csdn.net/zlc3323/article/details/100137222