自2014年4月Pivotal团队发布全新开源的轻量级框架SpringBoot的第一个版本,迅速红遍大江南北,各个公司都用SpringBoot作为开发项目的必选项。SpringBoot从最初的1.2.x版本发展到最新的3.1.x,JDK从最初的6到最新的JDK17、JDK20,Spring Framework从4.1.x到最新的6.0.x,体现了SpringBoot强大的生命力。之前写过SpringBoot相关的文章,例如《第一个Spring Boot程序》,大家用SpringBoot已经驾轻就熟,今天从另外一个角度剖析SpringBoot框架,希望对大家有所帮助。
SpringBoot作为一款开源轻量级框架为什么能极大的提升我们开发的生产力,我觉得和下面的一些特性是分不开的:
今天通过讲解SpringBoot(下面的示例代码基于spring-boot-2.3.1.RELEASE)提供的扩展点分析SpringBoot通过main方法启动后做了哪些事情,我们如果利用这些扩展点实现自己的业务应用,通过这些讲解希望大家了解自动配置,了解引入的Starter包是如果启动加载的。
SpringBoot项目都是通过main开始启动加载的,下面是一个例子:
public class MyFirstApplication {
public static void main(String[] args) {
SpringApplication.run(MyFirstApplication .class, args);
}
}
切入到SpringApplication类里面会看到这个类有构造方法,可以通过new一个对象来启动,因此上面的代码可以改写为:
public class MyFirstApplication {
public static void main(String[] args) {
new SpringApplication().run(MyFirstApplication .class, args);
}
}
直接上SpringApplication的源码:
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();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
这段代码设置了两种类型的扩展点,setInitializers设置的是初始化器,setListeners设置的是事件监听器。先讲初始化器,事件监听器放到第二节讲。
在SpringApplication的下面方法设置断点:
private Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
启动项目后,查看instances的值,会发现有7个实例类,这些类都实现了 ApplicationContextInitializer 接口。
如果要像这些类一样,在容器启动的时候就加载,是不是实现ApplicationContextInitializer即可,写个代码试试:
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("My first ApplicationContextInitializer");
}
}
重启项目,定位到之前的断点处,查看instances的值,会发现还是7个实例类,没有自己的MyApplicationContextInitializer ,哪里出问题了?仔细研究代码发现这个类不是一个Bean,肯定不能自动加载,那SpringBoot自身默认的那些类是怎么加载的,这里就需要看文章前面介绍的SpringBoot特性“自动配置”,通过SPI机制可以让SpringBoot查找到该实现类,需要在reources/META-INF文件夹下新增一个spring.fatories文件,在里面加入:
org.springframework.context.ApplicationContextInitializer=com.example.init.MyApplicationContextInitializer
重启项目,定位到之前的断点处,查看instances的值,会发现还是8个实例类:
在MyApplicationContextInitializer 类里可以实现业务逻辑,这些事情可以在Spring容器建立前做,例如可以排除哪些Starter组件加载。
在第一节中介绍了初始化器,这一节介绍事件监听器,事件监听器是通过setListeners方法设置的。同理,实现事件监听器首先实现ApplicationListener接口:
public class MyStartingApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationStartingEvent applicationStartingEvent) {
System.out.println("My event starting");
}
}
在spring.fatories文件里面加入:
org.springframework.context.ApplicationListener=com.example.init.MyStartingApplicationListener
在org.springframework.boot.context.event包下有8个事件可以用:
Runner的扩展点有两个接口:CommandLineRunner和ApplicationRunner,这个是在Spring容器建立后才启动,因此不需要通过SPI机制,直接作为Bean启动。
可以先看看源码:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List
业务代码:
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("My first ApplicationRunner");
}
}
如果有多个Runner,需要控制启动顺序,可以在类上加@Order(1)设置优先级,数字越小优先级越高。
在SpringApplication的此方法:public ConfigurableApplicationContext run(String... args) 中有一步刷新容器:refreshContext(context),最终调用了父类的方法AbstractApplicationContext#refresh,展示下这个方法的核心代码:
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
invokeBeanFactoryPostProcessors(beanFactory)就是一个扩展点,该扩展点对应的接口是BeanFactoryPostProcessor,执行对BeanFactory的后置处理。
自定义一个BeanFactoryPostProcessor,在这里面可以获取BeanDefinition。
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("StudentController Bean:" + beanFactory.getBeanDefinition("studentController"));
System.out.println("My first BeanFactoryPostProcessor");
}
}
在AbstractApplicationContext#refresh中的registerBeanPostProcessors(beanFactory)就是向上面建立成功的BeanFactory注册beanPostProcessor,用于后续Bean的处理。
下面建立一个自己的BeanPostProcessor:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("studentController")){
System.out.println("开始加载Bean studentController");
}
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("studentController")){
System.out.println("加载Bean studentController完成");
}
return null;
}
}