如何使用 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 对象的 sayHello() 方法时
proxy.sayHello("Shawearn");
程序并不会直接进入 MyServiceImpl 中的 sayHello() 方法,而是先进入 MyServiceProxy 中的 invoke() 方法,invoke() 是我们实现 java.lang.reflect.InvocationHandler 接口时所必须实现的方法,该方法有三个参数:
- proxy 被调用方法所在的代理对象
可以通过下面断点的截图看到 proxy 信息
- method 代理对象被调用的方法所对应的接口方法的方法实例(原文:the Method instance corresponding to the interface method invoked on the proxy instance.)
可以通过下面断点的截图看到 method 信息
- args 被调用方法的参数值
可以通过下面断点截图帮助理解
当程序进入到 MyServiceProxy 的 invoke() 方法后,通过反射调用到被代理对象中对应的方法,在本例中即 MyServiceImpl 中的 sayHello() 方法。
// 反射调用被代理对象中的方法;
Object result = method.invoke(target, args);
至此,我们便已经成功使用了 JDK 的动态代理,至于 JDK 底层如何实现动态代理,我们后面再做讨论。