静态代理

/**
 * 定义接口
 */
interface UserDao {
    void save();
}

/**
 * 实现接口
 */
class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

/**
 * 代理对象,静态代理
 */
class UserDaoProxy implements UserDao{
    //接收保存目标对象
    private UserDao target;
    public UserDaoProxy(UserDao target){
        this.target=target;
    }

    public void save() {
        System.out.println("开始事务...");
        target.save();//执行目标对象的方法
        System.out.println("提交事务...");
    }
}

/**
 * 测试
 */
public static void main(String[] args) {
    UserDao target = new UserDaoImpl();
    //代理
    UserDao proxy = new UserDaoProxy(target);
    proxy.save();//执行的是代理的方法
}

动态代理

JDK动态代理

目标对象需要实现接口

/**
 * 定义接口
 */
interface UserDao {
    void save();
}

/**
 * 实现接口
 */
class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

/**
 * jdk动态代理
 */
class ProxyFactory {
    //维护一个目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //给目标对象生成代理对象
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("开始事务");
                    //执行目标对象方法
                    Object returnValue = method.invoke(target, args);
                    System.out.println("提交事务");
                    return returnValue;
                }
        );
    }
}

/**
 * 测试
 */
public static void main(String[] args) {
    UserDao target = new UserDaoImpl();
    //代理
    UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
    proxy.save();//执行的是代理的方法
}

Cglib动态代理

Cglib是需要额外依赖jar包的!

Cglib是针对类实现代理,主要对指定的类生成一个子类,覆盖其中的方法,添加额外功能,因为是继承,所以该类方法不能用final来声明.

class UserDao{
    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

/**
 * Cglib动态代理
 */
class ProxyFactory implements MethodInterceptor {
    //维护目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务...");

        //执行目标对象的方法
        Object returnValue = method.invoke(target, args);

        System.out.println("提交事务...");

        return returnValue;
    }
}

/**
 * 测试
 */
public static void main(String[] args) {
    //目标对象
    UserDao target = new UserDao();
    //代理对象
    UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
    proxy.save();
}

AspectJ静态代理

AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。AspectJ则是属于静态代理的代表。

The AspectJ Project | The Eclipse Foundation​www.eclipse.org
0803ca6477973a64fdfd8c13ec2b80d3.png

举个栗子

public class Hello {
    // 定义一个方法,模拟业务逻辑
    public void sayHello() {
        System.out.println("Hello AspectJ!");
    }
}

aspect增强

public aspect TxAspect {
    // 指定执行 Hello.sayHello() 方法时执行下面代码块
    void around():call(void Hello.sayHello()){
        System.out.println("开始事务 ...");
        proceed();
        System.out.println("事务结束 ...");
    }
}

然后使用一个类似增强版的javac的编译命令 -> ajc,这样在编译时候就把业务逻辑增强了。

Spring AOP

事务问题

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" mode="aspectj"/>
  1. mode=

默认为mode=proxy模式,此模式是使用动态代理的方式。

可以改为mode=aspectj模式,此模式改为使用Spring的AspectJ事务方面​​编织受影响的类,修改目标类字节码以应用于任何类型的方法调用。AspectJ编织需要spring-aspects.jar在类路径中进行,并且需要启用加载时编织(或编译时编织)。

例如我在公司遇到的一个事务不生效问题,就是因为mode=aspectj了,但是没有加载编译。本地测试时候,我使用类加载器织入的方式,在VM options中加入-javaagent:/Users/路径/aspectjweaver-1.8.9.jar则事务可以生效(还有其他几种方式,例如在pom.xml文件中加入aspectj-maven-plugin插件配置的编译时织入方式)。或者mode改为默认模式事务也是生效的。

静态织入的好处就是不存在内调用事务失效的问题,而动态代理的方式内调用则需要

//设置expose-proxy属性为true,将代理暴露出来
@EnableAspectJAutoProxy(exposeProxy = true)
AopContext.currentProxy()

2. proxy-target-class=

proxy-target-class参数只有mode=proxy生效,控制为带有@Transactional注释的类创建哪种类型的事务代理。

如果proxy-target-class=true,则会创建基于类的代理,也就是Cglib动态代理。

如果proxy-target-class=false(false为默认属性)或如果省略该属性,那么将创建基于标准JDK接口的代理,也就是JDK动态代理。但是如果目标对象没有接口,则spring会自动使用Cglib。

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations​docs.spring.io
Logo

前往低代码交流专区

更多推荐