使用 JDK 动态代理

如何使用 JDK 动态代理

使用 JDK 的动态代理,需要完成以下两步:
- 编写服务接口及其实现
- 编写代理类,代理类中提供绑定方法和代理方法

接下来我们通过代码一步步完成一个动态代理的示例。
编写接口

package com.shawearn.proxy.service;

/**
 * Created by Shawearn on 2017/8/1.
 */
public interface MyService {
    void sayHello(String name);
}

编写接口实现类

package com.shawearn.proxy.service.impl;

import com.shawearn.proxy.service.MyService;

/**
 * Created by Shawearn on 2017/8/1.
 */
public class MyServiceImpl implements MyService {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name + " !");
    }
}

编写代理类,代理类需要实现 java.lang.reflect.InvocationHandler 接口

package com.shawearn.proxy;

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

/**
 * 代理类;
 * <p>
 * Created by Shawearn on 2017/8/1.
 */
public class MyServiceProxy implements InvocationHandler {

    private Object target;

    /**
     * 构造方法,绑定对象;
     *
     * @param target
     */
    public MyServiceProxy(Object target) {
        super();
        this.target = target;
    }

    /**
     * 获取代理对象;
     *
     * @return
     */
    public Object getProxyInstance() {
        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("########## JDK 动态代理示例 ##########");
        System.out.println("开始调用被代理对象方法:" + method.getName());
        // 反射调用被代理对象中的方法;
        Object result = method.invoke(target, args);
        System.out.println("已经调用被代理对象方法:" + method.getName());
        return result;
    }
}

编写测试方法

package com.shawearn.proxy;

import com.shawearn.proxy.service.MyService;
import com.shawearn.proxy.service.impl.MyServiceImpl;

/**
 * Created by Shawearn on 2017/8/1.
 */
public class Main {
    public static void main(String[] args) {
        MyServiceProxy myServiceProxy = new MyServiceProxy(new MyServiceImpl());
        MyService proxy = (MyService) myServiceProxy.getProxyInstance();
        proxy.sayHello("Shawearn");
    }
}

运行结果:

########## JDK 动态代理示例 ##########
开始调用被代理对象方法:sayHello
Hello Shawearn !
已经调用被代理对象方法:sayHello

代码说明

上面的代码中 MyService 和 MyServiceImpl 只是我们的接口及接口实现类,关键在 MyServiceProxy 类中,下面我们以 main 主方法为起点,一步步描述我们是如何实现代理的。
首先我们需要新建 MyServiceProxy 对象,新建对象时需要传入参数告知 MyServiceProxy 代理哪一个实例对象:

MyServiceProxy myServiceProxy = new MyServiceProxy(new MyServiceImpl());

调用 getProxyInstance() 方法获取到代理对象:

MyService proxy = (MyService) myServiceProxy.getProxyInstance();

因为我们绑定了 MyServiceImpl 的实例对象,而 MyServiceImpl 实现了 MyService 接口,所以我们可以直接把 getProxyInstance() 方法返回的结果强转为 MyService 类型。
此时对程序打断点,我们能看到 proxy 对象其实并不是真正的 MyServiceImpl 类型,而是如下图所示的一个代理对象

proxy对象

当我们调用 proxy 对象的 sayHello() 方法时

proxy.sayHello("Shawearn");

程序并不会直接进入 MyServiceImpl 中的 sayHello() 方法,而是先进入 MyServiceProxy 中的 invoke() 方法,invoke() 是我们实现 java.lang.reflect.InvocationHandler 接口时所必须实现的方法,该方法有三个参数:
- proxy 被调用方法所在的代理对象

可以通过下面断点的截图看到 proxy 信息

invoke方法中的proxy参数

  • method 代理对象被调用的方法所对应的接口方法的方法实例(原文:the Method instance corresponding to the interface method invoked on the proxy instance.)

可以通过下面断点的截图看到 method 信息
invoke方法中的method参数
- args 被调用方法的参数值

可以通过下面断点截图帮助理解
invoke方法中的args参数
当程序进入到 MyServiceProxy 的 invoke() 方法后,通过反射调用到被代理对象中对应的方法,在本例中即 MyServiceImpl 中的 sayHello() 方法。

// 反射调用被代理对象中的方法;
Object result = method.invoke(target, args);

至此,我们便已经成功使用了 JDK 的动态代理,至于 JDK 底层如何实现动态代理,我们后面再做讨论。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章