代理模式是java中一个非常经典和常用的设计模式,我们知道我们最为常用的spring框架中的Aop机制,正是基于代理模式来实现的(动态代理),那么代理是什么的,网上有各种各样的详细而生动的解释,其实概括起来就一句话:对目标对象的加强。java与模式中对其的定义为:为其它对象提供一种代理,以控制这个对象的访问。
要学习代理模式,首先需要理解代理模式的三个概念:抽象角色(接口或抽象类,提供公共方法),真实角色(),代理角色(对真实角色的加强)
例子代码:
//抽象角色
public interface ProxyInterface {
public void say();
}
//真实角色
public class Person implements ProxyInterface {
public void say() {
System.out.println("--------开始讲话---------");
}
}
//代理角色
public class Teacher implements ProxyInterface {
private ProxyInterface proxy;
private Teacher() {};
public Teacher(ProxyInterface proxy) {
this.proxy = proxy;
}
public void say() {
// 调用目标对象之前可以做相关操作
System.out.println("-----同学们好-------");
proxy.say();
// 调用目标对象之后可以做相关操作
System.out.println("-----下课-------");
}
}
//测试
public class TestProxy {
/**
* main(这里描述这个方法的使用)
* @param args
* void
* @exception
*/
public static void main(String[] args) {
ProxyInterface proxy = new Teacher(new Person());
proxy.say();
}
}
结果:
-----同学们好-------
--------开始讲话---------
-----下课-------
上面是一个静态代理的简单demo,在编码中使用静态代理,随着业务的增加,你会面临要写一个又一个的代理类的窘境(比如我们要给所有的像操作数据库的功能前加入权限验证或要在操作之后记录下操作日志),这个时候,就是我们使用静态代理的时候了,其实spring的aop正是基于这种动态代理来实现的。
对于动态代理,我们首先要搞明白一点:动态代理其实还是代理,与静态代理所不同的只是,它的代理角色是在程序运行期间有jvm帮我们创建的,这样就减少了我们手动去建造大量的代理类的麻烦。
那么对于这一点java是怎么做到的呢?这就要说到java的反射机制了(关于java反射会在后续的博客中补上)。在jdk1.3之后的java.lang.reflect包中,提供了对动态代理的支持,相关的类有三个:InvocationHandler(接口),Proxy,Method。
每一个动态代理都必须要实现InvocationHandler接口,并且每个代理类都关联到了一个handler,当我们通过代理对象调用一个方法放入时候,这个方法的调用就会被转发为由InvocationHandler接口的invoke()来进行调用。
示例代码:
抽象角色:
public interface TargetInterface {
public void targetMethod1();
public void targetMethod2();
}
//目标角色
public class TargetClass implements TargetInterface{
public void targetMethod1() {
System.out.println("----------方法1执行-----------:");
}
public void targetMethod2(){
System.out.println("----------方法2执行-----------:");
}
}
//调用处理器
public class ProxyHandler implements InvocationHandler{
//真实角色
private Object realObj;
public ProxyHandler(Object realObj )
{
this.realObj = realObj;
}
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
// 在代理真实对象前我们可以添加一些自己的操作
doBefore();
System.out.println("Method:" + method);
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object result = method.invoke(realObj, args);
// 在代理真实对象后我们也可以添加一些自己的操作
doAfter();
return result;
}
private void doBefore(){
System.out.println("----执行之前,检查权限------");
}
private void doAfter(){
System.out.println("----执行之后,记录日志,以备查验--------");
}
}
//测试代码
public class TestProxy {
/**
* main(这里描述这个方法的使用)
* @param args
* void
* @exception
*/
public static void main(String[] args) {
// 我们要代理的真实对象
TargetInterface realSubject = new TargetClass();
// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler handler = new ProxyHandler(realSubject);
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 realSubject.getClass().getClassLoader() ,我们这里使用realSubject这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
Class<?> cls = realSubject.getClass();
TargetInterface subject = (TargetInterface)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), handler);
System.out.println(subject.getClass().getName());
subject.targetMethod1();
subject.targetMethod2();
}
}
执行结果:
com.sun.proxy.$Proxy0
----执行之前,检查权限------
Method:public abstract void com.finet.designpattern.dynaproxy.TargetInterface.targetMethod1()
----------方法1执行-----------:
----执行之后,记录日志,以备查验--------
----执行之前,检查权限------
Method:public abstract void com.finet.designpattern.dynaproxy.TargetInterface.targetMethod2()
----------方法2执行-----------:
----执行之后,记录日志,以备查验--------