spring aop对象的内部方法嵌套无效
看看内部通话失败的经典示例
//如果存在事务,则抛出异常。
public class TxDemo{
@Transactional(propagation = Propagation.NEVER)
public void serviceH() {
UserInfoVo infoVo = new UserInfoVo();
infoVo.setAge(1000);
infoVo.setUserName("H");
userInfoDao.save2(infoVo);
}
public void serviceI() {
UserInfoVo infoVo = new UserInfoVo();
infoVo.setAge(1000);
infoVo.setUserName("I");
userInfoDao.save2(infoVo);
}
@Transactional
public void serviceHI() {
serviceI();
serviceH();
}
}
在上面的示例中,在serviceH标头上带注释的事务表明,如果当前存在事务,则会引发异常. 在我的测试示例中,serviceHI中有一个事务,然后再次调用serviceH. 通常情况下,应该引发异常,但是结果也不是异常,并且执行是平稳的. 为什么?
让我们从实际战斗中了解AOP的基本原理.
Spring AOP的底层不过是动态代理动态方法调用 为什么不安全,而事务也基于动态代理,那么为什么不编写动态代理呢?
代码: 测试快球
第一步是定义一个接口. 让我们基于JDK接口创建一个动态代理模型.
public interface ReadableInterface {
void read();
void write();
}
第二步: 实现接口类
public class StudentImpl implements ReadableInterface {
@Override
public void read() {
System.out.println("read........");
write();
}
@Override
public void write() {
System.out.println("write ..........");
}
}
创建代理:
第3步: 创建jdk动态代理
public class ProxyObjectImpl implements InvocationHandler {
private Object proxied = null;
public ProxyObjectImpl() {
}
public ProxyObjectImpl(Object proxied) {
this.proxied = proxied;
}
/**
* // 该方法负责集中处理动态代理类上的所有方法调用
//
*@param proxy:第一个参数既是代理类实例
*@param method:第二个参数是被调用的方法对象
*@param args:第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
* 注意:千万别把第一个参数放入method.invoke里面哦,不然就会死循环了,代理类执行代理类,变成自己代理自己了
*@return
*@throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理做事前。。。。。。。");
Object o=method.invoke(proxied, args);
System.out.println("代理做事后。。。。。。");
return o;
}
}
第4步: 模拟客户通话
public class DynamicProxyDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
proxy1();
}
private static void proxy2() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
StudentImpl student = new StudentImpl();
//获取委托方
ReadableInterface readableInterface = (ReadableInterface) Proxy.newProxyInstance(StudentImpl.class.getClassLoader(),
StudentImpl.class.getInterfaces(),
new ProxyObjectImpl(student));
readableInterface.read();
}
}
输出为:
代理做事前。。。。。。。
read.........
write ..........
代理做事后。。。。。。
当客户调用read时,将使用代理,因此将打印“代理之前,代理之后”的字样;但是write是直接输出的,因为write()在read()内部被调用.
整个过程等同于
//客户端如下:
main(){
proxy.read();
}
//被代理的接口类最终的样子如下:proxy.class
proxy.read(){
print("调用前...");
read();
print("调用后...");
}
proxy.write(){
print("调用前...");
write();
print("调用后...");
}
read(){
this.write();
}
write(){
}
您看到了吗?在Clint中,proxy.read()被调用;剩下的代理,在read()中,直接调用write,而不是proxy.write();
这不是Spring AOP的问题,而是我们使用代理的问题.
以上分析仅是基于接口代理的jdk引起的问题.
CGLib是基于类的代理,而不是接口代理.
第一步: 基础课
public class Person {
public void play(){
System.out.println("person is play ....");
}
public void getMoney(){
play();
System.out.println("give me 100$");
}
}
第2步: 编写代理
public class CglibProxyHandler implements MethodInterceptor {
public CglibProxyHandler() {
}
/**
* 1、代理对象;2、委托类方法;3、方法参数;4、代理方法的MethodProxy对象。
*@param o
*@param method
*@param objects
*@param methodProxy
*@return
*@throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("proxy before......");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("proxy after .......");
return o1;
}
}
第三步: 客户编写
public class CglibProxyDome {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib");
CglibProxyHandler cglibProxy = new CglibProxyHandler();
//jdk需要提供接口,cglib需要是非私有类,且不能处理final关键字修饰的方法
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(Person.class);
//设置回调对象
enhancer.setCallback(cglibProxy);
Person person = (Person) enhancer.create();
person.getMoney();
}
}
打印结果:
proxy before......
proxy before......
person is play ....
proxy after .......
give me 100$
proxy after .......
您会发现内部方法调用也离开了代理!
为什么?
基于类的代理模式是生成子类以继承父类
CGLib生成的子类的最终形式如下:
public class Person$$EnhasncerBy....... extends Person{
public final void play(){
print("代理前");
super.play();
print("代理后");
}
public final void getMoney(){
print("代理前");
super.getMoney();
print("代理后");
}
}
public class Demo(){
Person p=new Person$$Enhasncer.....()
p.getMoney();
}
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/tongxinshuyu/article-273206-1.html