在 Spring IOC(3) 我们介绍了Spring IOC中的prepareRefreshobtainFreshBeanFactoryprepareBeanFactory

本章介绍AbstractApplicationContext#refresh的第7, 8, 10个方法,国际化和事件发布。第9个方法onRefresh刷新蓉器这个方法是一个空方法,由子类实现,这里直接跳过了。

国际化

单纯的Spring中设置国际化实际上是体现不出来的,需要用到Spring MVC 才能有所体现。

初始化国际化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
protected void initMessageSource() {
// 获取beanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 如果工厂中已经有这个bean,那就获取出来设置到messageSource上
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
// 判断父类是否存在,如果存在则将判断父类是否设置了消息源,没有设置就设置给他
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// Use empty MessageSource to be able to accept getMessage calls.
// 如果容器中没有注册bean,那么new一个
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
// 注册到容器中
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}

首先是从容器中获取MessageSource接口的实现,如果存在则直接赋值给AbstractApplicationContextmessageSource属性,用于解析国际化和参数化。如果没有就直接new一个委派的实现类,然后赋值给messageSource属性,并注册到容器中。

在Spring中提供了两个默认的实现:

  • ResourceBundleMessageSource :基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化资源
  • ReloadableResourceBundleMessageSource:这个功能和第一个类的功能类似,多了定时刷新功能,允许在不重启系统的情况下,更新资源的信息

国际化的使用

通常我们在ApplicationContext类型的容器中使用国际化3个步骤

  1. 创建国际化文件

    image-20221125165613192

  2. 向容器中注册一个MessageSource类型的bean,bean名称必须为:messageSource

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Configuration
    public class MessageConfig {
    @Bean
    public ResourceBundleMessageSource messageSource() {
    ResourceBundleMessageSource result = new ResourceBundleMessageSource();
    //可以指定国际化化配置文件的位置,格式:路径/文件名称,注意不包含【语言_国家.properties】含这部分
    result.setBasenames("i18n/message"); //
    return result;
    }
    }
  3. 调用AbstractApplicationContext中的getMessage来获取国际化信息,其内部将交给第二步中注册的messageSource名称的bean进行处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @ComponentScan("com.lq.spring")
    public class Context {

    public static void main(String[] args) {
    ApplicationContext ac = new AnnotationConfigApplicationContext(Context.class);
    //未指定Locale,此时系统会取默认的locale对象,本地默认的值中文【中国】,即:zh_CN
    System.out.println(ac.getMessage("username", null, Locale.CHINA));
    }
    }

事件发布

初始化多播器、刷新容器、注册监听器

refresh的第8个方法initApplicationEventMulticaster初始化多播器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 从容器中获取bean,applicationEventMulticaster
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 没有事件多播器就new一个,多播器会创建一个监听器的集合,用于存放监听器
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}

registerListeners注册监听器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protected void registerListeners() {
// Register statically specified listeners first.
// 循环已经存在的监听器,并将监听器加入到多播器中
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}

// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
// 在容器中获取所有实现了ApplicationListener接口的bd,并将beaName加入到多播器中
// 这些都还没有实例化 实际是添加到了org.springframework.context.event.AbstractApplicationEventMulticaster.DefaultListenerRetriever#applicationListenerBeans中
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
// 遍历bean,并加入到监听器bean集合中
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}

// Publish early application events now that we finally have a multicaster...
// 获取早期的事件,这个事件是在准备刷新阶段(第一个阶段前戏阶段)设置进来的,是一个空的集合
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}

Spring的事件发布是如何设计的?

Spring的事件机制实际上是使用了观察者模式进行设计的,观察者模式分为两大角色,观察者和被观察者,只是Spring更加的抽象,在Spring中分为:

  • 事件ApplicationEvent。被观察者角色,监听器重点监听对象
  • 监听器ApplicationListener。观察者角色,用来监听对应的事件,做相应的处理
  • 事件发布者ApplicationEventPublisher。用来创建并发布事件
  • 事件的多播器ApplicationEventMulticaster。拥有监听器对象,提供发布事件的功能,遍历监听器,监听器来处理自己需要处理的事件,ApplicationEventMulticasterApplicationEventPublisher 的底层实现

AbstractApplicationContext这个SpringBeanFactory容器就是实现了ApplicationEventPublisher,可以对事件进行发布。在Spring中内置了很多的事件,比如:ContextClosedEventContextRefreshedEventContextStartedEventContextStopedEvent,而监听器的话Spring内置不多,Spring还提供了注解方式的配置监听器,注解为@EventListener

img

官网提供了两种配置监听器的方式,一种是实现ApplicationListener接口,一种是使用@EventLister注解配置,基于注解还可以配置异步的,排序的。

在自定义的事件发布时需要实现ApplicationEventPublisherAware 接口获取到ApplicationEventPublisher 进行发布事件。

示例

代码是Spring官网提供的,功能就是如果邮箱被拉黑,那么就不发送消息给邮箱,而是发布一个事件进行其他处理

  1. 定义一个事件源发布者:用来处理黑名单的邮箱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Service
public class EmailService implements ApplicationEventPublisherAware {

private List<String> blackList;

// 发布事件
private ApplicationEventPublisher applicationEventPublisher;

public EmailService()
{
blackList = new ArrayList<>();
blackList.add("123@qq.com");
}

public List<String> getBlackList() {
return blackList;
}

public void setBlackList(List<String> blackList) {
this.blackList = blackList;
}


@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}

public void sendEmail(String address, String context){
if (blackList.contains(address)){
// 发布一个事件,不发生消息到邮箱
applicationEventPublisher.publishEvent(new EmailEvent(this, address, context));
return;
}
System.out.println(".......发送邮箱 ......");
}

}
  1. 定义事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class EmailEvent extends ApplicationEvent {

private final String address;
private final String context;

/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public EmailEvent(Object source, String address, String context) {
super(source);
this.address = address;
this.context = context;
}

@Override
public String toString() {
return "EmailEvent{" +
"address='" + address + '\'' +
", context='" + context + '\'' +
'}';
}
}
  1. 定义监听器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class EmailApplicationListener implements ApplicationListener<EmailEvent> {

private String notifyAddress;

public String getNotifyAddress() {
return notifyAddress;
}

public void setNotifyAddress(String notifyAddress) {
this.notifyAddress = notifyAddress;
}

@Override
public void onApplicationEvent(EmailEvent event) {
System.out.println("收到事件,开始发布");
System.out.println("发送消息给" + notifyAddress + event.toString());
}
}
  1. 定义客户端
1
2
3
4
5
6
7
8
9
@ComponentScan("com.lq.spring")
public class Context {

public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(Context.class);
EmailService bean = ac.getBean(EmailService.class);
bean.sendEmail("123@qq.com", "test");
}
}
  1. 结果
1
2
收到事件,开始发布
发送消息给nullEmailEvent{address='123@qq.com', context='test'}

当然可有使用注解@EventListener进行配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class EmailNotifier {

private String notifyAddress;

public String getNotifyAddress() {
return notifyAddress;
}

public void setNotifyAddress(String notifyAddress) {
this.notifyAddress = notifyAddress;
}

@EventListener
public void processMessage(EmailEvent event){
System.out.println("收到事件,开始发布");
System.out.println("发送消息给" + notifyAddress + event.toString());
}
}

@EventListener解析流程

在Spring对注解进行扫描的时候,Spring会默认在容器中添加几个内置的Bean,并且以internal开头的Bean对象,这些Bean都是在AnnotationConfigUtils这个类的registerAnnotationConfigProcessors中设置的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建一个 EventListenerMethodProcessor 的BeanDefinition
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def,
EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}

// 创建一个 DefaultEventListenerFactory 的BeanDefinition
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}

设置了一个EventListenerMethodProcessor类还有一个DefaultEventListenerFactory,这两个类分别用来解析@EvenListener和创建ApplicationListener接口的适配器。

image-20220529154240747

EventListenerMethodProcessor 实现了Bean

FactoryPostProcessor接口、SmartInitializingSingleton接口和ApplicationContextAware接口,BeanFactoryPostProcessor接口是用来对BeanDefinition进行个性化设置解析等操作,SmartInitializingSingleton接口是在初始化所有的单例Bean之后触发的,也就是在preInstantiateSingletons方法中初始化Bean之后调用,ApplicationContextAware是用来获取ApplicationContext的。

  1. EventListenerMethodProcessorpostProcessBeanFactory方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    this.beanFactory = beanFactory;
    // 获取所有的 EventListenerFactory
    Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
    List<EventListenerFactory> factories = new ArrayList<>(beans.values());
    AnnotationAwareOrderComparator.sort(factories);
    this.eventListenerFactories = factories;
    }
  2. EventListenerFactory接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public interface EventListenerFactory {

    // 是否可以把当前方法包装成一个 ApplicationListener
    boolean supportsMethod(Method method);

    /**
    * 当前bean的名称 和 标注事件监听注解的方法 生成一个ApplicationListener
    */
    ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method);

    }

    这个类有2个实现DefaultEventListenerFactoryTransactionalEventListenerFactoryEventListenerMethodProcessor使用策略模式,调用者两个类,把符合要求的方法封装成ApplicationLister对象,注册到上下文(上下文会把它们注册到多播器)

  3. 封装成ApplicationListener

    afterSingletonsInstantiated方法中实现,在bean初始化后会被容器回调使用,获取容器中所有的bean,调用processBean方法把其中标注@EventListener的方法,or @EventListener符合注册的方法,包装一个ApplicationListener 并且注册到上下文中

    EventListenerMethodProcessor类中processBean的部分代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // ... 省略代码....
    // 遍历所有标有@EventListener注解的方法
    for (Method method : annotatedMethods.keySet()) {
    for (EventListenerFactory factory : factories) {
    if (factory.supportsMethod(method)) { // 选择合适的策略,EventListenerFactory的2个实现类
    Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
    // 使用工厂创建一个监听器,实际上创建的就是一个ApplicationListenerMethodAdapter
    ApplicationListener<?> applicationListener =
    factory.createApplicationListener(beanName, targetType, methodToUse);
    if (applicationListener instanceof ApplicationListenerMethodAdapter) {
    ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
    }
    // 添加到容器中,如果多播器不为空,则添加到多播器的监听器集合中
    context.addApplicationListener(applicationListener);
    break;
    }
    }
    }
    // ... 省略代码....


    public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
    return new ApplicationListenerMethodAdapter(beanName, type, method);
    }

    经典的 策略(2种策略)+工厂(创建ApplicationListener)+ 责任链设计模式

img

ApplicationListenerMethodAdapter

DefaultEventListenerFactory

这个类是在DefaultEventListenerFactory方法中创建而出的一个ApplicationListener适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
this.beanName = beanName;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.targetMethod = (!Proxy.isProxyClass(targetClass) ?
AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);

EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
// 解析第一个参数的类型, 没有参数会抛出异常
this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
this.condition = (ann != null ? ann.condition() : null);
this.order = resolveOrder(this.targetMethod);
}

处理事件

1
2
3
4
5
6
7
8
9
10
11
12
public void processEvent(ApplicationEvent event) {
Object[] args = resolveArguments(event); // 解析参数
if (shouldHandle(event, args)) {
Object result = doInvoke(args); //反射调用方法
if (result != null) {
handleResult(result); // 如果方法有返回值,后续还会处理返回值
}
else {
logger.trace("No result object given - no result to handle");
}
}
}

TransactionalEventListenerFactory

事务时讨论

发布事件

refresh方法的最后一个 方法finishRefresh中有个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
publishEvent(new ContextRefreshedEvent(this));


protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");

// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
// 把ContextRefreshedEvent变成ApplicationEvent
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}

// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 将给定的ContextRefreshedEvent事件多播到适当的侦听器
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}

// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}

跟进SimpleApplicationEventMulticaster#multicastEvent(applicationEvent, eventType);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
// 入参的event为ContextRefreshedEvent eventType为null
// 所以先确定eventType的值 因为eventType为null 所以获取的type为ContextRefreshedEvent的ResolvableType
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 这里是异步监听的处理 获取执行器的 默认是同步监听处理的
Executor executor = getTaskExecutor();
// getApplicationListeners(event, type) 是获取所有符合事件类型的监听 然后循环依次通知
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
// 如果不是异步 那么在这里执行监听通知
// 实际就是执行ApplicationListener的onApplicationEvent方法 也就是我们监听器的自定义监听内容
invokeListener(listener, event);
}
}
}

参考

https://blog.csdn.net/chenzoff/article/details/124682235

https://www.cnblogs.com/redwinter/p/16229572.html

https://www.cnblogs.com/cuzzz/p/16609679.html