spring会根据定义的AdviceMode类型(PROXY, ASPECTJ)选择不同的aop实现方式, 一般使用的是PROXY 。
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
/**
* @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for
* {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively
*/
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case org.springframework.context.annotation.AdviceMode.PROXY:
return new String[] { ProxyAsyncConfiguration.class.getName() };
case org.springframework.context.annotation.AdviceMode.ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
default:
return null;
}
}
}
Xml的配置
- < aop:aspect>:定义切面(切面包括通知和切点), 只需要定义一般的bean就行。
//定义切面 public class SleepHelperAspect{ public void beforeSleep(){ System.out.println("睡觉前要脱衣服!"); } public void afterSleep(){ System.out.println("起床后要穿衣服!"); } } //aop配置 <bean id="sleepHelperAspect" class="com.ghs.aop.SleepHelperAspect"></bean> <aop:config> <aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/> <aop:aspect ref="sleepHelperAspect"> <!--前置通知--> <aop:before method="beforeSleep" pointcut-ref="sleepPointcut"/> <!--后置通知--> <aop:after method="afterSleep" pointcut-ref="sleepPointcut"/> </aop:aspect> </aop:config>
- < aop:advisor>:定义通知器(通知器跟切面一样,也包括通知和切点), 引用的通知时,通知必须实现Advice接口。
//定义通知 public class SleepHelper implements MethodBeforeAdvice,AfterReturningAdvice{ @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("睡觉前要脱衣服!"); } @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println("起床后要穿衣服!"); } } //aop配置 <bean id="sleepHelper" class="com.ghs.aop.SleepHelper"></bean> <aop:config> <aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/> <aop:advisor advice-ref="sleepHelper" pointcut-ref="sleepPointcut"/> </aop:config>
@EnableAsync 原理
常规切面配置是定义 Advisor,其内绑定了Advice及Pointcut 关系。
启动装配
SpringBoot当要使用@Async时,需启动类显示声明@EnableAsync 来 注入 AsyncConfigurationSelector(一个AdviceModeImportSelector);默认我们选择的是AdviceMode.PROXY, 所以最终注入的是ProxyAsyncConfiguration, 构建了一个内部含Advisor的类:AsyncAnnotationBeanPostProcessor。 那这个内部Advisor怎么初始化的呢?
// AbstractAsyncConfiguration
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableAsync = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
if (this.enableAsync == null) {
throw new IllegalArgumentException(
"@EnableAsync is not present on importing class " + importMetadata.getClassName());
}
}
// ProxyAsyncConfiguration extends AbstractAsyncConfiguration
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
if (this.executor != null) {
bpp.setExecutor(this.executor);
}
if (this.exceptionHandler != null) {
bpp.setExceptionHandler(this.exceptionHandler);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}
AsyncAnnotationBeanPostProcessor 继承了 BeanFactoryAware 接口。在setBeanFactory方法中,初始化了AsyncAnnotationAdvisor !这就比较熟悉了。
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
}
PS:@EnableAsync 可以通过内部属性annotation 来指定注自定义解类型 。
ProxyAsyncConfiguration构建AsyncAnnotationBeanPostProcessor时这个参数值被注入到其内部属性AsyncAnnotationType,并在 setBeanFactory 方法中被注入进AsyncAnnotationAdvisor)
AsyncAnnotationAdvisor
看下具体的构造函数
在这个Advisor的构造函数中明确构建了advice -> AnnotationAsyncExecutionInterceptor 与 pointcut,并指定切面注解类型是@Async。
(番外: 在自定义advisor 时, advice与 pointcut 最好是全局共享属性)
AnnotationAsyncExecutionInterceptor
其父类AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor。
首先我们来看下MethodInterceptor的定义, 可见 MethodInterceptor就是所谓的Advice。
public interface Interceptor extends Advice {}
public interface MethodInterceptor extends Interceptor {
/**
* Implement this method to perform extra treatments before and
* after the invocation. Polite implementations would certainly
* like to invoke {@link Joinpoint#proceed()}.
* @param invocation the method invocation joinpoint
* @return the result of the call to {@link Joinpoint#proceed()};
* might be intercepted by the interceptor
* @throws Throwable if the interceptors or the target object
* throws an exception
*/
Object invoke(MethodInvocation invocation) throws Throwable;
}
AsyncExecutionAspectSupport.invoke 就是一个切面在命中时的具体处理逻辑。比较重要的是如何选择异步处理的线程池AsyncTaskExecutor过程 determineAsyncExecutor:以方法作为区分从beanFactory中去查找,名称优先为方法注解@Async指定异步处理线程池对象Qualifier名 ,其次为 "taskExecutor" 的Executor.class类型对象。
ComposablePointcut
对于单个pointcut由MethodMatcher (方法匹配)与 ClassFilter (类匹配)组合构成。
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
/**
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
大体上常用的有
- AnnotationMatchingPointcut : 针对于指定的类上注解、方法上注解 。依不同构造方法的入参来指定注解类型
- 抽象类 StaticMethodMatcherPointcut 的 具体实现 AnnotationClassOrMethodPointcut : 与AnnotationMatchingPointcut 用法大体一致。只针对于同一个注解来判定方法或类。
- AspectJExpressionPointcut :主要针对于AspectJ的语法解析
- ComposablePointcut : 组合用法。 可以(intersection 交集 或 union 并集)多个pointcut, ClassFilter、 MethodMatcher
以下是spring针对Async构建的ComposablePointcut ,是由2个AnnotationMatchingPointcut 取并集而成。这也佐证了对于类及方法上的@Async都有效
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
ComposablePointcut result = null;
for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
if (result == null) {
result = new ComposablePointcut(cpc);
}
else {
result.union(cpc);
}
result = result.union(mpc);
}
return (result != null ? result : Pointcut.TRUE);
}
那是什么时候执行条件过滤呢?
调用入口
我们回到AsyncAnnotationBeanPostProcessor,不仅仅继承BeanFactoryAware, 而且还实现了BeanPostProcessor接口!
在执行postProcessAfterInitialization方法内, 通过isEligible 方法对bean进行判定是否需要构建代理: 会优先通过Pointcut的ClassFilter进行判定, 其次是MethodMatcher!