Spring AOP中的动态代理技术


AOP
面向切面编程(Aspect-oriented programming,AOP)是spring 中另外一种编程思想,作用是可以在不修改原业务代码的情况下,在类调用关系之间新增加一些操作,优点,将新增的代码和原有业务代码解耦,方便对新增代码的后续维护。代理技术是对AOP思想实现的一种方式。

问题引入

在这里插入图片描述

根据代理类创建的时机分为静态代理和动态代理,静态代理在程序运行之前由用户提前定义好,动态代理在程序运行过程中进行创建和加载。

静态代理

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;
     
     //两个用户之间转账
    @Override
    public void transferMoney(Account from, Account to,double amount) throws Exception {
        //1 查询账户from
        Account accountFrom = accountDao.getAccountByName(from.getName());
        //2 查询账户to
        Account accountTo = accountDao.getAccountByName(to.getName());
        //3 from 账户扣钱 amount
        accountFrom.setMoney(accountFrom.getMoney()-amount);
        accountDao.updateAccount(accountFrom);
        int i =1/0;//出现异常,导致转账失败,并且已经扣款的账户from金额丢失
        //4 to 账户加钱
        accountTo.setMoney(accountTo.getMoney()+amount);
        accountDao.updateAccount(accountTo);
    }
}

AccountServiceImpl的代理类
public class AccountServiceImplProxy implements AccountService {
    private AccountService accountService;
    private TransactionManager tx;
    // 转账操作增加事务,保证整个操作
    public AccountServiceImplProxy (AccountService accountService){
				this.accountService = accountService();
		}
    @Override
    public void transferMoney(Account from, Account to, double amount) throws Exception {
        try{
            // 1 开启事务
            tx.beginTransaction();
            // 2 执行事务
            accountService.transferMoney(from,to);
            // 3 提交事务
            tx.commitTransaction();
        }catch (Exception e){
            // 4回滚事务
            tx.rollbackTransaction();
        }finally {
            // 5 关闭事务
            tx.closeTransaction();
        }

    }
}

通过AccountService代理调用转账业务
AccountService  accountService  = new AccountServiceImplProxy(new AccountServiceImpl());
accountService.transfer(from,to);

两种动态代理技术

实现动态代理的方式有两种,基于类的动态代理和基于接口的动态代理。

基于接口的动态代理

要求被代理类必须实现了某一个接口,有JDK提供的Proxy类实现,通过newProxyInstance方法动态创建一个代理类实现,下面是该方法定义原型。

/**
* loader- 用来加载创加载新的代理类,由于创建的代理类和被代理实现了相同的接口,所有两个类使用同一个加载器
* interfaces- 被代理类实现的接口,新的代理类也会实现这些接口
* InvocationHandler- 用户实现该接口,在里面的invoke方法实现对修饰方法的代码的添加。
*/
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

实现代码

public class AccountProxy implements InvocationHandler {
    private Object proxyedBean;
    private TransactionManager transactionManager;

    //重写该方法,在该方法里面实现新增一些操作
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"被成功代理");
        Object returnValue = null;
        try{
            // 1 开启事务
            transactionManager.beginTransaction();
            // 2 执行事务
            returnValue = method.invoke(proxyedBean,args);//调用旧的业务逻辑
            // 3 提交事务
            transactionManager.commitTransaction();
        }catch (Exception e){
            // 5回滚事务
            transactionManager.rollbackTransaction();
        }finally {
            transactionManager.closeTransaction();
            // 4 关闭任务
        }
        return  returnValue;//动态新创建AccountServiceImpl的代理对象
    }
}

使用AccountProxy代理对象调用AccountServiceImpl中转账操作

AccountProxy accountProxy = new AccountProxy();
AccountService accountService = new accountServiceImpl();
accountProxy.setProxyedBean(accountService );
AccountService accountService1 = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(),  accountProxy);
accountService1.transferMoney(from,to,amount);
 

不足:要求代理类必须实现一个接口。
思考1:为什么被代理类必须要实现一个接口?
JDKProxy生成的动态代理类为 **public final class $Proxy0 extends Proxy implements XXX(被代理类实现的接口) **,通过观察代理类的实现方式可知,动态代理类已经默认继承Proxy类,Java不支持多继承,只能通过实现的方式实现动态代理。至于为什么一定要继承或者实现被代理类,个人理解:是为了从用户的角度,保证被代理类的父类引用可以指向新生成的代理类,不用对后面的业务代码进行修改,这也是动态代理可以支持AOP的原因。
思考2:同一个被代理类中的方法a调用同一个代理类中的方法b,为什么不会走动态代理类?

public void methodA(){
 ...
}
public void methodB(){
   methodA();//等价于this.methodA();
}

在这里插入图片描述
外部类调用methodB() 调用对象是新生成动态代理类,当动态内部类使用的代理类调用methodB()方法,此时当前对象变成了被代理类,在methodB方法内部调用methodB,此时当前对象是被代理类,所以不会走动态代理类。

基于类的动态代理

第三方提供的动态代理库cglib中的Enhancer类也能实现动态代理,代理类不必实现接口,只要不是被final修饰的最终类进行。

public class TransactionProxy implements MethodInterceptor {
    //被代理对象
    private Object proxyedBean;
    private TransactionManager tx;

    /**
     *
     * @param o 被代理对象引用
     * @param method 被代理的方法
     * @param objects 被代理方法的参数
     * @param methodProxy 当前执行方法的代理对象????
     * @return 动态新创建AccountServiceImpl的代理对象
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object returnValue = null;
        try{
            // 1 开启事务
            tx.beginTransaction();
            // 2 执行事务
            returnValue = method.invoke(proxyedBean,objects);
            // 3 提交事务
            tx.commitTransaction();
        }catch (Exception e){
            // 4 回滚事务
            tx.rollbackTransaction();
        }finally {
            tx.closeTransaction();
            // 5 关闭任务
        }
        return  returnValue;
    }
}

使用AccountProxy代理对象调用AccountServiceImpl中转账操作
AccountProxy accountProxy = new AccountProxy();
AccountService accountService = new accountServiceImpl();
accountProxy.setProxyedBean(accountService );
AccountService accountService1 = (AccountService) Enhancer.create(AccountService.class,transactionProxy);
accountService1.transferMoney(from,to,amount);

总结

静态代理实现简单,缺点是当需要有很多类需要增强同样内容是,静待代理需要为每个类编写代理类,而动态代理只需编写一个代理类,具体代理的是哪个类,通过反射机制可以在运行过程中自动识别。动态代理应用广泛,structs拦截器,spring中AOP,mybatis中的sql查询。Spring里会自动在JDK的代理和CGLIB之间切换,同时我们也可以强制Spring使用CGLIB。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值