说到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还是相对比较简单的,代理的级别也只是方法级,但是对于绝大多数应用已经足够了