持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天, ,点击查看活动详情
spring-bean 常见扩展点
spring容器启动过程
Spring-boot中的入口在ServletWebServerApplicationContext 类的refresh() 方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
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();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
- Bean实例化[createBeanInstance()]
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(): 实例化前调用,可以在这里修改BeanDefinition。
如果这里返回不为空,会直接略过后续的bean创建过程,将返回结果当作此次创建的bean。
[代码位置:AbstractAutowireCapableBeanFactory --> createBean/ resolveBeforeInstantiation]
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
2 Bean的属性注入[populateBean()]
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(): 在属性设置前来改变bean的最后机会。返回值为是否继续填充bean。 InstantiationAwareBeanPostProcessor.postProcessPropertyValues(): 在属性获取完、属性填充前对属性的再次处理,
典型应用是 RequiredAnnotationBeanPostProcessor 中对属性的验证。 [调用位置:populateBean()]
3 Bean初始化[initializeBean()]
BeanNameAware.setBeanName():获取 Bean 的名称 BeanClassLoaderAware.setBeanClassLoader():获取 Bean 的classLoader BeanFactoryAware.setBeanFactory(): 获取beanFactory BeanPostProcessor
AbstractAutowireCapableBeanFactory
/**
- 实例化bean
*/
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
// 实例化之前的回调, 调用 postProcessBeforeInitialization
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// 先调用 afterPropertiesSet()方法, 再调用指定的init方法
invokeInitMethods(beanName, wrappedBean, mbd);
//实例化之后的回调, 调用postProcessAfterInitialization
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
4 Bean的销毁
DisposableBean.destory() 自定义的destory-method
web容器stop的时候,beandestory的顺序问题?????ß
5 其他
PropertyEditorRegistrar:可注册自定义的属性编辑器,让spring可以按自定义方式解析某些类型的属性。spring在initBeanWrapper()的时候会调用PropertyEditorRegistrar.registerCustomEditors(),之后在属性填充的环节就可以用自定义编辑器进行解析了。
spring-context常见扩展点
ApplicationContextAware: 可获取applicationContext ApplicationListener/ApplicationEvent: 事件监听(很重要)
@EventListener
ApplicationEventMulticaster: 事件派发,管理多个ApplicationListener并向它们发布事件。通常,ApplicationContext 会使用 ApplicationEventMulticaster 作为实际发布事件的委托。 Import ImportSelector:
在初始化bean工厂时期[invokeBeanFactoryPostProcessors()] 会调用。
ImportSelector的导入实现是通过BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()来实现的。
Import 注解原理图
ImportBeanDefinitionRegistrar:
需要与 @Import 和 @Configuration 共同配合使用。@EnableFeignClients, @EnableDubboConfig 等都是通过ImportBeanDefinitionRegistrar 来动态注入的服务调用类到spring容器里面。
- Mybatis应用
mybatis-spring-boot-autoconfigure-1.2.1.jar!/META-INF/spring.factories
Auto Configure 机制
Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
Import注解
@org.springframework.context.annotation.Configuration
@Import({ AutoConfiguredMapperScannerRegistrar.class })
@ConditionalOnMissingBean(MapperFactoryBean.class)
public static class MapperScannerRegistrarNotFoundConfiguration
ImportBeanDefinitionRegistrar实现类
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(packages));
}
}
- Feign应用
Feign Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration
EnableFeignClients注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients
FeignClientsRegistrar
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,ResourceLoaderAware, EnvironmentAware {
\
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
}
SmartLifecycle/Lifecycle:
Lifecycle是Spring中最基础的生命周期接口,该接口定义了容器启动和停止的方法,Lifecycle是容器生命周期级别的。
SmartLifecycle是对Lifecycle的一个扩展接口。当需要根据Spring容器的生命周期,来做一些逻辑时,一般会自定义一个类实现SmartLifecycle接口;
很少有人会使用Lifecycle。多个SmartLifecycle时,可以使用接口中的getPhase()方法控制回调的顺序。方法返回值越小,越靠前执行start()方法,越靠后执行stop()方法。
SmartLifecycle这个接口的isAutoStartup()方法,一定要返回true,容器启动时才会回调SmartLifecycle的start()方法。
应用
- @KafkaListener 的实现原理
KafkaListenerAnnotationBeanPostProcessor去解析@KafkaListener
一个KafkaListener注解对应一个ConcurrentMessageListenerContainer对象
AbstractApplicationContext#finishRefresh()
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
\
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
\
// Propagate refresh to lifecycle processor first. 只有一个LifecycleProcessor就是 DefaultLifecycleProcessor
getLifecycleProcessor().onRefresh();
\
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
\
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
KafkaListenerEndpointRegistry#start()方法 调用 AbstractMessageListenerContainer#start()方法 调用 ConcurrentMessageListenerContainer#doStart()方法
protected void doStart() {
if (!isRunning()) {
checkTopics();
setRunning(true);
\
for (int i = 0; i < this.concurrency; i++) {
// 一个线程对应一个 KafkaMessageListenerContainer
KafkaMessageListenerContainer<K, V> container = new KafkaMessageListenerContainer<>(this);
container.setBeanName((beanName != null ? beanName : "consumer") + "-" + i);
container.setApplicationEventPublisher(getApplicationEventPublisher());
container.setClientIdSuffix("-" + i);
container.setGenericErrorHandler(getGenericErrorHandler());
container.setAfterRollbackProcessor(getAfterRollbackProcessor());
container.setRecordInterceptor(getRecordInterceptor());
\
if (isPaused()) {
container.pause();
}
container.start();
this.containers.add(container);
}
}
}
- @RabbitListener的实现原理
RabbitListenerAnnotationBeanPostProcessor 去解析@RabbitListener
MessageListenerContainer继承了SmartLifecycle
SimpleMessageListenerContainer本身并没有重写start方法, SimpleMessageListenerContainer父类AbstractMessageListenerContainer类中的start()方法会调用SimpleMessageListenerContainer类的doStart()方法
ClassPathBeanDefinitionScanner:
可以扫描路径下的类(里面的一些方法可以脱离spring环境独立使用).
应用
在mybatis中用到了此类去扫描Mapper接口类并将Mapper接口生成MapperFactoryBean 对象
TypeFilter: 自定义过滤规则接口。可以配置@ComponentScan注解使用。
spring-core里常见的AnnotationTypeFilter(类是否有注解修饰)
RegexPatternTypeFilter(类名是否满足正则表达式)
AssignableTypeFilter(类型是否能赋值指定类型) 等都实现了TypeFilter接口。
ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider
private final List includeFilters = new LinkedList<>();
\
private final List excludeFilters = new LinkedList<>();
springMVC常用扩展点
HandlerMapping: 处理请求的映射.依据指定策略来决定一个web请求到HandlerExecutionChain对象的生成。 HandlerInterceptor:
拦截器.可以在一个请求被真正处理之前、请求被处理但还没输出到响应中、请求已经被输出到响应中之后这三个时间点去做任何我们想要做的事情,对应方法: preHandle()/postHandle()/afterCompletion()。实际使用时,除了直接实现HandlerInterceptor,我们也经常直接继承HandlerInterceptorAdapter。 HandlerAdapter:调用具体的方法对用户发来的请求来进行处理
HandlerMethodArgumentResolver:
通过策略模式实现方法参数的解析
处理方法参数解释绑定器.调用controller方法之前对方法参数进行解释绑定
Converter: 类型转换器.实现自己的类型的转换 ViewResolver: 将逻辑上的视图名称解析为真正的视图 HandlerExceptionResolver: 异常处理 RequestBodyAdvice: 对RequestBody的内容进行统一处理 ResponseBodyAdvice:
对请求响应的最后处理工作, 如统一修改接口的响应对象
可拦截controller方法返回结果并作进一步处理 @ControllerAdvice / @RestControllerAdvice:
Controller的Advice通知, 可搭配@InitBinder / @ModelAttribute / @ExceptionHandler / RequestBodyAdvice / ResponseBodyAdvice 等使用 ExceptionHandler
经常和@ControllerAdvice一起使用, 实现应用统一异常处理操作
\
@ControllerAdvice
@ResponseBody
@Order(-2147483648)
public class ApExceptionHandler implements ResponseBodyAdvice {
/**
** 统一异常处理
*/
@ResponseStatus(HttpStatus.PARTIAL_CONTENT)
@ExceptionHandler({ApBusinessException.class})
public BaseResponse apBusinessExceptionHandler(ApBusinessException ex, HttpServletRequest request, HttpServletResponse response) {
if (request instanceof ContentCachingRequestWrapper) {
log.info("error url ={}", request.getRequestURI());
}
\
\
return BaseResponseUtil.getInstance().error(ex.getCode(), ex.getMsg());
}
\
/**
** 是否支持对此返回类型的响应体进行处理
**/
public boolean supports(MethodParameter returnType, Class aClass) {
return null == returnType.getMethodAnnotation(Raw.class);
}
\
/**
- 统一修改响应对象
**/
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (!MediaType.APPLICATION_JSON.equals(selectedContentType) && !MediaType.APPLICATION_JSON_UTF8.equals(selectedContentType)) {
return MediaType.TEXT_PLAIN.equals(selectedContentType) ? JSONUtil.toJsonStr(BaseResponseUtil.getInstance().success(body)) : body;
} else {
Method method = returnType.getMethod();
if (null != method && "openapiJson".equals(method.getName())) {
return body;
} else if (null != method && ResponseEntity.class.equals(method.getReturnType())) {
return body;
} else if (null == body) {
return BaseResponseUtil.getInstance().success();
} else if (body instanceof BaseResponse) {
((BaseResponse)body).setTracestack(MDC.get("IM-TraceId"));
return body;
} else {
return BaseResponseUtil.getInstance().success(body);
}
}
}
servlet常用扩展点
ServletContainerInitializer 容器启动时触发
在servlet3.0之前都是采用ServletContextListener这种机制来初始化spring上下文(关键代码在org.apache.catalina.core.StandardContext#listenerStart里边)
在servlet3.0后就使用了ServletContainerInitializer这种机制,来初始化spring上文(关键代码org.apache.catalina.core.StandardContext#startInternal里边)
一、ServletContainerInitializer
1、Servlet容器启动会扫描,当前应用里面每一个jar包的ServletContainerInitializer的实现
2、实现了ServletContainerInitializer接口的实现类,必须在jar包的META-INF/services/javax.servlet.ServletContainerInitializer文件里面进行声明,声明的内容就是实现ServletContainerInitializer接口的全类名
3、使用SPI机制,然后进行反射实例化。
二、@HandlesTypes标签的作用
1、@HandlesTypes标签用在实现ServletContainerInitializer接口的类上面,比如:
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer
2、通过HandlesTypes可以将我们感兴趣的一些类注入到ServletContainerInitializer的onStartup方法的第一个入参里面:
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
3、当容器启动的时候,我们就可以通过拿到Set<Class<?>> c里面我们感兴趣的类,然后做一些初始化的工作
三、SpringBootServletInitializer分析
1、SpringBootServletInitializer实现了WebApplicationInitializer接口
2、WebApplicationInitializer接口通过HandlesTypes注册到SpringServletContainerInitializer
3、SpringServletContainerInitializer实现了ServletContainerInitializer接口
4、spring-web-xxx.RELEASE.jar里面的META-INF\services\javax.servlet.ServletContainerInitializer文件内容注册了SpringServletContainerInitializer的全路径:
org.springframework.web.SpringServletContainerInitializer 5、当Tomcat启动时候,会找到SpringServletContainerInitializer,然后再找到SpringBootServletInitializer,从而把spring容器给启动起来
SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
\
List initializers = new LinkedList<>();
\
for (Class<?> waiClass : webAppInitializerClasses) {
initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
}
\
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
\
}
WebApplicationInitializer 的实现类
Logback通过该类进行Web环境初始化
public class LogbackServletContainerInitializer implements ServletContainerInitializer
Listener:
在发生特定事件时触发,比如容器关闭等
ServletContextListener:
监听 ServletContext 对象的生命周期,或者说监听 Web 应用的生命周期。当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。spring-webmvc中的WebApplicationContext就是在ContextLoaderListener初始化和清理的,ContextLoaderListener实现了ServletContextListener。
Filter: 客户端请求时触发
OncePerRequestFilter:
spring-web中的这个类实现了Filter接口,它能够确保在一次请求中只通过一次filter,而需要重复的执行。我们可以继承这个类来实现自己的filter,这样我们自定义的filter也能确保一次请求中只通过一次filter。
通过在请求中增加标记的方式来实现过滤器只被执行一次
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
其他:
在SpringBootApplication上使用@ServletComponentScan注解后,Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码。