Spring启动扩展点流程

pull/4/head
lry 2 years ago
parent 3432fd2138
commit a47b35eac4

@ -5851,6 +5851,374 @@ MethodInterceptor是AOP项目中的拦截器它拦截的目标是方法
# SpringBoot
## 启动流程扩展点
![Spring启动扩展点流程](images/Middleware/Spring启动扩展点流程.png)
### ApplicationContextInitializer
整个spring容器在刷新之前初始化`ConfigurableApplicationContext`的回调接口,简单来说,就是在容器刷新之前调用此类的`initialize`方法。用户可以在整个spring容器还没被初始化之前做一些事情。
```java
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("[ApplicationContextInitializer]");
}
}
```
因为这时候spring容器还没被初始化所以想要自己的扩展的生效有以下三种方式
- 在启动类中用`springApplication.addInitializers(new TestApplicationContextInitializer())`语句加入
- 配置文件配置`context.initializer.classes=com.example.demo.TestApplicationContextInitializer`
- Spring SPI扩展在spring.factories中加入`org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer`
### BeanDefinitionRegistryPostProcessor
在读取项目中的`beanDefinition`之后执行,提供一个补充的扩展点。使用场景是你可以在这里动态注册自己的`beanDefinition`可以加载classpath之外的bean。
```java
public class TestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanDefinitionRegistry");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanFactory");
}
}
```
### BeanFactoryPostProcessor
是`beanFactory`的扩展接口调用时机在spring在读取`beanDefinition`信息之后实例化bean之前。在这个时机用户可以通过实现这个扩展接口来自行处理一些东西比如修改已经注册的`beanDefinition`的元信息。
```java
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("[BeanFactoryPostProcessor]");
}
}
```
### InstantiationAwareBeanPostProcessor
该接口继承了`BeanPostProcess`接口,区别如下:
**`BeanPostProcess`接口只在bean的初始化阶段进行扩展注入spring上下文前后而`InstantiationAwareBeanPostProcessor`接口在此基础上增加了3个方法把可扩展的范围增加了实例化阶段和属性注入阶段。**
该类主要的扩展点有以下5个方法主要在bean生命周期的两大阶段**实例化阶段** 和**初始化阶段** ,下面一起进行说明,按调用顺序为:
- `postProcessBeforeInstantiation`实例化bean之前相当于new这个bean之前
- `postProcessAfterInstantiation`实例化bean之后相当于new这个bean之后
- `postProcessPropertyValues`bean已经实例化完成在属性注入时阶段触发`@Autowired`,`@Resource`等注解原理基于此方法实现
- `postProcessBeforeInitialization`初始化bean之前相当于把bean注入spring上下文之前
- `postProcessAfterInitialization`初始化bean之后相当于把bean注入spring上下文之后
使用场景:这个扩展点非常有用 无论是写中间件和业务中都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集或者对某个类型的bean进行统一的设值等等。
```java
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);
return bean;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);
return true;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName);
return pvs;
}
}
```
### SmartInstantiationAwareBeanPostProcessor
该扩展接口有3个触发点方法
- `predictBeanType`
该触发点发生在`postProcessBeforeInstantiation`之前(在图上并没有标明,因为一般不太需要扩展这个点)这个方法用于预测Bean的类型返回第一个预测成功的Class类型如果不能预测返回null当你调用`BeanFactory.getType(name)`时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。
- `determineCandidateConstructors`
该触发点发生在`postProcessBeforeInstantiation`之后用于确定该bean的构造函数之用返回的是该bean的所有构造函数列表。用户可以扩展这个点来自定义选择相应的构造器来实例化这个bean。
- `getEarlyBeanReference`
该触发点发生在`postProcessAfterInstantiation`之后当有循环依赖的场景当bean实例化好之后为了防止有循环依赖会提前暴露回调方法用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。
```java
public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
@Override
public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName);
return beanClass;
}
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName);
return null;
}
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName);
return bean;
}
}
```
### BeanFactoryAware
这个类只有一个触发点发生在bean的实例化之后注入属性之前也就是Setter之前。这个类的扩展点方法为`setBeanFactory`,可以拿到`BeanFactory`这个属性。
使用场景为你可以在bean实例化之后但还未初始化之前拿到 `BeanFactory`在这个时候可以对每个bean作特殊化的定制。也或者可以把`BeanFactory`拿到进行缓存,日后使用。
```java
public class TestBeanFactoryAware implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());
}
}
```
### ApplicationContextAwareProcessor
该类本身并没有扩展点但是该类内部却有6个扩展点可供实现 这些类触发的时机在bean实例化之后初始化之前。该类用于执行各种驱动接口在bean实例化之后属性填充之后通过执行以上红框标出的扩展接口来获取对应容器的变量。**所以这里应该来说是有6个扩展点**
- `EnvironmentAware`
用于获取`EnviromentAware`的一个扩展类,这个变量非常有用, 可以获得系统内的所有参数。当然个人认为这个Aware没必要去扩展因为spring内部都可以通过注入的方式来直接获得。
- `EmbeddedValueResolverAware`
用于获取`StringValueResolver`的一个扩展类, `StringValueResolver`用于获取基于`String`类型的properties的变量一般我们都用`@Value`的方式去获取如果实现了这个Aware接口把`StringValueResolver`缓存起来,通过这个类去获取`String`类型的变量,效果是一样的。
- `ResourceLoaderAware`
用于获取`ResourceLoader`的一个扩展类,`ResourceLoader`可以用于获取classpath内所有的资源对象可以扩展此类来拿到`ResourceLoader`对象。
- `ApplicationEventPublisherAware`
用于获取`ApplicationEventPublisher`的一个扩展类,`ApplicationEventPublisher`可以用来发布事件,结合`ApplicationListener`来共同使用,下文在介绍`ApplicationListener`时会详细提到。这个对象也可以通过spring注入的方式来获得。
- `MessageSourceAware`
用于获取`MessageSource`的一个扩展类,`MessageSource`主要用来做国际化。
- `ApplicationContextAware`
用来获取`ApplicationContext`的一个扩展类,`ApplicationContext`应该是很多人非常熟悉的一个类了就是spring上下文管理器可以手动的获取任何在spring上下文注册的bean我们经常扩展这个接口来缓存spring上下文包装成静态方法。同时`ApplicationContext`也实现了`BeanFactory``MessageSource``ApplicationEventPublisher`等接口,也可以用来做相关接口的事情。
```java
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
```
### BeanNameAware
这个类也是Aware扩展的一种触发点在bean的初始化之前也就是`postProcessBeforeInitialization`之前,这个类的触发点方法只有一个:`setBeanName`
使用场景为用户可以扩展这个点在初始化bean之前拿到spring容器中注册的的beanName来自行修改这个beanName的值。
```java
public class NormalBeanA implements BeanNameAware{
public NormalBeanA() {
System.out.println("NormalBean constructor");
}
@Override
public void setBeanName(String name) {
System.out.println("[BeanNameAware] " + name);
}
}
```
### @PostConstruct
这个并不算一个扩展点其实就是一个标注。其作用是在bean的初始化阶段如果对一个方法标注了`@PostConstruct`,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是在`postProcessBeforeInitialization`之后,`InitializingBean.afterPropertiesSet`之前。使用场景:用户可以对某一方法进行标注,来进行初始化某一个属性。
```java
public class NormalBeanA {
public NormalBeanA() {
System.out.println("NormalBean constructor");
}
@PostConstruct
public void init(){
System.out.println("[PostConstruct] NormalBeanA");
}
}
```
### InitializingBean
这个类顾名思义也是用来初始化bean的。`InitializingBean`接口为bean提供了初始化方法的方式它只包括`afterPropertiesSet`方法凡是继承该接口的类在初始化bean的时候都会执行该方法。这个扩展点的触发时机在`postProcessAfterInitialization`之前。使用场景:用户实现此接口,来进行系统启动的时候一些业务指标的初始化工作。
```java
public class NormalBeanA implements InitializingBean{
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("[InitializingBean] NormalBeanA");
}
}
```
### FactoryBean
一般情况下Spring通过反射机制利用bean的class属性指定支线类去实例化bean在某些情况下实例化Bean过程比较复杂如果按照传统的方式则需要在bean中提供大量的配置信息。配置方式的灵活性是受限的这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个`org.springframework.bean.factory.FactoryBean`的工厂类接口用户可以通过实现该接口定制实例化Bean的逻辑。`FactoryBean`接口对于Spring框架来说占用重要的地位Spring自身就提供了70多个`FactoryBean`的实现。它们隐藏了实例化一些复杂bean的细节给上层应用带来了便利。从Spring3.0开始,`FactoryBean`开始支持泛型,即接口声明改为`FactoryBean<T>`的形式。
使用场景用户可以扩展这个类来为要实例化的bean作一个代理比如为该对象的所有的方法作一个拦截在调用前后输出一行log模仿`ProxyFactoryBean`的功能。
```java
public class TestFactoryBean implements FactoryBean<TestFactoryBean.TestFactoryInnerBean> {
@Override
public TestFactoryBean.TestFactoryInnerBean getObject() throws Exception {
System.out.println("[FactoryBean] getObject");
return new TestFactoryBean.TestFactoryInnerBean();
}
@Override
public Class<?> getObjectType() {
return TestFactoryBean.TestFactoryInnerBean.class;
}
@Override
public boolean isSingleton() {
return true;
}
public static class TestFactoryInnerBean{
}
}
```
### SmartInitializingSingleton
这个接口中只有一个方法`afterSingletonsInstantiated`,其作用是是 在spring容器管理的所有单例对象非懒加载对象初始化完成之后调用的回调接口。其触发时机为`postProcessAfterInitialization`之后。
使用场景:用户可以扩展此接口在对所有单例对象初始化完毕后,做一些后置的业务处理。
```java
public class TestSmartInitializingSingleton implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.out.println("[TestSmartInitializingSingleton]");
}
}
```
### CommandLineRunner
这个接口也只有一个方法:`run(String... args)`,触发时机为整个项目启动完毕后,自动执行。如果有多个`CommandLineRunner`,可以利用`@Order`来进行排序。使用场景:用户扩展此接口,进行启动项目之后一些业务的预处理。
```java
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("[TestCommandLineRunner]");
}
}
```
### DisposableBean
这个扩展点也只有一个方法:`destroy()`,其触发时机为当此对象销毁时,会自动执行这个方法。比如说运行`applicationContext.registerShutdownHook`时,就会触发这个方法。
```java
public class NormalBeanA implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("[DisposableBean] NormalBeanA");
}
}
```
### ApplicationListener
准确的说这个应该不算spring&springboot当中的一个扩展点`ApplicationListener`可以监听某个事件的`event`触发时机可以穿插在业务方法执行过程中用户可以自定义某个业务事件。但是spring内部也有一些内置事件这种事件可以穿插在启动调用中。我们也可以利用这个特性来自己做一些内置事件的监听器来达到和前面一些触发点大致相同的事情。
接下来罗列下spring主要的内置事件
- ContextRefreshedEvent
ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在`ConfigurableApplicationContext`接口中使用 `refresh()`方法来发生。此处的初始化是指所有的Bean被成功装载后处理Bean被检测并激活所有Singleton Bean 被预实例化,`ApplicationContext`容器已就绪可用。
- ContextStartedEvent
当使用 `ConfigurableApplicationContext` ApplicationContext子接口接口中的 start() 方法启动 `ApplicationContext`时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
- ContextStoppedEvent
当使用 `ConfigurableApplicationContext`接口中的 `stop()`停止`ApplicationContext` 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作
- ContextClosedEvent
当使用 `ConfigurableApplicationContext`接口中的 `close()`方法关闭 `ApplicationContext` 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启
- RequestHandledEvent
这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时当Spring处理用户请求结束后系统会自动触发该事件
## MVC
### HTTP请求处理流程

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

@ -0,0 +1 @@
<mxfile host="Electron" modified="2022-10-02T02:49:25.343Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36" etag="WujSmMq5gzLMllHs7H_u" version="16.5.1" type="device"><diagram id="k88nMVZb51-p23jTSDSH" name="第 1 页">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</diagram></mxfile>
Loading…
Cancel
Save