Java动态代理的实现

1.为什么要使用动态代理

我们先创建一个接口

public interface ICalc {
    int add(int a, int b);
    int sub(int a, int b);
}

创建接口的实现类,并在方法里加入一些日志

public class ICalcImpl implements ICalc {
    @Override
    public int add(int a, int b) {
        System.out.println("这是一个加法运算");
        int r = a + b;
        System.out.println("add...");
        System.out.println("加法运算结束,结果是" + r);
        return r;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("这是一个减法运算");
        int r = a - b;
        System.out.println("sub....");
        System.out.println("加法运算结束,结果是" + r);
        return r;
    }
}

创建App类,调用这些方法

public class App {
    public static void main(String[] args) {
        ICalc iCalc = new ICalcImpl();
        iCalc.add(2, 3);
        iCalc.sub(3, 2);
    }
}

在这里插入图片描述
看起来没什么问题,程序正常执行,而且日志也打印出来了。
问题是:如果需求更改,需要打印其它内容的日志怎么办呢?以上代码硬编码,就显得缺乏灵活性,此时就需要动态代理出厂。

2.动态代理的实现

将实现类中的日志信息全部删除

public class ICalcImpl implements ICalc {
    @Override
    public int add(int a, int b) {
        int r = a + b;
        System.out.println("add...");
        return r;
    }

    @Override
    public int sub(int a, int b) {
        int r = a - b;
        System.out.println("sub....");
        return r;
    }
}

在App类中实现动态代理

public class App {
    public static void main(String[] args) {
        //要被动态代理的目标对象
        ICalc target = new ICalcImpl();
        /**
         * 第一个参数:ClassLoader 通过本类的字节码对象创建类加载器
         * 在创建一个类时,如执行new Person(),会创建一个类加载器将Person.class加载到JVM中
         * 而使用动态代理创建对象时,虽然是不常规的创建对象方式,但是毕竟也是在创建对象,所以同样也需要类加载器
         * 所以需要我们把一个类加载器传入进去
         */
        ClassLoader classLoader = App.class.getClassLoader();
        /**
         * 第二个参数:确定字节码实现的接口及字节码中的方法
         * 动态代理机制会在程序运行时,动态生成一个class文件,这个class文件并不是凭空生成的,而是根据第二个参数
         * 中的字节码生成,target.getClass()得到ICalcImpl的字节码,getInterfaces()获取ICalcImpl对象实现的
         * 所有接口的的字节码,所以要用字节码数组,此处ICalcImpl只实现了ICalc接口,自动生成的class文件会自动实现
         * 第二个参数所传入的接口。如下,第二个参数传入ICalc.class,自动生成的class就是下面这个样子,由于class文件
         * 可读性差,此处用对应的java文件的形式表示。
         * class Proxy0 implements ICalc{
         *     int add(int a,intb){
         *
         *     }
         *     int sub(int a,int b){
         *
         *     }
         * }
         */
        Class[] interfaces = target.getClass().getInterfaces();

        /**
         * 第三个参数:确定字节码中方法的方法体
         * 第三个参数传入后会在方法体中加入h.invoke()方法,在执行动态字节码中的h.invoke()方法时,会执行MyHandler
         * 中的invoke()方法
         *  class Proxy0 implements ICalc{
         *       int add(int a,intb){
         *          return h.invoke();
         *        }
         *        int sub(int a,int b){
         *          return h.invoke();
         *        }
         *  }
         *
         */
        InvocationHandler h = new MyHandler(target);
        //在缓存中生成一个实现ICalc接口的代理对象Proxy0
        ICalc proxy = (ICalc) Proxy.newProxyInstance(classLoader, interfaces, h);
        //调用缓存中代理对象中的add方法,执行其中的h.invoke()方法,此时调用MyHandler类中的invoke()方法
        //代理对象proxy作为MyHandler类中的invoke()方法的第一个参数,通过反射add()方法生成Method对象作为
        //第二个参数,两个参数(1,2)生成Object数组作为第三个参数
        proxy.add(1,2);
    }
}
class MyHandler implements InvocationHandler {
    private Object target;
	//在实例化时传入目标对象
    public MyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前置通知
        System.out.println(method.getName() + "方法开始,参数是" + Arrays.toString(args));
        //通过反射机制,调用动态代理proxy对象所调用的方法,add()或者sub(),方法参数是args中的内容,如果有执行结果,就返回执行结果
        Object result = method.invoke(target, args);
        //后置通知
        System.out.println(method.getName() + "执行完毕,结果是" + result);
        return result;
    }
}

执行结果:
在这里插入图片描述
可见,我们并未在方法体写任何日志,但是通过动态代理在方法加入日志

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值