知道了原因,从来都不缺解决方案:
- 强制使用
CGLIB
动态代理机制 - 监听器(
@EventListener
)单独写在一个@Compnent
里。当然你可以使用内部类没关系,如下也是ok的,若需要高内聚小姑的话可以这么写:
@Slf4j @Service public class HelloServiceImpl implements HelloService { ... // 这里用private是木有关系的 需要注意的是若你使用内部类,建议务必是static的 否则可能报错如下: // Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'helloServiceImpl' is expected to be of type 'com.fsx.service.HelloServiceImpl' but was actually of type 'com.sun.proxy.$Proxy35' // 因为static的类初始化不依赖于外部类,而非static得依赖外部类(所以若不是CGLIB代理 一样出问题) @Component private static class MyListener { private ApplicationContext applicationContext; @EventListener(classes = ContextRefreshedEvent.class) public void applicationContextEvent(ContextRefreshedEvent event) { applicationContext = event.getApplicationContext(); } } ... }
Spring事件机制原理分析
事件收集(EventListenerMethodProcessor)
事件的收集前面讲了继承ApplicationListener的收集情况,那么此处就重点说说Spring4.2后提供的关于@EventListener注解的情况,看看Spring是怎么收集到这些方法,然后管理起来的。
一切源于Spring容器启动过程中:AnnotationConfigUtils.registerAnnotationConfigProcessors(context)注册的7大基础组件时,其中有一个是EventListenerMethodProcessor,它就是处理EventListener注解然后把它注册为一个特别的ApplicationListener的处理器。 当然还有一个EventListenerFactory(DefaultEventListenerFactory)
// 它是一个SmartInitializingSingleton,所以他会在preInstantiateSingletons()的最后一步执行~~~ // 并且它还是实现了BeanFactoryPostProcessor,所以它需要实现方法`postProcessBeanFactory` // @since 4.2 public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware { @Nullable private ConfigurableApplicationContext applicationContext; // 解析注解中的Conditon的 private final EventExpressionEvaluator evaluator = new EventExpressionEvaluator(); // 视图 这样set也变成线程安全的了 private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64)); // 这个方法是BeanFactoryPostProcessor的方法,它在容器的BeanFactory准备完成后,会执行此后置处理器 // 它的作用:BeanFactory工厂准备好后,就去找所有的EventListenerFactory 然后保存起来 // 此处:默认情况下Spring在准备Bean工厂的时候,会给我们注册一个`DefaultEventListenerFactory`, //如果你使用了注解驱动的Spring事务如@EnableTransactionManagement,它就会额外再添加一个`TransactionalEventListenerFactory` // 他俩的实现都非常的简单,下面会简单的说一下~~~ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { this.beanFactory = beanFactory; Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false); List<EventListenerFactory> factories = new ArrayList<>(beans.values()); // 会根据@Order进行排序~~~~ AnnotationAwareOrderComparator.sort(factories); this.eventListenerFactories = factories; } @Override public void afterSingletonsInstantiated() { // 从容器里获得所有的EventListenerFactory,它是用来后面处理标注了@EventListener方法的工厂(Spring默认放置的是DefaultEventListenerFactory,我们也可以继续放 支持@Order等注解) List<EventListenerFactory> factories = getEventListenerFactories(); ConfigurableApplicationContext context = getApplicationContext(); // 这里厉害了,用Object.class 是拿出容器里面所有的Bean定义~~~ 一个一个的检查 String[] beanNames = context.getBeanNamesForType(Object.class); for (String beanName : beanNames) { // 不处理Scope作用域代理的类。 和@Scope类似相关 if (!ScopedProxyUtils.isScopedTarget(beanName)) { Class<?> type = null; try { // 防止是代理,吧真实的类型拿出来 type = AutoProxyUtils.determineTargetClass(context.getBeanFactory(), beanName); } if (type != null) { // 对专门的作用域对象进行兼容~~~~(绝大部分都用不着) if (ScopedObject.class.isAssignableFrom(type)) { ... } // 真正处理这个Bean里面的方法们。。。 processBean(factories, beanName, type); } } } } protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) { // 缓存下没有被注解过的Class,这样再次解析此Class就不用再处理了 //这是为了加速父子容器的情况 做的特别优化 if (!this.nonAnnotatedClasses.contains(targetType)) { Map<Method, EventListener> annotatedMethods = null; try { // 这可以说是核心方法,就是找到这个Class里面被标注此注解的Methods们 // 在讲述到反射专题的时候,这个方法已经分析过~ annotatedMethods = MethodIntrospector.selectMethods(targetType, (MethodIntrospector.MetadataLookup<EventListener>) method -> AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class)); } catch (Throwable ex) { // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex); } } // 若一个都没找到,那就标注此类没有标注注解,那就标记一下此类 然后拉到算了 输出一句trace日志足矣 if (CollectionUtils.isEmpty(annotatedMethods)) { this.nonAnnotatedClasses.add(targetType); if (logger.isTraceEnabled()) { logger.trace("No @EventListener annotations found on bean class: " + targetType.getName()); } } //若存在对应的@EventListener标注的方法,那就走这里 // 最终此Method是交给`EventListenerFactory`这个工厂,适配成一个ApplicationListener的 // 适配类为ApplicationListenerMethodAdapter,它也是个ApplicationListener else { // Non-empty set of methods ConfigurableApplicationContext context = getApplicationContext(); // 处理这些带有@EventListener注解的方法们 for (Method method : annotatedMethods.keySet()) { // 这里面注意:拿到每个EventListenerFactory (一般情况下只有DefaultEventListenerFactory,但是若是注解驱动的事务还会有它:TransactionalEventListenerFactory) for (EventListenerFactory factory : factories) { // 加工的工厂类也可能有多个,但默认只有Spring注册给我们的一个 // supportsMethod表示是否支持去处理此方法(因为我们可以定义处理器,只处理指定的Method都是欧克的) Spring默认实现永远返回true(事务相关的除外,请注意工厂的顺序) if (factory.supportsMethod(method)) { // 简单的说,就是把这个方法弄成一个可以执行的方法(主要和访问权限有关) // 这里注意:若你是JDK的代理类,请不要在实现类里书写@EventListener注解的监听器,否则会报错的。(CGLIB代理的木关系) 原因上面已经说明了 Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); // 把这个方法包装成一个监听器ApplicationListener(ApplicationListenerMethodAdapter类型) // 通过工厂创建出来的监听器 也给添加进context里面去~~~~~ ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { // 这个init方法是把ApplicationContext注入进去 ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator); } // 添加进去 管理起来 context.addApplicationListener(applicationListener); // 这个break意思是:只要有一个工厂处理了这个方法,接下来的工厂就不需要再处理此方法了~~~~(所以工厂之间的排序也比较重要) break; } } } } } } }
就着这样,最终我们所有的Listener
都被管理了起来。
EventListenerFactory:为Method生产ApplicationListener
它是一个策略结果,准们为Method生产ApplicationListener
,类似一个转换器的作用。
// @since 4.2 // Strategy interface for creating {@link ApplicationListener} for methods annotated with {@link EventListener}. public interface EventListenerFactory { // 是否支持此方法 支持才create boolean supportsMethod(Method method); // 根据Method等相关信息,生成一个ApplicationListener ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method); }
它的实现类常见的有两个:DefaultEventListenerFactory
和TransactionalEventListenerFactory
DefaultEventListenerFactory
它是在Bean工厂准备好后,默认都会注册的6大Bean之一~~~~~
public class DefaultEventListenerFactory implements EventListenerFactory, Ordered { // 它希望自己是被最后执行的~~~ private int order = LOWEST_PRECEDENCE; public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } // 匹配所有的标注了@EventListener 的方法 public boolean supportsMethod(Method method) { return true; } // ApplicationListenerMethodAdapter是一个通用的方法监听适配器~~~~ @Override public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) { return new ApplicationListenerMethodAdapter(beanName, type, method); } }
TransactionalEventListenerFactory
它是一个和注解驱动的声明式事务相关的监听器工厂。用于处理@TransactionalEventListener
这个注解标注的方法
public class TransactionalEventListenerFactory implements EventListenerFactory, Ordered { private int order = 50; public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } // 很显然,它要求此方法必须标注@TransactionalEventListener这个注解 // 备注:@TransactionalEventListener继承自@EventListener @Override public boolean supportsMethod(Method method) { return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class); } // ApplicationListenerMethodTransactionalAdapter这个是适配事务监听方法的适配器 // 它继承自:ApplicationListenerMethodAdapter @Override public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) { return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method); } }
关于@EventListener
中condition使用
我们先看@EventListener
注解本身的定义:
// @since 4.2 只能标注在方法上 和当作元注解使用 @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EventListener { @AliasFor("classes") Class<?>[] value() default {}; // 标注此方法需要处理的事件类型~~~ @AliasFor("value") Class<?>[] classes() default {}; /// 则个条件大多数使用者都非常的默认,毕竟绝大多数情况下都是不需要使用的~~~ // 总体上,它是根据条件,判断此handler是否需要处理这事件 更加细粒度的控制 支持SpEL表达值 // 内置的#root.event表示当前事件,#root.args当前方法的入参(数组形式) String condition() default ""; }
秉着知其然,知其所以然的态度,下面看看condition条件的生效时机。
首先当然是ApplicationListenerMethodAdapter:
// @since 4.2 @EventListener最终都会适配成它 // GenericApplicationListener接口提供方法:boolean supportsEventType(ResolvableType eventType); public class ApplicationListenerMethodAdapter implements GenericApplicationListener { // 事件表达式处理器 默认使用的SpEL去解析 只是对它进行了增强 @Nullable private EventExpressionEvaluator evaluator; 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注解信息 备注:至少指定一个监听类型 EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class); // 按照上例 此处为org.springframework.context.event.ContextRefreshedEvent this.declaredEventTypes = resolveDeclaredEventTypes(method, ann); // 拿到条件信息 SpEL中有用 this.condition = (ann != null ? ann.condition() : null); // 从此处也能看出,它是支持在方法上标注@Order来控制执行顺序的 this.order = resolveOrder(this.targetMethod); } // 判断该处理器 是否支持当前类型的事件 // 判断思路很简单:类型匹配上了 就表示可以处理这个事件(支持事件的泛型依赖匹配~~~) // 关于condition 是在process处理的时候会生效的 @Override public boolean supportsEventType(ResolvableType eventType) { for (ResolvableType declaredEventType : this.declaredEventTypes) { if (declaredEventType.isAssignableFrom(eventType)) { return true; } if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) { ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric(); if (declaredEventType.isAssignableFrom(payloadType)) { return true; } } } return eventType.hasUnresolvableGenerics(); } // 开始处理事件~~~ 此处会checking if the condition match and handling non-null result @Override public void onApplicationEvent(ApplicationEvent event) { processEvent(event); } public void processEvent(ApplicationEvent event) { // 解析参数,很简单 主要是兼容PayloadApplicationEvent 把事件拿出来 // 返回的数组要么为[],总之最多只有一个参数 就是事件本身 Object[] args = resolveArguments(event); // 此处是我们本文的重点,就是解析condition 条件的地方,下面专门讨论,现在继续往下走 // 总之就是根据事件源、绝大多数情况下args里面装的就是这个event~~~~~ if (shouldHandle(event, args)) { Object result = doInvoke(args); // 这一句非常的简单 就是调用此方法Method~ // 这一步就是@EventListener最大的优势。如果它的返回值不为null,那么它可以行使事件链,可以继续发布事件 // 把返回值当作事件继续publish(返回值可以是个Object,最终被包装成payload事件~~~~) if (result != null) { handleResult(result); } else { logger.trace("No result object given - no result to handle"); } } } }
从源码处可知,真正执行的时候,还要经过condition
这一关:下面作为本处的重点,重点分析一下此方法:
private boolean shouldHandle(ApplicationEvent event, @Nullable Object[] args) { if (args == null) { return false; } String condition = getCondition(); // condition默认是空串 只有配置了才会去执行~~~ 是用的解析器是EventExpressionEvaluator if (StringUtils.hasText(condition)) { Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null"); // 最终委托给EventExpressionEvaluator去解析 // 备注EventExpressionEvaluator是个内部使用的类,只有此处解析用到了~~~ return this.evaluator.condition(condition, event, this.targetMethod, this.methodKey, args, this.applicationContext); } return true; }
EventExpressionEvaluator
:表达式执行器
它是处理SpEL表达式解析的实用程序类。用于可重用、线程安全的组件
// @since 4.2 CachedExpressionEvaluator也是4.2出来的,提供了缓存的能力,并且内部使用SpEL来解析表达式~~~ class EventExpressionEvaluator extends CachedExpressionEvaluator { // ExpressionKey为CachedExpressionEvaluator的一个吧内部类 // Expression为:org.springframework.expression.Expression 表达式 private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64); // 它只有这个一个方法~~~ public boolean condition(String conditionExpression, ApplicationEvent event, Method targetMethod, AnnotatedElementKey methodKey, Object[] args, @Nullable BeanFactory beanFactory) { // EventExpressionRootObject就是简单的持有传入的两个变量的引用而已~~~ // 这个RootObject是我们#root值的来源~~~~ EventExpressionRootObject root = new EventExpressionRootObject(event, args); // 准备一个执行上下文。关于SpEL的执行上文,请参照下面的链接 // 这个执行上下文是处理此问题的重中之重~~~~下面会有解释 // getParameterNameDiscoverer 它能够根据方法参数列表的名称取值~~~强大 // 同时也支持a0、a1... 和 p0、p1...等等都直接取值~~这个下面会为何会支持得这么强大的原因~~~ MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(root, targetMethod, args, getParameterNameDiscoverer()); if (beanFactory != null) { evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); } // 可以看到 请让表达式的值返回bool值~~~~~~~ // getExpression是父类的~ 最终有用的就一句话:expr = getParser().parseExpression(expression); // 默认采用的是SpelExpressionParser这个解析器解析这个表达式 return (Boolean.TRUE.equals(getExpression(this.conditionCache, methodKey, conditionExpression).getValue( evaluationContext, Boolean.class))); } }
关于SpEL这块,强烈建议先参照:
【小家Spring】SpEL你感兴趣的实现原理浅析spring-expression~(SpelExpressionParser、EvaluationContext、rootObject)
MethodBasedEvaluationContext :执行器的执行上下文
它是个SpEL中的EvaluationContext。这里最重要的是MethodBasedEvaluationContext这个执行上下文,其实也比较简单。比较有意思的一点是它支持取值除了#root.xxx方式,还支持有a0、a1... 和 p0、p1...等方式
// @since 4.2 public class MethodBasedEvaluationContext extends StandardEvaluationContext { ... private final Object[] arguments; private final ParameterNameDiscoverer parameterNameDiscoverer; // .... 这里是按照变量名或者a0这种方式取值的核心处理~~~~ protected void lazyLoadArguments() { // 支持根据参数名取值 String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method); ... // variables是个Map<String, Object> variables for (int i = 0; i < paramCount; i++) { // 支持a0、a1..方式取值 setVariable("a" + i, value); // 支持p0、p1..方式取值 setVariable("p" + i, value); // 根据参数列表的形参名字进行取值 如id、name或者obj对象都是ok的(如果是对象,也是支持Bean导航的,因为SpEL是支持的) if (paramNames != null && paramNames[i] != null) { setVariable(paramNames[i], value); } } } }
处理Event的执行上下文是MethodBasedEvaluationContext,支持cache相关注解使用的上下文是CacheEvaluationContext。
由于CacheEvaluationContext是MethodBasedEvaluationContext的子类,所以我们cache相关注解比如@Cacheable等等,他们使用SpEL时也都是支持使用上面方式取值的~~~~
关于cache注解的表达式执行器请参考:org.springframework.cache.interceptor.CacheOperationExpressionEvaluator
事件发布(SimpleApplicationEventMulticaster)
我们一般都会使用AbstractApplicationContext#publish()来发布一个事件:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { // Decorate event as an ApplicationEvent if necessary // 如果这个事件不是ApplicationEvent类型,那就包装成这个类型 ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { // 注意此处:第一个参数为source,这里传的source,第二个是payload,才传的是事件本身 applicationEvent = new PayloadApplicationEvent<>(this, event); // 若没有指定类型。就交给PayloadApplicationEvent<T>,它会根据泛型类型生成出来的~~~ 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 { // 最终把这些时间都委派给了`ApplicationEventMulticaster` 让它去发送事件 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Publish event via parent context as well... // 此处注意:特别重要,如果是父容器,也会向父容器里广播一份~~~~~ if (this.parent != null) { // 这个判断的用意是,既然eventType已经解析出来了,所以就调用protected内部方法即可,而不用再次解析一遍了 if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } // 如果是普通的发布,就没有eventType了 else { this.parent.publishEvent(event); } } }
最后事件都会向父类里广播一份,这个就特别想
js事件冒泡
机制
因此重点看看ApplicationEventMulticaster#multicastEvent
:它的唯一实现为:
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { // 若set了一个执行器,那所有的监听器都将会异步执行 @Nullable private Executor taskExecutor; // 监听者执行失败的回调~~~~~~(比如做回滚等等) @Nullable private ErrorHandler errorHandler; @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); // 这里面有个细节:如果有执行器executor ,那就会扔给线程池异步去执行 // 默认情况下是没有的(Spring默认情况下同步执行这些监听器的) 我们可以调用set方法配置一个执行器(建议) for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); // 放在线程池里执行,相当于异步执行。绝大多数情况下,这里都是null if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } } private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { // 如果是实现了ApplicationListener接口,则直接调用其中的onApplicationEvent()方法; //如果是用@EventListener注释,则调用ApplicationListenerMethodAdapter中的onApplicationEvent()方法 listener.onApplicationEvent(event); } }
ApplicationListenerMethodAdapter#onApplicationEvent
:
@Override public void onApplicationEvent(ApplicationEvent event) { processEvent(event); } public void processEvent(ApplicationEvent event) { // 获取参数,最终会交给回调的方法的。事件类型是PayloadApplicationEvent,那就把.getPayload(),否则就是event本身喽 Object[] args = resolveArguments(event); // 解析condition表达式(注意,此处把args传进去了) 因此我们表达式里是可以用这个参数的哦 if (shouldHandle(event, args)) { // 就是执行目标方法,我们一般返回值都是void,所以就是null // 但是,但是,但是注意了,此处若返回的不是null,还有处理~~~~非常给力: Object result = doInvoke(args); if (result != null) { // 如果返回值是数组或者Collection,会把里面内容当作事件循环publishEvent // 如果就是个POJO,那就直接publish // 事件的传递性 就这么的来了,强大啊 handleResult(result); } else { logger.trace("No result object given - no result to handle"); } } }
Spring的使用
@EventListener
监听事件。若监听方法有返回值,那将会把这个返回值当作事件源,一直发送下去,直到返回void或者null
停止
@EventListener(value = {ContextRefreshedEvent.class}) public List<Child> handle(Object o) { List<Child> childList = new ArrayList<>(); childList.add(new Child("1")); childList.add(new Child("2")); return childList; } // 因为上个方法有返回 所以事件会传递到此处 @EventListener(Child.class) public void handChild(Child c) { System.out.println(c.getName() + " 发来了事件"); } 输出: 1 发来了事件 2 发来了事件
Spring事件传递的应用场景,巧妙的使用,可以事半功倍。(当然必须了解原理,才能运用自如)
这里,抽象父类AbstractApplicationEventMulticaster内部的一些方法是不容忽视的:比如getApplicationListeners:获取对应的监听者