1. ConfigurableApplicationContext的结构
---
title: Spring Context
---
classDiagram
direction BT
class ApplicationContext {
<<Interface>>
}
class ApplicationEventPublisher {
<<Interface>>
}
class AutoCloseable {
<<Interface>>
}
class BeanFactory {
<<Interface>>
}
class Closeable {
<<Interface>>
}
class ConfigurableApplicationContext {
<<Interface>>
}
class EnvironmentCapable {
<<Interface>>
}
class FunctionalInterface
class HierarchicalBeanFactory {
<<Interface>>
}
class Lifecycle {
<<Interface>>
}
class ListableBeanFactory {
<<Interface>>
}
class MessageSource {
<<Interface>>
}
class ResourceLoader {
<<Interface>>
}
class ResourcePatternResolver {
<<Interface>>
}
ApplicationContext --> ApplicationEventPublisher
ApplicationContext --> EnvironmentCapable
ApplicationContext --> HierarchicalBeanFactory
ApplicationContext --> ListableBeanFactory
ApplicationContext --> MessageSource
ApplicationContext --> ResourcePatternResolver
FunctionalInterface .. ApplicationEventPublisher
Closeable --> AutoCloseable
ConfigurableApplicationContext --> ApplicationContext
ConfigurableApplicationContext --> Closeable
ConfigurableApplicationContext --> Lifecycle
HierarchicalBeanFactory --> BeanFactory
ListableBeanFactory --> BeanFactory
ResourcePatternResolver --> ResourceLoader
BeanFactory本身功能并不充分,通过一系列的后置处理器来完善,ApplicationContext就实现一系统的后置处理器来完成功能增强。 具体增强哪些功能,在每个Bean定义时,去指定具体的接口,来完成具体的功能。
2. Spring 上下文初始化过程
- https://www.cnblogs.com/Createsequence/p/16585528.html
- https://www.cnblogs.com/Createsequence/p/16585530.html
- https://www.cnblogs.com/Createsequence/p/16585531.html
2.1. org.springframework.context.support.AbstractApplicationContext#refresh
@Override
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{
// ================= 二、BeanFactory的初始化 =================
// 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);
// ================= 三、事件,Bean及其他配置的初始化 =================
// 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();
}
}
}
3. Spring常见的一些后置处理器
3.1. @PostConstruct和@PreDestroy注解
这两个注解分别用于在Spring容器初始化Bean之后和销毁Bean之前执行特定的方法。
3.2. LifecycleProcessor 接口
这是一个接口,用于处理Bean的生命周期回调。Spring容器会使用这个接口来调用Bean的init()和destroy()方法。
3.3. InitializingBean和DisposableBean接口
两个接口分别定义了afterPropertiesSet()和destroy()方法, 当Bean被Spring容器初始化后,会自动调用afterPropertiesSet() 方法,而在Bean被销毁前,会自动调用destroy()方法。
3.4. CustomScopeProcessor
这个处理器用于处理自定义的作用域。例如,你可能想要在不同的作用域中管理不同的Bean实例。
3.5. BeanFactoryPostProcessor
这是一个特殊的后置处理器,它允许你在Spring容器初始化之前修改或创建Bean的定义。
3.6. BeanPostProcessor
这是一个更通用的后置处理器,允许你在Spring容器初始化Bean之后、但在应用上下文启动之前对Bean进行操作。
3.7. PropertySourcesPostProcessor
这个后置处理器用于处理@PropertySource注解,允许你在Spring Boot应用中加载属性文件。
3.8. CommandLineRunner和ApplicationRunner
这两个接口分别定义了run()方法,当Spring应用上下文启动后,会自动调用这些方法。这使得你可以在应用启动后执行特定的逻辑。
4. Spring 上下文类型
- ClassPathXmlApplicationContext:从类路径下的XML配置文件中加载上下文定义,将应用上下文的定义文件作为类资源。
- FileSystemXmlApplicationContext:从文件系统下的XML配置文件中加载上下文定义。
- XmlWebApplicationContext:从Web应用下的XML配置文件中加载上下文定义。
- AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文。
- AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下文。 这些上下文类型都继承自Spring的ApplicationContext接口,并提供了各自的特点和用途。选择适合的上下文类型取决于应用程序的需求和配置方式。
5. Aware接口
- BeanFactoryAware:允许将BeanFactory注入到实现该接口的类中。
- ApplicationContextAware:允许将ApplicationContext注入到实现该接口的类中。
- WebApplicationContextAware:允许将WebApplicationContext注入到实现该接口的类中。
6. Spring事件
6.1. 模型
- 事件 org.springframework.context.ApplicationEvent
- 事件的监听者 org.springframework.context.ApplicationListener
- 事件的发布者 org.springframework.context.ApplicationEventPublisher
- 事件的广播者 org.springframework.context.event.SimpleApplicationEventMulticaster
如下图所示:发布一个事件后,会调用multicastEvent方法,在multicastEvent方法可能线程池异步的调用invokeListener方法。 在invokeListener方法中调用doInvokeListener,真正执行具体的ApplicationListener类中onApplicationEvent方法。
---
title: Spring Event
---
classDiagram
class ApplicationListener {
+ void onApplicationEvent(E event)
}
namespace Publisher {
class ApplicationEventPublisher {
<<Interface>>
+ void publishEvent(ApplicationEvent event)
}
class AbstractApplicationContext {
+ void publishEvent(ApplicationEvent event)
}
}
namespace Multicaster {
class SimpleApplicationEventMulticaster {
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType)
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event)
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event)
}
class ApplicationEventMulticaster {
<<Interface>>
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType)
}
}
AbstractApplicationContext --|> ApplicationEventPublisher
SimpleApplicationEventMulticaster --|> ApplicationEventMulticaster
SimpleApplicationEventMulticaster ..> ApplicationListener
ApplicationEventPublisher ..> SimpleApplicationEventMulticaster
调用时序图如下
sequenceDiagram
ApplicationEventPublisher ->> ApplicationEventPublisher: publishEvent(Object event)
ApplicationEventPublisher ->> ApplicationEventPublisher: getApplicationEventMulticaster()
ApplicationEventPublisher ->> SimpleApplicationEventMulticaster: multicastEvent()
SimpleApplicationEventMulticaster ->> SimpleApplicationEventMulticaster: invokeListener()
SimpleApplicationEventMulticaster ->> SimpleApplicationEventMulticaster: doInvokeListener()
SimpleApplicationEventMulticaster ->> ApplicationListener: onApplicationEvent()
6.2. 异步发布事件
Spring 发布事件是使用这个类来发布事件,可以指定线程池来异步发布事件 org.springframework.context.event.SimpleApplicationEventMulticaster
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//此处默认是空的
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
@Configuration
public class Config {
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticater() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(Executors.newFixedThreadPool(1)); // 自定义线程池
return multicaster;
}
}
7. spring中的资源
7.1. 使用线程上下文
使用maven在打包时,resource文件夹的文件,默认是打包在模块的根目录下。 可以使用线程的上下文获取
InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
7.2. 使用Spring的ApplicationContext
但文件是在包路径下面的某个文件夹中,就需要添加相应的文件路程
// 从类路径下
Resource[]list=context.getResource("classpath:");
Resource[]list=context.getResource("classpath:*");
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/classicmodel/*.xml")
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("test.lua")));
Resource[]list=context.getResource("classpath:*/META-INF");
// 从文件路径下
Resource[]listcontext.getResource("file:");
8. aop失效
原因
- AOP代理对象失效:在类内部方法中调用同一个类中定义的方法,则调用的是当前的对象, 而不是代理对象,则代理失效,注解失效。
- 要增强的类或方法没被spring管理:例如,把Aop的实现类注入到了spring容器中, 而要切的类方法是在Controller层,即springmvc容器中的东西。在这种情况下, aop在初始化时会在自己的容器中寻找能够匹配的类方法, 因为这些类和方法是在springmvc容器中管理的,因此就没有代理成功。
8.1. 解决方法
- 可以采取的解决办法包括将需要进行AOP管理的方法放在独立的类中定义;
- 将Aop的配置切点移到springmvc容器的xml中;
- 使用AopContext.currentProxy() 获得当前切面的代理对象,通过这个代理对象来调用方法等。 但需要加上配置:@EnableAspectJAutoProxy(exposeProxy = true)