spring AOP 浅析

说到spring的Transactional,必须先了解spring AOP的原理,先看个简单的例子

//一个普通的类
public class CyclicA {
   
    public void printlnMethod(){
        System.out.println("i am method");
    }
}

/**
 * 切面,包含了一个Pointcut和多个Advice
 */
@Aspect
public class aspectjTest {

	//切点
    @Pointcut("execution(* *.printlnMethod(..))")
    public void test(){

    }

	//前置通知
    @Before("test()")
    public void before(JoinPoint joinPoint){
        System.out.println("传入参数:"+ Arrays.asList(joinPoint.getArgs()));
        System.out.println("before");
    }

	//后置通知
    @After("test()")
    public void after(){
        System.out.println("after");
    }

	//环绕通知
    @Around("test()")
    public Object  around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("aroundBefore");
        Object o = null;
        try {
            //执行拦截器方法链,如果是最后一个拦截器,这里执行原对象方法
            proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("aroundAfter");
        return o;
    }

}

<!--spring-core.xml--> 
<bean class="spring.aspectjTest"></bean>
<aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/>

//测试方法
public static void main(String[] args){
        ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext("spring-core.xml");
        CyclicA cyclicA = (CyclicA) factory.getBean("cyclicA");
        cyclicA.printlnMethod();
    }

打印结果

aroundBefore
传入参数:[]
before
i am method
aroundAfter
after

为了更好的理解,先介绍一下AOP相关的术语和对应的接口类

1、JoinPoint(连接点)

public interface JoinPoint {
	 //执行方法链,该方法链会被拦截器拦截
    Object proceed() throws Throwable;

    Object getThis();

    AccessibleObject getStaticPart();

}

简单的理解,连接点就是目标对象可以被代理的部分,SpringAOP代理是方法级,所以目标对象的每个方法都可以认为是一个连接点.
上面只是列举了JoinPoint接口的常用方法,在前置通知中,可以通过JoinPoint获取参数进行预处理或者记录日志等

//前置通知
    @Before("test()")
    public void before(JoinPoint joinPoint){
        System.out.println("传入参数:"+ Arrays.asList(joinPoint.getArgs()));
        System.out.println("before");
    }

2、Pointcut(切点)

public interface Pointcut {

	ClassFilter getClassFilter();

	MethodMatcher getMethodMatcher();
}

实际运用中,并不是每一个连接点都需要去代理,那么如何去选择需要的连接点,这就用到了切点Pointcut,从接口方法的名称可以看出来,切点主要的工作就是根据制定的规则匹配连接点,例如上面的例子

//切点
    @Pointcut("execution(* *.printlnMethod(..))")
    public void test(){

    }

3、Advice(通知)

public interface Advice {
}

//前置通知
public interface MethodBeforeAdvice extends BeforeAdvice {
	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}

//后置通知
public interface AfterReturningAdvice extends AfterAdvice {
	void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;

}

匹配到指定的连接点,我们就可以做一些逻辑处理了,而这些逻辑处理就是通知,分为

前置通知(Before advice)- 在目标方法调用前执行通知
后置通知(After advice)- 在目标方法完成后执行通知
返回通知(After returning advice)- 在目标方法执行成功后,调用通知(异常则不调用)
异常通知(After throwing advice)- 在目标方法抛出异常后,执行通知
环绕通知(Around advice)- 在目标方法调用前后均可执行自定义逻辑

4、顾问(Advisor)
可以认为是Advice的装配器,让advice能正确的匹配到目标方法

public interface Advisor {
	Advice getAdvice();
}

public interface PointcutAdvisor extends Advisor {
	Pointcut getPointcut();
}

5、拦截器(Interceptor)
通常一个目标方法可能存在多个通知乃至多个切面,并且通知的时机也不相同,例如前置通知、后置通知. springAop通过JoinPoint.proceed()执行方法链,通过拦截器对方法链进行拦截,在此过程中决定advice执行的时机,当所有拦截器执行完毕,将通过反射执行目标方法

public interface Interceptor extends Advice {
}

public interface MethodInterceptor extends Interceptor {
	//核心方法,通过该方法执行拦截器的逻辑	
	Object invoke(MethodInvocation invocation) throws Throwable;
}

//看一个例子,前置通知拦截器
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

	//拦截器包装了前置通知
	private final MethodBeforeAdvice advice;


	/**
	 * Create a new MethodBeforeAdviceInterceptor for the given advice.
	 * @param advice the MethodBeforeAdvice to wrap
	 */
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

	//对方法链进行了拦截,在下一个方法执行之前执行前置通知逻辑
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}

}

6、织入(Weaving)
把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象,这样的行为叫做织入,上面Advice、Advisor、Interceptor的逻辑就是织入的具体实现

下面通过一个简单的时序图来说明下spring AOP的一个大概实现过程
在这里插入图片描述
1、通过beanFactory.getBean(String beanName)获取bean的时候,代理流程就开始了

2、AbstractAutoProxyCreator实现了BeanPostProcessor接口,所以可以通过postProcessAfterInitialization方法在bean初始化之后进行处理

3、获取匹配目标bean的所有通知Advice 并且包装成Advisor列表

4、对步骤3的分解,查找所有类型是Advisor的bean, 子类AnnotationAwareAspectJAutoProxyCreator对该方法进行了扩展,可以查找所有加了@Aspect注解的bean,并且将其中的PointCut 和 Advice包装成Advisor

5、对步骤3的分解,对Advisor列表进行分析,寻找匹配目标bean的Advisor

8、如果有匹配的Advisor,开始针对目标bean创建代理类,这个实现最终交给DefaultAopProxyFactory

9、默认如果目标对象实现了接口,那么交给JdkDynamicAopProxy实现JDK动态代理
否则交给ObjenesisCglibAopProxy使用Cglib创建代理

至此一个代理类就顺利创建了,那么接下来看看这个代理类执行方法的时候springAop是如何实现织入的

以一开始的例子,假设只注入了前置通知和后置通知,通过JDK动态代理

在这里插入图片描述

2、假设是JDK动态代理,执行方法的时候,调用invoke方法进行拦截

3、如果配置了exposeProxy属性为true,则将当前代理缓存到ThreadLocal中

4、通过Advisor包装的advice创建相应的拦截器,加入到拦截器数组中,例如前面提到的MethodBeforeAdviceInterceptor以及后置通知AspectJAfterAdvice,这里的AspectJAfterAdvice自身就实现了MethodInterceptor,所以他既是Advice又是Interceptor

//在下一个方法执行之前执行前置通知逻辑  MethodBeforeAdviceInterceptor
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}

//在方法链调用结束后再执行后置通知逻辑 AspectJAfterAdvice
@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		finally {
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}

7、通过ReflectiveMethodInvocation类调用方法链proceed,主要逻辑就是从拦截器数组中按顺序取出拦截器执行invoke,并且将自身也当作参数传入, 由拦截器决定下一个proceed和advice的调用顺序,以此正确的织入前置通知、后置通知. 当所有的拦截器全部调用完毕,则通过反射执行目标方法返回

看一下这部分代码

public Object proceed() throws Throwable {
		//	当所有当拦截器都调用完毕,最后通过反射执行目标方法
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		//获得下一个拦截器
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			//拦截器调用
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

Spring AOP还是相对比较简单的,代理的级别也只是方法级,但是对于绝大多数应用已经足够了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值