Spring AOP的实现原理之动态代理

本文参考自:https://www.cnblogs.com/liantdev/p/10132680.html

一、JDK动态代理

  JDK动态代理与静态代理一样,目标类需要实现一个代理接口,它的开发步骤如下:

  • 1.定义一个java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法
  • 2.将InvocationHandler对象作为参数传入java.lang.reflect.Proxy的newProxyInstance方法中
  • 3.通过调用java.lang.reflect.Proxy的newProxyInstance方法获得动态代理对象
  • 4.通过代理对象调用目标方法

示例:

实现计算器功能的接口:

public interface ArithmeticDAO {
    //+ 加
    public void plus(int i, int j);
    //- 减
    public void sub(int i, int j);
   //* 乘
    public void mul(int i, int j);
   //div 除
    public void div(int i, int j);
   //求余(取模)
    public void mod(int i, int j);
}

实现计算器的实现类:

public class ArithmeticDAOImpl implements ArithmeticDAO {
    @Override
    public void plus(int i, int j) {
        System.out.println(i+"+"+j+"="+(i+j));
    }
    @Override
    public void sub(int i, int j) {
        System.out.println(i+"-"+j+"="+(i-j));
    }
    @Override
    public void mul(int i, int j) {
        System.out.println(i+"*"+j+"="+(i*j));
    }
    @Override
    public void div(int i, int j) {
        System.out.println(i+"/"+j+"="+(i/j));
    }
    @Override
    public void mod(int i, int j) {
        System.out.println(i+"%"+j+"="+(i%j));
    }
}

代理实现类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/*使用JDK动态代理实现在方法前后加入日志信息
  1. 用匿名内部类的方式实现InvocationHandler 接口
*/
public class ArithmeticLoggingProxy{
    private ArithmeticDAO target;  //需要被代理的对象

    public ArithmeticLoggingProxy(ArithmeticDAO target) {
        this.target = target;
    }

    public ArithmeticDAO getLoggingProxy(){
        ArithmeticDAO proxy = null;  //代理对象
       //指定代理对象由哪一个类加载器加载
        ClassLoader loader = target.getClass().getClassLoader();
        //需要代理对象实现的数组,里面保存的是代理对象的类型,即其中有哪些方法
        Class[] interfaces = new Class[]{ArithmeticDAO.class};
        /*
         调用代理对象中的方法时,应该执行的代码。InvocationHandler 调用处理器
        Object proxy:代理对象
        Method method:正在被调用的方法
        Object[] args:调用方法时,传入的参数
        */
        InvocationHandler invoke = new InvocationHandler () {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();
                System.out.println(name+" start……"+ Arrays.asList(args));
                Object result = method.invoke(target, args);
                System.out.println(name+" end……");
                return result;
            }
        };
        proxy = (ArithmeticDAO) Proxy.newProxyInstance(target.getClass().getClassLoader(),interfaces ,invoke);
        return proxy;
    }
}

//==============================================================
/*2. 在类上实现InvocationHandler 接口*/
public class ArithmeticLoggingProxy implements InvocationHandler{
    private ArithmeticDAO target;  //需要被代理的对象

    public ArithmeticLoggingProxy(ArithmeticDAO target) {
        this.target = target;
    }

    /*
       调用代理对象中的方法时,应该执行的代码。InvocationHandler 调用处理器
       Object proxy:代理对象
       Method method:正在被调用的方法
       Object[] args:调用方法时,传入的参数
       */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        String name = method.getName();
        System.out.println(name+" start……"+ Arrays.asList(args));
        try{
            result = method.invoke(target, args);
        }catch (Exception e){
            System.out.println("异常信息:"+e.getMessage());
        }finally {
            System.out.println(name+" end……" );
        }
        return result;
    }
}

测试:

//用匿名内部类的方式实现InvocationHandler 接口的测试类
  @Test
    public void test01(){
          //需要被代理的对象
        ArithmeticDAOImpl target = new ArithmeticDAOImpl();
        ArithmeticDAO proxy = new ArithmeticLoggingProxy2(target).getLoggingProxy();
        proxy.plus(10,20);
        proxy.sub(10,5);
    }
//=============================================================
//在类上实现InvocationHandler 接口的测试类
@Test
    public void test01(){
        //需要被代理的对象
        ArithmeticDAO target = new ArithmeticDAOImpl();
        //获取处理器
        InvocationHandler handler = new ArithmeticLoggingProxy(target);
        //代理对象
        ArithmeticDAO proxy = (ArithmeticDAO)Proxy.newProxyInstance(Main.class.getClassLoader(), target.getClass().getInterfaces(), handler);
        roxy.plus(10,20);
        proxy.sub(10,5);
}

运行结果:
在这里插入图片描述

二、CgLib动态代理

  CgLib动态代理的原理是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理它的开发步骤

  • 1.定义一个org.springframework.cglib.proxy.MethodInterceptor接口的实现类,重写intercept方法
  • 2.获取org.springframework.cglib.proxy.Enhancer类的对象
  • 3.分别调用Enhancer对象的setSuperclass和setCallback方法,使用create方法获取代理对象
  • 4.通过代理对象调用目标方法

示例:

/* CGLib动态代理方式实现 */
public class ArithmeticInteceptor implements MethodInterceptor {

   /*
     Object o:代理对象
     Method method:正在调用的方法
     Object[] args:目标方法的形参
     MethodProxy proxyMethod:代理方法
   */
   @Override
   public Object intercept(Object proxy, Method method, Object[] args, MethodProxy proxyMethod) throws Throwable {
      Object result = null;
       String name = method.getName();
       System.out.println(name+" start……"+ Arrays.asList(args));
       try{
          result = proxyMethod.invokeSuper(proxy,args);
          // result = method.invoke(proxy,args);
       }catch (Exception e){
           System.out.println("异常信息:"+e.getMessage());
       }finally {
           System.out.println(name+" end……");
       }
       return null;
   }
}

测试类代码:

 @Test
    public void test01(){
      /* 获取Enhance对象
        Enhancer enhancer = new Enhancer();
        设置目标类
        enhancer.setSuperclass(ArithmeticDAOImpl.class);
       调用回调方法
        enhancer.setCallback(new ArithmeticInteceptor());
         ArithmeticDAOImplproxy = (ArithmeticDAOImpl)enhancer.create();
       */
       
       //============== 相当于以下一句=============
        //获取代理对象
        Arithmetic proxy = (ArithmeticDAOImpl)Enhancer.create(ArithmeticDAOIMpl.class, new ArithmeticInteceptor());
        proxy.plus(10, 20);
           proxy.sub(10,5);
    }

运行结果:
在这里插入图片描述

三、JDK动态代理与CgLib动态代理的区别

  • JDK动态代理针对实现了接口的类的接口方法进行代理
  • CgLib动态代理是基于继承来实现代理,所以它无法对final类、private方法和static方法实现代理

Spring AOP中的代理使用的默认策略是:

  • 如果目标对象实现了接口,则默认采用JDK动态代理
  • 如果目标对象没有实现接口,则采用CgLib进行动态代理
  • 如果目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值