JDK动态代理

2020-04-03 14:40:15 浏览数 (1)

JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口才能产生一个对象,也就是说JDK动态代理是对接口的代理。一般要使用JDK动态代理,首先得定义接口,然后再对这个接口的实现类对象进行代理,产生代理对象。SpringAOP就是JDK动态代理的应用之一。

第一步:定义接口

JDK动态代理必须有接口,这里仅仅定义一个及其简单的接口用于示例。

代码语言:javascript复制
package com.lemon.designmode.interfaces;

/**
 * @author lemon
 * @date 2018/2/11 上午10:25
 */
public interface HelloWorld {

    void sayHello();
}

第二步:实现接口

这里是接口的实现类,具体的业务逻辑写在这里。

代码语言:javascript复制
package com.lemon.designmode.bean;

import com.lemon.designmode.interfaces.HelloWorld;

/**
 * @author lemon
 * @date 2018/2/11 上午10:23
 */
public class HelloWorldImpl implements HelloWorld {

    @Override
    public void sayHello() {
        System.out.println("向Java世界问好...");
    }
}

这里是最简单的Java接口和实现类的关系,此时可以开始动态代理了,一般会分为两个步骤:第一是建立代理对象和真实服务对象的代理和被代理关系,第二步是实现代理对象具体方法的逻辑。

第三步:动态代理绑定和代理逻辑实现

JDK动态代理中,要实现代理逻辑类必须娶实现java.lang.reflect.InvocationHandler接口,它内部定义了一个invoke方法,并提供接口数组用于将代理对象下挂到被代理对象所属的接口下,也就是说代理对象和被代理对象都是实现了同一个接口,只不过被代理对象是显式实现(使用了关键字implements),代理对象是运行时实现。

代码语言:javascript复制
package com.lemon.designmode.proxy;

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

/**
 * @author lemon
 * @date 2018/2/11 上午10:27
 */
public class JdkProxyExample implements InvocationHandler {

    private Object target;

    /**
     * 该方法用于将真实对象绑定到代理类,并返回代理对象
     *
     * @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 obj = method.invoke(target, args);
        System.out.println("代理方法调用真实对象之后的逻辑处理");
        return obj;
    }
}

上面的代码实现了两步:第一是将真实服务对象和代理对象进行了绑定;第二步是实现了代理逻辑。 这里通过bind方法将真实服务对象保存到了代理类的成员变量中,并返回了代理对象。

newProxyInstance方法分析: 1)第一个参数是类加载器,这里使用的是target的类加载器 2)第二个参数是传入真实服务对象所在的接口,用于将代理对象下挂到和真实服务对象所在的接口 3)第三个参数是定义实现方法逻辑的代理类,this表示当前类对象,也就是代理类,那么这个代理类就必须实现InvocationHandler接口的invoke方法,它就是代理逻辑的实现方法。

invoke方法分析: 1)第一个参数是代理对象,就是bind方法生成的对象 2)第二个参数是当前调度的方法 3)第三个参数是调度方法的参数。

第四步:测试JDK动态代理

代码语言:javascript复制
package com.lemon.designmode.test;

import com.lemon.designmode.bean.HelloWorldImpl;
import com.lemon.designmode.interfaces.HelloWorld;
import com.lemon.designmode.proxy.JdkProxyExample;

/**
 * @author lemon
 * @date 2018/2/11 上午10:44
 */
public class JdkProxyTest {

    public static void main(String[] args) {
        JdkProxyExample jdkProxyExample = new JdkProxyExample();
        HelloWorld proxy = (HelloWorld) jdkProxyExample.bind(new HelloWorldImpl());
        proxy.sayHello();
    }
}

这里使用代理对象直接调用真实服务对象同名方法,实现了零入侵式代码增强。Spring AOP就是这个原理,在真实服务对象方法加入前置通知、后置通知、环绕通知等。 测试方法运行结果如下:

代码语言:javascript复制
进入代理方法
代理方法调用真实对象之前的逻辑处理
向Java世界问好...
代理方法调用真实对象之后的逻辑处理

0 人点赞