一、为什么要使用代理模式
为了为某些类的方法附加上额外且通用的功能,同时不改变原来的代码结构。eg:为操作数据库的代码加上事务(使用spring的aop)
二、代理模式的角色
1、抽象角色:是下面的真实角色和代理角色的公有方法的集合。一般为接口,也可以是抽象方法
2、真实角色:是拥有核心功能的类
3、代理角色:拥有真实角色的引用,同时附加了额外的功能。
eg:租房子这件事里面,有中介,房主,和租房者,这里的租房者就是客户端,中介是代理角色,房主就是真实角色。租房者要租房,但是他是通过中介这个代理去完成租房
(核心功能)这件事的,但是同时他能够帮客户与房主联系,讨价还价等等(附加功能)。但是最终还是房主把房子租给了客户(完成核心功能的最终还是真实角色)。
三、三者之间的关系
1、真实角色和代理角色要实现抽象角色定义的公有方法
2、代理角色拥有真实角色的引用
四、静态代理
首先看实现吧
1、抽象角色
package com.chenrui.proxy;
/**
* 代理模式中的公有接口(起到统一的作用)
* */
public interface Subject {
public void request();
}
2、真实角色
</pre><pre name="code" class="java">package com.chenrui.proxy;
/**
* 代理模式里面的真实角色
* */
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("this is the realSubject");
}
}
3、代理角色
package com.chenrui.proxy;
/**
* 代理模式里面的代理角色
* */
public class ProxySubject implements Subject {
private RealSubject rs;//代理角色所拥有的真实角色的引用,用于访问真实角色
@Override
public void request() {
if(rs == null) {
rs = new RealSubject();
}
rs.request();
add();
}
public void add() {
System.out.println("the extra function");
}
}
4、测试程序
package com.chenrui.proxy;
public class Main {
public static void main(String[] args) {
Subject s = new ProxySubject();
s.request();
}
}
最终的输出结果:
this is the realSubject
the extra function
静态代理的流程:
1、使用抽象角色的引用 获得 代理角色的对象(在里面生成了真实角色的对象)
2、调用方法 (这里调用的是代理角色的同名方法,在实现了真实角色的方法的同时,添加了额外功能,上面的是add)
静态代理的优点:编写简单,理解也简单吧。
静态代理的缺点:每一个类如果要添加额外功能的时候,都要对应一个代理类,在多个类的方法的额外功能相同的情况下,将会产生大量重复的代码。
为了解决这个问题,所以有了动态代理。
五、动态代理
首先也先上代码
1、抽象角色(这个跟静态代理的一致)
package com.chenrui.DynamicProxy;
/**
* 动态代理的接口类(抽象角色)
* */
public interface Hello {
void sayHello(String to);
void print(String p);
}
2、真实角色(这个也一致)
package com.chenrui.DynamicProxy;
/**
* 真实角色
* */
public class HelloImpl implements Hello {
public void sayHello(String to) {
System.out.println("Say hello to " + to);
}
public void print(String s) {
System.out.println("print : " + s);
}
}
3、处理类(这个就跟静态代理的不一致了,先把代码看完,下面有详细讲解)
package com.chenrui.DynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 相比静态代理的优点,这个是可以作为多个类的处理类的,比如添加事务的处理类。
* */
public class LogHandler implements InvocationHandler {
private Object dele;
public LogHandler(Object obj) {
this.dele = obj;
}
/**
* 代理是在这里为原本的类添加新的功能
* 这些能够用来进行用来为某些操作之前进行检查,特别是使用面向切面编程的时候。
* */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doBefore();
Object result = method.invoke(dele, args);//这个是真正执行方法的地方
after();
return result;
}
private void doBefore() {
System.out.println("before....");
}
private void after() {
System.out.println("after....");
}
}
4、测试程序
package com.chenrui.DynamicProxy;
import java.lang.reflect.Proxy;
/**
* 测试程序
*
* */
public class ProxyTest {
public static void main(String[] args) {
HelloImpl impl = new HelloImpl();
LogHandler handler = new LogHandler(impl); //这里指定了代理类所拥有的真实角色
//这里把handler与新生成的代理类相关联 ,这里的动态代理的类是由jvM生成的,他所拥有的东西
//(1)一个handler(拥有一个真实角色),handler同时还有一个invoke方法并且我们自己能够给他添加相应的功能
//(2)生成的对象实现了了impl所实现的接口,也就是公共的接口
Hello hello = (Hello) Proxy.newProxyInstance(impl.getClass().getClassLoader(), impl.getClass().getInterfaces(), handler);
//这里无论访问哪个方法,都是会把请求转发到handler.invoke
hello.print("All the test"); //在这一步,java将会调用proxy内部的LogHandler的invoke方法,当调用方法的时候,调用方法的对象就是上面的impl,方法的参数就是这里的参数,java内部提供了将这里的参数转换成object的方法
hello.sayHello("Denny");
}
}
5、动态代理所用到的类和接口
(1)Proxy类:使用其中的newProxyInstance方法生成一个代理对象,这个对象是jvm内部帮助我们生成的,这个生成的就是代理角色.
方法的具体定义如下:
<pre name="code" class="java">public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
loader:是真实角色的类加载器;
interfaces:是真实角色所实现的所有公有接口的数组,这个相当于是让代理角色实现了公有接口
InvocationHandler:是一个处理器类,里面拥有真实角色的实现,额外功能的附加以及核心方法的调用都是在里面实现的
(2)Interface InvocationHandler: 上面的处理器都要求要实现这个接口。里面有一个invoke方法
6、处理器(实现了InvocationHandler的类)的作用
(1)实现额外功能
(2)决定额外功能与和核心功能的调用顺序。
(3)当代理类调用某个方法的时候,就是调用这里的invoke();然后里面的method.invoke(obj,args)方法实际上就是利用反射调用真实角色的对应方法。
7、抽象角色
代理类:是动态生成的类(测试程序中的hello)通过newProxyInstance方法,在生成它的时候必须先提供接口给他,然后该类就宣布他实现了这些接口,但是实际上,他 不会完成实际的工作,他对接口里面的方法的调用,实际上是对真实角色方法的调用。
8、动态体现在哪里?
体现在代理类是在程序运行阶段才确定的,而不是在程序的编译阶段。
六、总结