使用AOP 代理后的方法调用执行流程,如图所示
也就是说我们首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。
问题
目标对象内部的自我调用将无法实施切面中的增强,如图所示
此处的this指向目标对象,因此调用this.b()将不会执行b事务切面,即不会执行事务增强
解决方案一
1 此处a方法中调用b方法时,只要通过AOP代理调用b方法即可,即可以进行方法拦截,如下所示
判断一个Bean是否是AOP代理对象可以使用如下三种方法:
AopUtils.isAopProxy(bean) : 是否是代理对象;
AopUtils.isCglibProxy(bean) : 是否是CGLIB方式的代理对象;
AopUtils.isJdkDynamicProxy(bean) : 是否是JDK动态代理方式的代理对象;
定义一个接口类AService,该接口类有两个方法 A()和AA()
package com.spring.service;
public interface AService {
public void A();
public void AA();
}
定义一个AServiceImpl实现类,该实现类实现了AService接口,在该接口里面 A() 方法会调用自身的AA()方法,会使用AopContext.currentProxy()得到代理对象,注意是代理对象,而不是自身对象。如果是自身对象再调用方法就不会被拦截了。因为拦截的逻辑只是织入到代理对象中。
package cn.spring.service.impl;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.spring.service.AService;
import com.spring.service.BeanSelfAware;
public class AServiceImpl implements AService{
public void A() {
System.out.println("A()");
((AService) AopContext.currentProxy()).AA();
}
@Override
public void AA() {
System.out.println("AA()");
}
}
定义一个advice,该advice会拦截service包或者子包里面的任何方法。
package cn.spring.advice;
public class Advice {
public void check(){
System.out.println("check()");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="aServiceImpl" class="cn.spring.service.impl.AServiceImpl" ></bean>
<bean id="advice" class="cn.spring.advice.Advice"></bean>
<aop:config expose-proxy="true">
<aop:aspect ref="advice">
<aop:pointcut expression="execution( * cn.spring.service.impl..*.*(..))" id="checkPoint"/>
<aop:before method="check" pointcut-ref="checkPoint"/>
</aop:aspect>
</aop:config>
</beans>
测试:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.*;
import com.spring.service.AService;
public class Test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
AService aService=(AService)context.getBean("aServiceImpl");
aService.A();
}
}
结果:
解决方案二
AServiceImpl 类实现ApplicationContextAware接口。
实现public void setApplicationContext(ApplicationContext applicationContext)throws BeansException 方法。该方法会自动回调,传入上下文对象。得到上下文对象,即可得到该类的代理对象。
private AService proxyObject;
proxyObject.AA();
}
@Override
public void AA() {
System.out.println("AA()");
}
public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {
System.out.println("setApplicationContext "+applicationContext.hashCode());
context=applicationContext;
self( );
}
System.out.println("self()");
proxyObject=(AService)context.getBean("aServiceImpl");
}
}
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("context "+context.hashCode());
AService aService=(AService)context.getBean("aServiceImpl");
aService.A();
解决方案三:
private ApplicationContext context;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(!(bean instanceof BeanSelfAware)) { //② 如果Bean没有实现BeanSelfAware标识接口 跳过
return bean;
}
if(AopUtils.isAopProxy(bean)) { //③ 如果当前对象是AOP代理对象,直接注入
((BeanSelfAware) bean).setSelf(bean);
} else {
//④ 如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入
//此种方式不适合解决prototype Bean的代理对象注入
((BeanSelfAware)bean).setSelf(context.getBean(beanName));
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {
this.context = applicationContext;
}
}
private ApplicationContext context;
private AService proxyObject;
System.out.println("A()");
proxyObject.AA();
}
@Override
public void AA() {
System.out.println("AA()");
}
}
<bean id="advice" class="cn.spring.advice.Advice"></bean>
<bean id="injectBeanSelfProcessor" class="com.spring.service.InjectBeanSelfProcessor"></bean>
<aop:config expose-proxy="true">
<aop:aspect ref="advice">
<aop:pointcut expression="execution( * cn.spring.service.impl..*.*(..))" id="checkPoint"/>
<aop:before method="check" pointcut-ref="checkPoint"/>
</aop:aspect>
</aop:config>
测试代码
public class Test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
AService aService=(AService)context.getBean("aServiceImpl");
aService.A();
}
}
测试结果
注意测试结果,发现会有两个check()方法,第一个check(),并不是调用A()方法而引起的拦截方法,是代理对象在调用InjectBeanSelfProcessor类的postProcessAfterInitialization方法中调用(BeanSelfAware) bean).setSelf(bean); 这行代码引起的对代理对象的拦截,如果要使其不拦截,修改xml文件,pointcut就可以了. 如下:
<aop:config expose-proxy="true">
<aop:aspect ref="advice">
<aop:pointcut expression="execution( * cn.spring.service.impl..*.A*(..))" id="checkPoint"/>
<aop:before method="check" pointcut-ref="checkPoint"/>
</aop:aspect>
</aop:config>
测试结果: