Java动态代理采用反射的机制,在程序运行时动态生成代理类。我们可以采用Java动态代理机制来对被代理类的一些方法实现增强的行为。Spring的APO也是基于动态代理来实现的。
实现动态代理有两种方法,第一种是基于JDK的Proxy类,一种是基于CGLIB。JDK的动态代理只能是针对于接口代理,如果需要对类进行动态代理只能使用CGLIB了。本篇博客之讨论基于JDK的动态代理。
首先,我们需要一个接口,代理类和真实类都要实现这个接口(或者真实类和代理类同时实现多个相同的接口)。比如,我们可以定义如下的接口:
public interface ISubject {
public String sayHello();
public String waterFlower();
}
接着,定义一个类,实现这个接口
public class RealSubject implements ISubject {
@Override
public String sayHello() {
PrintUtils.printlnString("打招呼。");
return "做一个有礼貌的好学生";
}
@Override
public String waterFlower() {
PrintUtils.printlnString("我在浇花。");
return "做一个喜欢花花草草的有情趣的人";
}
}
现在,我们定义一个代理类,需要实现InvocationHandler接口。
bind方法生成一个代理对象,因为Proxy.newProxyInstance的第二个参数使用的是真实类的接口realObject.getClass().getInterfaces(),所以代理对象可以调用真实类的接口方法。当代理对象调用真实类接口方法的时候就会调用invoke方法。
public class DynamicProxy implements InvocationHandler {
//需要代理的真实对象
private Object realObject;
public Object bind(Object realObject) {
//给代理的真实对象赋值
this.realObject = realObject;
/**
* 通过Proxy.newProxyInstance创建代理对象,需要三个参数
* 1 realObject.getClass().getClassLoader() 类加载路径
* 2 realObject.getClass().getInterfaces() 真实类的接口
* 3 this 将代理对象关联到InvocationHandler
*
* 通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,
* 它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在
* 运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后
* 一个数字表示对象的标号。
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
realObject.getClass().getInterfaces(), this);
}
/**
* 在代理对象上处理一个方法调用,返回处理结果
*
* @param proxy 动态代理类的对象
* @param method 被拦截的方法
* @param args 被拦截的方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
PrintUtils.printlnString("=========我是JDK动态代理===========");
PrintUtils.printlnString("method的名字是:" + method.getName());
Object obj = method.invoke(realObject, args);
PrintUtils.printlnString(obj.toString());
PrintUtils.printlnString("完成方法的调用");
return obj;
}
}
public class TestProxy {
@Test
public void testJDKProxy() {
DynamicProxy proxyHandler = new DynamicProxy();
//创建代理对象
ISubject proxy = (ISubject) proxyHandler.bind(new RealSubject());
proxy.sayHello();
proxy.waterFlower();
}
}
执行打印的日志:
=========我是JDK动态代理===========
method的名字是:sayHello
打招呼。
做一个有礼貌的好学生
完成方法的调用
=========我是JDK动态代理===========
method的名字是:waterFlower
我在浇花。
做一个喜欢花花草草的有情趣的人
完成方法的调用
到这里,我们用一个例子完成了基于JDK的动态代理的入门学习。