Java教程

Java 动态代理

本文主要是介绍Java 动态代理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

参考:https://www.bilibili.com/video/BV1Kb411W75N

代理设计模式的原理

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

静态代理

package com.a.java;

/*
静态代理
特点:代理类和被代理类在编译期间被确定。
 */
interface ClothFactory {
    void produceCloth();
}

// 代理类
class ProxyClothFactory implements ClothFactory {
    private ClothFactory factory;  // 用被代理类对象进行实例化

    public ProxyClothFactory(ClothFactory factory) {
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工厂做一些准备工作");

        factory.produceCloth();

        System.out.println("代理工厂做一些后续的收尾工作");
    }
}

// 被代理类
class NikeClothFactory implements ClothFactory {

    @Override
    public void produceCloth() {
        System.out.println("Nike工厂生产一批运动服");
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
        // 创建被代理类的对象
        NikeClothFactory nike = new NikeClothFactory();
        // 创建代理类的对象
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);

        proxyClothFactory.produceCloth();
    }
}

上面的代码首先声明了一个接口ClothFactory,里面只有一个无返回值的方法produceCloth。然后声明了一个代理类ProxyClothFactory和一个被代理类NikeClothFactory,它们均实现了ClothFactory接口。其中,被代理类NikeClothFactoryproduceCloth方法只输出一句话:“Nike工厂生产一批运动服”。代理类ProxyClothFactory声明了一个ClothFactory类型的私有变量factory,并通过有参的构造方法对factory进行实例化,这个factory就是用被代理类对象进行实例化的。之后,代理类ProxyClothFactoryproduceCloth()中调用了factoryproduceCloth(),也就是被代理类的produceCloth()

main方法中先创建被代理类的对象,然后将被代理类的对象作为参数创建代理类的对象,最后调用代理类的produceCloth(),由于代理类的produceCloth()中也调用了被代理类的produceCloth(),所以被代理类的produceCloth()也被调用了。

上面的代码就是一个静态代理,它的代理类和被代理类都是写死的。

动态代理

实现动态代理要解决2个问题:

  1. 如何根据加载到内存中的被代理类动态地创建一个代理类及其对象?
  2. 当通过代理类的对象调用方法a时,如何动态地去调用被代理类中的同名方法a?

第一个问题用Proxy.newProxyInstance方法解决,第二个问题通过实现InvocationHandler接口中的invoke方法实现。下面是介绍:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
Params: loader – 定义代理类的类加载器(与被代理类相同)
    	interfaces – 代理类要实现的接口列表(与被代理类相同)
    	h – 将方法调用分派到的调用处理程序
Returns: 返回一个代理类的对象

上面的第三个参数InvocationHandler是一个接口:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

里面声明了一个invoke方法,有三个参数:

proxy – 调用该方法的代理实例

method – 代理类实现的接口中声明的方法

args – method的参数

返回值:执行method后的返回值

所以在动态代理的代码中,我们要自己写一个类来实现InvocationHandler 接口。

package com.a.java;

/*
动态代理
 */

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

interface Human {
    String getBelief();

    void eat(String food);
}

// 被代理类
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}

/*
实现动态代理要解决的问题:
1. 如何根据加载到内存中的被代理类动态地创建一个代理类及其对象?
2. 当通过代理类的对象调用方法a时,如何动态地去调用被代理类中的同名方法a?
 */

class ProxyFactory {
    // 通过调用此方法,返回一个代理类的对象,解决问题1
    public static Object getProxyInstance(Object obj) {  // obj: 被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();

        handler.bind(obj);

        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
}

class MyInvocationHandler implements InvocationHandler {

    private Object obj;  // 需要使用被代理类的对象进行赋值

    public void bind(Object obj) {
        this.obj = obj;
    }

    // 当通过代理类的对象调用方法a时,就会自动地调用下面的invoke()
    // 将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // method: 代理类对象调用的方法,此方法也作为被代理类对象要调用的方法
        // obj: 被代理类的对象
        Object returnValue = method.invoke(obj, args);
        // 代理类对象调用的方法,也即被代理类对象要调用的方法的返回值就作为当前类中的invoke()的返回值
        return returnValue;
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        // proxyInstance: 代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        // 当通过代理类对象调用方法时,会自动调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("四川麻辣烫");

        System.out.println("******************************************");

        NikeClothFactory nikeClothFactory = new NikeClothFactory();

        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);

        proxyClothFactory.produceCloth();
    }
}

上面的代码首先声明一个Human接口,然后被代理类SuperMan和ProxyFactory类实现该接口,ProxyFactory中的getProxyInstance方法会动态地返回一个代理类的对象,这解决了问题1。具体实现是通过Proxy.newProxyInstance方法,这是一个系统内置类的方法,有3个参数,第一个参数是与被代理类相同的ClassLoader,第二个参数是被代理类实现的接口,第三个参数是InvocationHandler类型,是一个接口。因此我们还要自己写一个类来实现InvocationHandler接口,就是后面写的MyInvocationHandler类。MyInvocationHandler类里面实现了invoke方法,invoke方法的第二个参数method是代理类对象调用的方法,此方法也作为被代理类对象要调用的方法,第三个参数是method的参数,有了方法和参数还需要有对象,要建立起被代理类的对象与MyInvocationHandler类的联系,因此声明了一个obj属性并用bind方法将被代理类的对象赋值给obj。然后在invoke方法里执行obj的method方法,并将结果返回。这样就解决了问题2,完成了动态代理的实现。

main方法中先实例化被代理类SuperMan,然后使用ProxyFactory.getProxyInstance(superMan)创建代理类的对象,之后当通过代理类对象调用getBelief和eat方法时,会自动调用被代理类中同名的方法。后面又实例化了NikeClothFactory类,然后再通过ProxyFactory.getProxyInstance(nikeClothFactory)创建一个代理类的对象,又可以执行NikeClothFactory类的produceCloth方法了。这就体现了动态代理的动态性。

这篇关于Java 动态代理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!