通过@Async来看spring的AOP实现方式

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

看下具体的构造函数

251723fdb8eb772aa747fcef540cf8702d6.jpg

在这个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类型对象。

56d0485e0abcb8f14fd1e554357bd83726e.jpg

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!

8c0d54e5f8b148a29d198d1c566186016e1.jpg

b412447550a9e368e98bffe2eb5e57b7d6f.jpg

dfcb230a168a4726ff9f508135a4c68a910.jpg

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/u/3434392/blog/3067322

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值