设计模式-java实现动态代理
通过 设计模式-java实现代理模式(静态代理)这篇文章,我们了解到了代理模式的基本定义,涉及到的角色和各个角色的基本作用。并通过了一个简单的例子解释了在java中如何使用代理模式(静态代理),首先回顾一下代理模式的特点:
- 使用代理对象将真实对象(被代理对象)包装起来,然后用该代理对象取代真实对象。
- 任何通过对真实对象的调用都要通过代理对象调用,不能直接调用真实对象。
- 代理对象决定是否以及何时将调用转移到真实对象上。
通过代理模式的特点可以看出任何对真实对象的调用都需要通过代理对象调用,不能通过真实对象直接调用。我们从传统的静态代理模式来看,一个真实类都需要一个代理类,假如我们增加一个真实类就需要增加一个代理类。这样将会让系统中的类个数急剧增加,显然是不友好的。动态代理可以让程序能够根据实际需要(真实对象)来动态创建代理对象,让一个代理类能够代理多个真实类。
在java中可以使用反射来达到动态代理,在学习动态代理之前需要具有一定反射的知识,如果不了解java反射可以看下java基础-反射1(类型信息,Class对象简介,Class对象初始化)
java基础-反射2(反射,反射操作对象,Class对象的使用,类型信息的获取)
java基础-反射3(反射,反射创建对象,操作对象属性,调用对象方法)这几篇文章
首先我们来了解几个类:
Proxy类
Proxy类可以创建动态代理类和真实对象(被代理类)。主要有以下方法:
- getProxyClass(ClassLoader loader,Class… interfaces),可以返回一个Class类型的代理类,需要传入类加载器,和类实现的接口数组。注意:被代理类和代理类的类加载器需要一样,被代理类和代理类实现的接口要一样
- newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler)可以返回一个代理类的实例,需要传入类加载器,代理类所实现的接口与被代理类的接口一致,handler是一个实现了InvocationHandler 接口的对象。(这里让代理对象和InvocationHandler 的实例对象关联起来了)
InvocationHandler接口
InvocationHandler 的主要方法时如下:
- invoke(Object proxy, Method method, Object[] args):invoke方法可以处理对代理类对象的方法调用并返回相应的结果,当代理实例中的方法被调用时将自动调用该方法。proxy表示代理类的实例,method表示需要代理的方法,args表示代理方法的参数
InvocationHandler网上解释很多,但是很多都不是很好理解,我的理解是每个代理对象都要关联了一个实现了InvocationHandler接口 的handler(通过Proxy类的newProxyInstance()创建代理对象时关联),通过代理对象调用时,这个接口的方法调用就会转接到该handler的invoke方法进行调用,然后在invoke方法中对真实对象(被代理对象)进行调用
动态代理的实现
我们通过比较常见的一个情况来演示,日志系统,在每调用一个方法时都要写日志。
第一步:创建抽象角色
/**
* Created by IntelliJ IDEA.
* User:hubin
* Description:
* Date:2017/12/13
* Time:15:16
*/
public interface UserServer {
/**
* 登录
* @param name
* @param password
*/
public String login(String name ,String password);
/**
* 注册
* @param name
* @param password
*/
public String registered(String name ,String password);
}
/**
* Created by IntelliJ IDEA.
* User:hubin
* Description:
* Date:2017/12/13
* Time:15:19
*/
public interface DepartmentServer {
/**
* 创建部门
* @param name
* @return
*/
public String createDepart(String name);
}
第二步:创建真实角色(被代理类)
/**
* Created by IntelliJ IDEA.
* User:hubin
* Description:
* Date:2017/12/13
* Time:15:21
*/
public class UserServerimpl implements UserServer {
@Override
public String login(String name, String password) {
System.out.println("用户登录--->账号:"+ name +" 密码:" + password);
return name;
}
@Override
public String registered(String name, String password) {
System.out.println("用户注册--->账号:"+ name +" 密码:" + password);
return name;
}
}
/**
* Created by IntelliJ IDEA.
* User:hubin
* Description:
* Date:2017/12/13
* Time:15:24
*/
public class DepartmentServerimpl implements DepartmentServer {
@Override
public String createDepart(String name) {
System.out.println("部门注册--->部门名称:"+ name );
return name;
}
}
第三部:创建代理角色
(1)创建实现了InvocationHandler接口的类
/**
* Created by IntelliJ IDEA.
* User:hubin
* Description:
* Date:2017/12/13
* Time:15:25
*/
public class LogInvocationHandler implements InvocationHandler {
/**
* 被代理对象
*/
private Object target;
public void proxyObject(Object target){
this.target = target;
}
/**
* 每个动态代理类都要关联一个实现了InvocationHandler类的实例,
* 通过代理对象调方法时就会转接到与之关联的handler对象的的invoke方法进行调用(就是下面这个方法)
* @param proxy 代理类实例
* @param method 代理的方法
* @param args 方法传入的函数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用前日志");
Object result = method.invoke(target,args);
System.out.println("方法调用后返回值:" + result);
System.out.println("方法调用后日志");
System.out.println();
return result;
}
}
(2)创建动态代理类并且关联InvocationHandler 的实例
/**
* Created by IntelliJ IDEA.
* User:hubin
* Description:
* Date:2017/12/13
* Time:15:58
*/
public class LogDynamicProxy {
/**
*
* @param target 被代理对象
* @return 代理对象
*/
public static Object getLogProxy(Object target){
LogInvocationHandler handler = new LogInvocationHandler();
handler.proxyObject(target);
/**
* 创建代理对象
* 注意:
* 代理对象 proxy 和 被代理对象 target 需要相同的类加载器(target.getClass().getClassLoader())
* 代理对象 proxy 和 被代理对象 target 需要相同的实现接口 (target.getClass().getInterfaces())
* 代理对象 需要关联 实现了 InvocationHandler的实例 如下面的(handle)
*/
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);
return proxy;
}
}
第四步:测试
/**
* Created by IntelliJ IDEA.
* User:hubin
* Description:
* Date:2017/12/13
* Time:16:08
*/
public class TestDynamicProxy {
public static void main(String agrs[]){
//创建真实UserServer对象(被代理对象)
UserServer userServer = new UserServerimpl();
//得到userServer的代理类
UserServer userServerProxy = (UserServer)LogDynamicProxy.getLogProxy(userServer);
userServerProxy.login("TestLogin","TestLogin");
userServerProxy.registered("TestRegist","TestRegist");
//创建真实DepartmentServer对象(被代理对象)
DepartmentServer departmentServer = new DepartmentServerimpl();
//得到DepartmentServer的代理类
DepartmentServer departmentServerProxy = (DepartmentServer)LogDynamicProxy.getLogProxy(departmentServer);
departmentServerProxy.createDepart("TestDepartment");
}
}
运行结果为:
从我们测试来看,我们给 有UserServer和DepartmentServer两个真实对象(被代理类),但是我们只通过一个动态代理类LogDynamicProxy 动态的得到了他们的代理对象,给他们的每个方法都加上了日志。不需要额外的添加代理类,如果我们现在要加一个CompanyServer,并且也需要给他加上日志,就可以直接通过动态代理类LogDynamicProxy得到 CompanyServer的代理对象,这样就加上了日志。
java动态代理作为代理模式的一种扩展形式,在框架设计中运用广泛,比如AOP的设计,其实这个日志例子就已经是一个日志切面的例子了。