同類中嵌套AOP--註解事物在同一類中嵌套調用不生效

 

  1、背景

   spring的註解事物沒有生效,異常數據沒有回滾。java

  2、具體現象

   同一個類中有多個方法,A方法沒有開啓事物,B方法經過註解開啓事物,B方法的事物註解沒有生效。代碼以下:spring

   

package com.test.transcation;

import org.springframework.transaction.annotation.Transactional;

/**
 * Created by shaobo on 2018/4/9.
 */
public class Insert {

    public void a(){
        this.b();
    }

    @Transactional
    public void b(){
        /**
         * 一通數據庫操做
         */
        throw new RuntimeException();
    }
}

    執行方法a(),方法b()中的數據成功更新到了數據庫中,預期結果爲數據回滾。數據庫

   3、分析

    咱們知道spring的事物是經過cglib來生成動態代理的。先來看JDK的動態代理。ide

    

package com.test.proxy;

/**
 * 接口
 */
public interface UserInterface {
     void update();
     void complex();
}

package com.test.proxy;

/**
 * 實現
 */
public class UserService implements UserInterface {
    @Override
    public void update() {
        System.out.println("userDao.update()");
    }

    @Override
    public void complex(){
        System.out.println("begin complex()");
        this.update();
        System.out.println("end complex()");
    }
}

package com.test.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * InvocationHandler
 */
public class JDKProxy implements InvocationHandler {

    private Object target;


    public  void bind(UserInterface userInterface){
        target = userInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before Method Invoke " + method.getName());
        Object object = method.invoke(target,args);
        System.out.println("After Method Invoke " + method.getName());
        return object;
    }
}

package com.test.proxy;

import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        UserService userService = new UserService();
        JDKProxy jdkProxy = new JDKProxy();
        jdkProxy.bind(userService);
        UserInterface userInterface =  (UserInterface)Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),jdkProxy);
        userInterface.complex();
    }
}

 執行結果:咱們經過debug方式執行關鍵一下被代理對象this

 


 

 

 


    咱們能夠看到this對象爲實際對象,因此update方法並無被攔截。spa

 

    接下來咱們看一下cglib,debug

package com.test.cglib;

/**
 * 代理對象
 */
public class UserDao {
    public void update() {
        System.out.println("userDao.update()");
    }

    public void complex() {
        System.out.println("begin complex()");
        this.update();
        System.out.println("end complex()");
    }
}

package com.test.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 代理類
 */
public class DaoProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Before Method Invoke " + method.getName());
        methodProxy.invokeSuper(o, objects);
        System.out.println("After Method Invoke " + method.getName());
        return o;
    }
}

package com.test.cglib;

import net.sf.cglib.proxy.Enhancer;

public class Test {
    public static void main(String[] args) {
        DaoProxy daoProxy = new DaoProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserDao.class);
        enhancer.setCallback(daoProxy);
        UserDao dao = (UserDao)enhancer.create();
        dao.complex();
    }
}

    執行結果以下設計

 


 

 

    咱們能夠發現cglib中的this指向代理對象,因此也會執行攔截方法。3d

    spring aop的模型大體是這樣的:代理

 

    這樣會致使methodB()並不能被通知到。我想若是以下圖這樣的話就不會出現這種問題,但spring這樣這樣設計確定有其理由,須要後續繼續研究。

    

    4、解決辦法

    知道了緣由就好解決了,方法有以下兩種。

    一、不要使用spring 中嵌套aop,將這種嵌套放在兩個類中(推薦)。

    二、((UserInterface)AopContext.currentProxy()).update(),經過此方法得到代理對象直接調用。

    踩過的坑都是流過的淚。

相關文章
相關標籤/搜索