代理模式是一种很重要很有用的设计模式。被应用到很多著名的框架中,其中Spring Aop的底层就是通过JDK动态代理和CGLib动态代理实现的。其中,JDK动态代理是其默认实现。动态代理的作用就是在不修改原目标类的前提下,对目标类方法进行增强。比如横切一些逻辑:事务管理,日志记录,检验等。J相对于静态代理,JDK动态代理避免了重复编写代理类的缺点。只需要简单的指定一组接口及目标类就可以获得代理对象。Java在JDK1.3以后支持JDK动态代理。
相比于CGLib动态代理,JDK动态代理的特点就是:被代理的目标类必须有接口,生成的代理类实际上是实现了此接口。JDK动态代理是通过Java反射机制实现的。
JDK提供的动态代理就是通过:java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现的。InvocationHandler只提供了一个方法:invoke()方法,其具体的参数意义后面代码中进行分析。其方法实现了代理的逻辑,相当于Spring中的增强:Advice。代理逻辑类需要实现此接口。另外,通过Proxy类的newProxyInstance()方法来获得代理类,其具体参数的意义也在后边代码中进行解释。
下面通过代码,来看一下如何实现JDK的动态代理:
被代理的接口:
package com.blog.jdkproxy;
/**
* @Description: 模拟用户dao
* @Author: Jingzeng Wang
* @Date: Created in 20:51 2017/6/26.
*/
public interface IUserDao {
/**
* 模拟添加用户
* @return 添加成功的条数
*/
int add();
}
接口的一个简单实现类:
package com.blog.jdkproxy;
/**
* @Description: 用户操作实现类
* @Author: Jingzeng Wang
* @Date: Created in 20:53 2017/6/26.
*/
public class UserDao implements IUserDao{
/**
* 模拟添加用户
*
* @return 添加成功的条数
*/
public int add() {
System.out.println("添加用户成功!!!");
return 0;
}
}
实现代理逻辑:
package com.blog.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Description: 代理实现
* @Author: Jingzeng Wang
* @Date: Created in 20:56 2017/6/26.
*/
public class MyInvocationHandler implements InvocationHandler {
// 被代理的目标类
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
/***
*
* @param proxy 生成的代理对象
* @param method 被代理的目标类的方法
* @param args 被代理的目标类方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置增强逻辑
System.out.println("---------------------before 事务开始--------------------");
//调用目标类方法
Object obj = method.invoke(target, args);
//后置增强逻辑
System.out.println("---------------------after 事务结束---------------------");
return obj;
}
/**
* 生成代理对象
*
* @return 返回代理对象(调用时转型为目标对象的接口)
*/
public Object getProxy() {
//参数意义:1. 当前类加载器 2. 目标类的class对象 3. 目标类的所有接口 4. 实现InvocationHandler接口的类
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
}
}
测试类:
package com.blog.jdkproxy;
/**
* @Description: JDK动态代理测试类
* @Author: Jingzeng Wang
* @Date: Created in 21:04 2017/6/26.
*/
public class Test {
public static void main(String[] args) {
IUserDao userDao = new UserDao();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(userDao);
IUserDao proxy = (IUserDao) myInvocationHandler.getProxy();
proxy.add();
/**
* 相同接口的代理对象,生成的是同一个代理类class com.sun.proxy.$Proxy0 $Proxy0类名
* 是使用了缓存 WeakCache 类实现
* If the proxy class defined by the given loader implementing the given interfaces exists, this will simply return the cached copy;
* otherwise, it will create the proxy class via the ProxyClassFactory
*/
IUserDao proxy2 = (IUserDao) myInvocationHandler.getProxy();
IUserDao userDao3 = new UserDao();
MyInvocationHandler myInvocationHandler3 = new MyInvocationHandler(userDao3);
IUserDao proxy3 = (IUserDao) myInvocationHandler3.getProxy();
System.out.println(proxy.getClass());
System.out.println(proxy2.getClass());
System.out.println(proxy3.getClass());
}
}
结果为:
---------------------before 事务开始--------------------
添加用户成功!!!
---------------------after 事务结束---------------------
class com.sun.proxy.$Proxy0
class com.sun.proxy.$Proxy0
class com.sun.proxy.$Proxy0
从结果上,可以看到,我们可以看到,已经对目标类的add方法完成了代理操作。同时,JDK帮我们生成的代理类名字为:class com.sun.proxy.$Proxy0。同时可以看到,如果是实现的是相同接口的代理类,生成的是同一个代理类。这是因为,JDK为代理类做了缓存操作。
通过上边的代码,就可以实现一个简单的JDK的动态代理。但是,JDK动态代理到底是怎么完成的,JDK的Proxy类到底为我们做了什么,是怎么生成的代理类,怎样完成的方法的调用。这些问题需要查看源码进行分析,将在下一篇博客中进行详细的分析。