Java静态代理和基于JDK的动态代理

1. 代理是什么

可以参考我的这篇笔记设计模式学习——代理模式

我的理解是这样的,首先要有代理对象和被代理对象(目标对象),代理对象通过代理这个行为,给目标对象加 buff 。

对于目标对象来说,无需更改自己,也就是程序员不需要去修改目标对象的代码,而是通过用代理对象来间接操控目标对象的方法,实现一些功能上的扩展。

2. 静态代理

2.1 实现例子

举个例子,假设我们有个加法器接口 Calculator 和一个实现他的先进加法器类 ヾ(•ω•) CalculatorImpl,它的功能就是输出两个数相加之和

// 加法器接口
public interface Calculator {
    public void add(int a, int b);
}
// 加法器实现类
public class CalculatorImpl implements Calculator {
    @Override
    public void add(int a, int b) {
        System.out.println("加法器结果:"+(a+b));
    }
}

这时候上头给了你一个任务要求每次加法运算前打印一下被使用加法器的Class,但是不允许改变加法器的代码,思来想去我们就打算用一个代理CalculatorProxy来实现,这个CalculatorProxy也必须继承Calculator

// 加法器代理类
public class CalculatorProxy implements Calculator {
    // 目标对象
    private Calculator target;
    // 构造函数
    public CalculatorProxy(Calculator target) {
        this.target = target;
    }

    @Override
    public void add(int a, int b) {
        // 实现的增强功能
        System.out.println(target.getClass());
        // 调用加法器的方法
        target.add(1,1);
    }
}

现在我们只需要把业务中调用加法器的地方全部换成用代理类去实现

public class Test {
    public static void main(String[] args) {
        testStaticProxy();
    }

    public static void testStaticProxy() {
        Calculator calculatorImpl = new CalculatorImpl();
        CalculatorProxy calculatorProxy = new CalculatorProxy(calculatorImpl);
      	// 用代理类来实现
        calculatorProxy.add(1,1);
    }
}
// 输出结果
// class com.test.zhao.proxy.staticProxy.impl.CalculatorImpl
// 加法器结果:2 

2.2 静态代理的缺点

  • 要为每一个类都编写一个代理类,实现和目标类相同的接口
  • 需要手动创建代理对象,把目标对象塞进去
  • 高耦合度

举个例子:

随着公司业务急速发展,我们公司陆续实现了许多高级的算术器,它们分别实现CalculatorMultiplierDivider接口,现在我们有了CalculatorImpl1CalculatorImpl2CalculatorImpl3等先进的加法器。

// 乘法器接口
public interface Multiplier {
    public void multi(int a, int b);
}
// 除法器接口
public interface Divider {
    public void divide(int a, int b);
}
// 除法器实现类
public class DividerImpl implements Divider {
    @Override
    public void divide(int a, int b) {
        System.out.println("除法器结果:"+(a/b));
    }
}
// 乘法器实现类
public class MultiplierImpl implements Multiplier {
    @Override
    public void multi(int a, int b) {
        System.out.println("除法器结果:"+(a*b));
    }
}

你的任务就是给每个运算器都实现之前代理的功能,每次运算前打印一下运算器的Class,这不轻轻松松,照葫芦画瓢,把代理类写好

// 除法器代理类
public class DividerProxy implements Divider {
    private final Divider target;

    public DividerProxy(Divider target) {
        this.target = target;
    }

    @Override
    public void divide(int a, int b) {
        System.out.println(target.getClass());
        target.divide(1,1);
    }
}
// 乘法器代理类
public class MultiplierProxy implements Multiplier {
    private final Multiplier target;

    public MultiplierProxy(Multiplier target) {
        this.target = target;
    }

    @Override
    public void multi(int a, int b) {
        System.out.println(target.getClass());
        target.multi(1,1);
    }
}

跑一下测试,搞定

public class Test {
    public static void main(String[] args) {
        testStaticProxy();
    }

    public static void testStaticProxy() {
        Calculator calculatorImpl = new CalculatorImpl();
        CalculatorProxy calculatorProxy = new CalculatorProxy(calculatorImpl);
        calculatorProxy.add(1,1);

        Multiplier multiplierImpl = new MultiplierImpl();
        MultiplierProxy multiplierProxy = new MultiplierProxy(multiplierImpl);
        multiplierProxy.multi(1,1);

        Divider dividerImpl = new DividerImpl();
        DividerProxy dividerProxy = new DividerProxy(dividerImpl);
        dividerProxy.divide(1,1);

    }
}
// 输出结果
// class com.test.zhao.proxy.staticProxy.impl.CalculatorImpl
// 加法器结果:2
//
// class com.test.zhao.proxy.staticProxy.impl.MultiplierImpl
// 乘法器结果:1
//
// class com.test.zhao.proxy.staticProxy.impl.DividerImpl
// 除法器结果:1

看起来还不错,直到有一天,我们还要给xx器, xx器,xx器 ······ 都实现这样的功能。

静态代理的关系图大概如下(不严谨画了一下,本人不会UML类图/(ㄒoㄒ)/~~)

3. 动态代理

3.1 从静态代理到动态代理的思路

再讲动态代理前,让我们思考一下如何解决静态代理的问题。

我们之前的代理思路都是基于实现类的,也就是我们必须要为每个目标对象单独实现一个代理类,根据这个代理类的构造函数来实现代理实例。

既然已经有接口了,为什么还要多此一举实现代理类呢,就不能直接通过接口生成代理实例吗。思路如下图。

让我们思考一下以下几个问题

  • 我们为什么需要实现代理类?

    因为我们要通过代理类的构造函数来创建代理实例

  • 代理类和接口有什么关系?

    代理类实现了接口,接口包含了代理类需要的信息,但是接口没有代理类的构造函数

  • 通过接口有没有办法自动生成对应代理类?

    可以,通过反射拿到接口信息,通过拷贝接口中的类信息到一个新的Class对象中,这个Class对象带有构造器,利用这个构造器可以创建代理实例 。Jdk提供了getProxyClass 这个方法,是通过把接口信息拷贝到一个新的Class对象上,返回一个Class对象,这个Class对象有接口没有的构造函数,通过这个构造函数我们可以创建了代理实例。

3.2 基于JDK动态代理实现

  • 总而言之,动态代理和静态代理的区别在于代理类是动态生成的,不是我们直接写的。

  • 动态代理的实现方式有很多,大致可以分为两大类:基于接口的动态代理和基于类的动态代理。

    • 基于接口-- JDK 动态代理
    • 基于类-- cglib
    • Java 字节码实现 :Javassist

基于JDK的动态代理实现思路如下

  1. 通过getProxyClass获取代理Class对象
  2. 获取代理对象的有参构造器,这个构造函数需要传入一个实现调用处理程序实现的InvocationHandler接口
  3. 通过构造器.newInstance方法传入实现的InvocationHandler接口得到代理实例
public void test() throws Exception {
    // 1. 生成代理 Class 对象
    Class calculatorProxyClazz = Proxy.getProxyClass(Calculator.class.getClassLoader(), Calculator.class);

    // 2. 获得有参构造器 $Proxy0(InvocationHandler h)
    Constructor constructor = calculatorProxyClazz.getConstructor(InvocationHandler.class);

    // 3.反射创建代理实例
    // Class.newInstance() 调用的是无参构造函数
    // Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。这里用它
    Calculator calculatorProxyImpl = (Calculator) constructor.newInstance(new InvocationHandler() {
        /**
         * 每次调用代理对象的方法,最终都会调用invoke方法
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //手动new 一个目标对象
            Calculator calculatorImpl1 = new CalculatorImpl();
            //反射执行目标对象的方法
            method.invoke(calculatorImpl1, args);
            return null;
        }
    });
    //4.成功实现代理
    calculatorProxyImpl.add(1,1);
}

通过jdk 提供的newProxyInstance方法可以简化上述代码,该方法接收三个参数(代理对象类加载器,代理对象接口,代理实例调用处理程序实现的接口)

public static void test() throws Exception{
    Calculator calculator = new CalculatorImpl();
    Calculator calculatorProxyImpl = (Calculator)Proxy.newProxyInstance(calculator.getClass().getClassLoader(),
            calculator.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 需要实现的功能
                    System.out.println(calculator.getClass());
                    method.invoke(calculator, args);
                    return null;
                }
            });
    calculatorProxyImpl.add(1,1);
}

然后分装成一个接收代理对象返回代理实例的方法

public  Object getProxy(Object target) {
    Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 需要实现的功能
                    System.out.println(target.getClass());
                    method.invoke(target, args);
                    return null;
                }
            });
    return proxy;
}

测试

public static void main(String[] args) throws Exception {
    CalculatorImpl calculatorImpl = new CalculatorImpl();
    DividerImpl dividerImpl = new DividerImpl();
    MultiplierImpl multiplierImpl = new MultiplierImpl();

    Calculator proxy1 = (Calculator) getProxy(calculatorImpl);
    Divider proxy2 = (Divider) getProxy(dividerImpl);
    Multiplier proxy3 = (Multiplier) getProxy(multiplierImpl);

    proxy1.add(1,1);
    proxy2.divide(1,1);
    proxy3.multi(1,2);
}
// 输出结果
// class com.test.zhao.proxy.dynProxy.impl.CalculatorImpl
// 加法器1结果:2
// class com.test.zhao.proxy.dynProxy.impl.DividerImpl
// 除法器结果:1
// class com.test.zhao.proxy.dynProxy.impl.MultiplierImpl
// 乘法器结果:2

3.3 最终通用代码

public class ProxyInvocationHandler implements InvocationHandler {

    private Object target;

    public void setTarget(Object target){
        this.target = target;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args);
        return result;
    }
}
posted @ 2022-04-13 21:26  油虾条  阅读(34)  评论(0编辑  收藏  举报