Java中的动态代理

假设读者您目前就职于一家软件公司,担任软件工程师角色。客户带着需求来到你们公司,显然客户不会直接和你进行交流,而是去找商务,此时客户会认为商务就代表公司。此时商务就可以看成代理对象,而您,伟大的软件工程师,就可以看成一个真实的对象。

商务这个角色存在的意义就在于商务可以进行谈判。比如客户和商务谈判软件的价格、交付、开发进度等。这些事情都不要我们软件工程师参与。因此,代理的作用就是:在访问真实对象之前或者之后,加入一定的逻辑,根据其他规则来控制是否使用真实对象。

所以,商务和软件工程师之间的关系就是代理和被代理的关系。客户是经过商务去访问软件工程师的,此时客户就是程序中的调用者,商务就是代理对象,软件工程师就是真实对象。我们需要在调用者调用对象之前产生一个代理对象,而这个代理对象需要和真实对象产生代理关系,所以代理必须分为两个步骤:

  • 代理对象和真实对象产生代理关系。
  • 实现代理对象的代理逻辑方法

2、Java中的代理技术介绍

在Java中有很多的代理技术,比如JDK、CGLIB、Javassist、ASM,其中最常用的动态代理技术有两种,一:JDK的动态代理技术,这种代理技术是基于接口的。二:CGLIB代理技术,这种代理技术不需要提供接口,只需要一个非抽象类就可以实现动态代理。

3、JDK的动态代理

定义一个接口

public interface HelloWorld {
    public void sayHelloWorld();
}

接口的实现类

public class HelloWorldImpl implements HelloWorld {
    @Override
    public void sayHelloWorld() {
        System.out.println("hello world");
    }
}

动态代理绑定和代理逻辑的实现

按照之前的分析,需要先建立代理对象和真实对象之间的关系,然后实现代理逻辑。

在JDK的动态代理中,要想实现代理逻辑,必须必须去实现java.lang.reflect.InvocationHandler接口 它里面定义了一个invoke方法,并提供接口数组用于下挂代理对象。代码如下:

public class JDKProxyExample implements InvocationHandler {

    //真实对象
    private Object target = null;

    /**
     * 建立代理对象和真实对象之间的关系,并返回代理对象
     * @param target 真实对象
     * @return 代理对象
     */
    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    /**
     *
     * @param proxy 代理对象
     * @param method 当前调度的方法
     * @param args 方法参数列表
     * @return 代理结果返回
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("进入代理逻辑方法");
        System.out.println("在调度真实对象之前服务");
        Object invoke = method.invoke(target, args);//相当于调度sayHelloworld方法
        System.out.println("在调度真实对象之后服务");
        return invoke;
    }

    @Test
    public void testJdkProxy(){
        JDKProxyExample jdkProxyExample = new JDKProxyExample();
        HelloWorld proxy = (HelloWorld) jdkProxyExample.bind(new HelloWorldImpl());
        proxy.sayHelloWorld();
    }

}

上述代码中,首先通过target来保存真实的对象,然后通过代码

Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);

来生成代理对象,并返回。方法中包含三个参数

  • 第一个是类加载器,我们采用target本身的类加载器。
  • 第二个是把生成的代理对象下挂在那些接口之下,这样写我们就是放在target实现的接口之下。
  • 第三个就是定义实现方法逻辑的代理类,this代表当前对象,它必须实现InvocationHandler接口的invoke方法,它就是代理逻辑方法的现实方法。

nvoke方法实现了代理逻辑,invoke方法的三个参数的含义如下所示:

  • proxy,代理对象,就是bind方法生成的对象
  • method,当前调度的方法。
  • args,调度方法的参数。 当我们使用代理对象调度方法以后,它就会进入到invoke方法里面。
Object invoke = method.invoke(target, args);//相当于调度sayHelloworld方法

这行代码相当于调度真实对象的方法,只是通过反射实现而已。 类比上面的例子,proxy相当于商务对象,target就是软件工程师对象,bind方法就是建立商务对象和软件工程师对象之间的关系。而invoke就是商务逻辑,它将控制对于软件工程师的访问逻辑。

结果

进入代理逻辑方法
在调度真实对象之前服务
hello world
在调度真实对象之后服务

4、cglib动态代理

pom.xml文件

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>6.14.3</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

JDK动态代理必须提供接口,但是在一些不能提供接口的场景中,只能采用其他方式的代理方式,例如cglib动态代理。它的优势在于不需要提供接口,只需要一个非抽象类就可以进行代理。

public class Say {
    public void sayHello(String name){
        System.out.println("Hello: " + name);
    }
}
public class CglibProxyExample implements MethodInterceptor {

    /**
     * 生成cglib代理对象
     * @return
     */
    public Object getProxy(Class cls){
        //CGLIB enhancer增强类对象
        Enhancer enhancer = new Enhancer();
        //设置增强类型
        enhancer.setSuperclass(cls);
        //定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
        enhancer.setCallback(this);
        return enhancer.create();
    }

    /**
     * 代理逻辑方法
     * @param proxy 代理对象
     * @param method 方法
     * @param args 方法参数
     * @param methodProxy 方法代理
     * @return 代理逻辑返回
     * @throws Throwable 异常
     */
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用真实对象之前");
        //cglib对象反射调用真实对象的方法
        Object obj = methodProxy.invokeSuper(proxy, args);
        System.out.println("调用真实对象之后");
        return obj;
    }

    @Test
    public void testCglib(){
        CglibProxyExample proxyExample = new CglibProxyExample();
        Say say = (Say) proxyExample.getProxy(Say.class);
        say.sayHello("minmin");
    }
}

在上面的类中,使用cglib的加强者Enhancer,通过设置超类的方法(setSuperClass),然后通过setCallback方法设置那个类为代理类。其中,参数为this就代表当前对象,这时就要求当前对象必须实现MethodInterceptor方法,并实现intercept方法,然后返回代理对象。

结果

调用真实对象之前
Hello: minmin
调用真实对象之后