代理设计模式
给某一个对象提供代理对象,由代理对象控制具体对象的使用。
什么叫代理?
在生活中,代理无处不在,如各种代理商。
如:生产电脑的厂家不会直接把电脑卖给零售客户,而是通过代理来完成销售。客户也不用因为买电脑而跑去厂家。
这就是代理过程
代理类:
主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
代理类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。
代理类与委托类都实现了同一个接口。
代理中涉及的角色:
1,抽象主题角色:声明真实角色和代理对象的共同接口
2,真实角色:是我们最终要引用的对象
3,代理角色:代理对象内部加了一些自己的处理方法,最后还是要去调用真实角色的处理方法。
简单代理结构图:
为什么要使用代理?
1,授权机制不同,不用级别的用户对同一对象拥有不同的访问权限。如论坛中,注册用户和游客的权限差别。
2,不让客户端直接操作到某个对象,但又必须和那个对象有所互动。
如:
在网络中,在我们访问某个远端的服务器上的某个对象的时候,如果直接操作这个对象,网络速度原因可能比较慢,那我们就可以先用Proxy来代替那个对象。
如果我们在加载示一个很大的图片的时候,可以让其在后台加载,代理中显示等待信息。
下面看实例:
package proxy;
//公共接口,产生须要让其子类实现的方法
interface SellInterface
{
public void sell();
}
//真实角色,实现SellInterface
class SellFactory implements SellInterface
{
public void sell() {
System.out.println("真实角色在处理问题");
}
}
//代理类
class MyProxy implements SellInterface
{
SellFactory sf; //代理类须要调用真实角色的处理方法
public void sell()
{
/*
这样调用,可以让这个代理类在这里先处理一些问题,
比如判断什么的,如果不满足,就不用去调用具体实现方法了,这样可以提高效率。
在实际应用中,如我们在访问网站时,也是先访问的本地服务器,如果有自己所须要的,
在本地就可以完成访问,没必要每次都去总部访问资源,这样很占空间。
再比如:我们买正品戴尔电脑,不用去总部,在代理处就可以了。如果代理处满足不了,
在去总部。
*/
if(test()){ //满足条件
sf = new SellFactory();
sf.sell();
}
else
throw new RuntimeException();
}
protected boolean test()
{
return true;
}
}
//客户端
public class CommonProxy {
public static void main(String [] args)
{
//在代理处办事
SellInterface mp = new MyProxy();
mp.sell();
}
}
代理模式的--动态代理
JVM在执行时,运用反射机制动态创建一个类,如果这个类是代理类,那么这样的形式就是动态代理类。
动态代理和普通代理的区别?
普通代理类:在程序运行前,代理类的class文件已经存了
动态代理类:程序运行时 运用反射机制 动态创建而成
在上面的代理中,我们让代理类实现了接口SellInterface,这样使得代码,通用性很低。
在java中提供了其它方法,使得代理类通用性提高了。
通过类Proxy 和 接口InvocationHandler 来实现对代理模式的支持。
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
代理创建过程
1,首先通过Proxy类的方法来创建代理对象,须要通过来主题角色建立
2,代理类须要实现InvocationHandler 接口
动态代理结构图
动态代理的建立:两种方法
由Proxy类 和 接口 InvocationHandler 来共同完成。
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
InvocationHandler 是代理实例的调用处理程序实现的接口.
如:建立List list = new ArrayList 集合的代理类
a , 通过获取构造方法来建立
// 1,获取动态代理类的Class
Class clazz = Proxy.getProxyClass(
List.class.getClassLoader(), // 目标类所实现的接口的类加载器
new Class[]{List.class} //目标类所实现的接口
);
// 2, 实现InvocationHandler接口,通过这个去调用目标类。
InvocationHandler ih = new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//可以加入功能
Object re = method.invoke(list, args); //调用所要代理的对象的方法
//可以加入功能
return re;
}
};
// 3,获取代理类的构造函数
Constructor constructor = clazz.getConstructor(InvocationHandler.class);
// 4,创建代理对象
List myproxy = (List)constructor.newInstance(ih);
b , 直接通过newProxyInstance 来建立
List myproxy2 = (List)Proxy.newProxyInstance(
List.class.getClassLoader(), // 目标类所实现的接口的类加载器
new Class[]{List.class}, //目标类所实现的接口
new InvocationHandler(){ // 实现接口,去调用目标类
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
//可以加入功能
Object re = method.invoke(list, args); //调用所要代理的对象的方法
//可以加入功能
return re;
}
}
);
示例代码:
ackage proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collection;
interface SellInterface
{
public void sell();
}
class SellFactory implements SellInterface
{
public void sell() {
System.out.println("真实角色在处理问题");
}
}
//代理类1,尽量面向对向,使代理类更通用。
class MyProxy implements InvocationHandler
{
static Object obj;
MyProxy(Object obj) // obj:传入须要代理的类
{
this.obj = obj;
}
//通过Proxy类的newProxyInstance方法来返回代理对象
public static Object factory(Object o)
{
Class clazz = o.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new MyProxy(o));
}
/*
* 实现InvocationHander接口的invoke
* proxy:代理实例
* method:对应于在代理实例上调用的接口方法的 Method 实例
* args [] :传入代理实例上方法的参数值的对象数组.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("函数调用前被拦截了"+method);
// System.out.println(proxy.getClass().getName());
//代理调用真实角色处理方法
Object mo = method.invoke(obj, args);
System.out.println("函数调用后进行处理"+method);
return mo;
}
}
//客户端
public class DynamicProxy {
public static void main(String [] args) throws Exception
{
SellInterface mp = (SellInterface)MyProxy.factory(new SellFactory());
mp.sell();
// test();
}
public static void test() throws Exception
{
//获取代理类Proxy的字节码
System.out.println("-------------constructors list-------------");
Class clazzProxy = Proxy.getProxyClass(Object.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy.getName());
//获取代理类Proxy的构造方法
Constructor [] constructors = clazzProxy.getConstructors();
//获取Proxy的方法
System.out.println("-------------method list----------------");
Method [] methods = clazzProxy.getDeclaredMethods();
for(Method m : methods)
{
//以()形式输出方法里的参数
StringBuilder sb = new StringBuilder();
sb.append(m.getName());
sb.append("(");
//列出方法的参数
Class [] clazzParam = m.getParameterTypes();
for(Class c : clazzParam)
{
sb.append(c.getName()+",");
}
if(clazzParam!=null && clazzParam.length!=0)
sb.deleteCharAt(sb.length()-1);
sb.append(")");
System.out.println(sb);
}
//创建代理对象的方法
System.out.println("--------------create instance -------------------");
//创建构造器
Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);
//方法一:
class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法一");
// Object retVal = method.invoke(obj, args);
return null;
}
}
Object proxy1 = constructor.newInstance(new MyInvocationHandler());
System.out.println(proxy1);
//方法二
Object proxy2 = constructor.newInstance(new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法二");
// Object retVal = method.invoke(obj, args);
return null;
}
});
System.out.println(proxy2);
//方法三:
Object proxy3 = Proxy.newProxyInstance(
Object.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始切面。。。"+method);
// Object retVal = method.invoke(obj, args); //动态调用真实角色处理方法
System.out.println("结束切面。。。"+method);
return null;
}
}
);
System.out.println(proxy3);
}
}
用代理实现AOP拦截机制的例子
可以拦截我们指定的函数,并在拦截前后根据需要进行处理.
除了拦截,代理模式还常用于资源加载,当我们要加载的资源很大时,我们可以让真实主题角色在后台加载资源,让代理主题角色负责处理前台的等待提示信息.
看下面代码示例:
package proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collection;
interface AopInterface
{
public void start(Object obj); //切面--调用前的处理
public void end(Object obj); //切面--调用后处理
}
class AopInterfaceImp implements AopInterface //对象切面的具体操作
{
public void start(Object obj) {
System.out.println("调用前被拦截了,做一些其它的事");
}
public void end(Object obj) {
System.out.println("调用后被拦截,去处理一些其它事");
}
}
//代理类
class MyProxy4 implements InvocationHandler
{
private AopInterface aop; //切入时调用
private Object obj;
private String methodName = null; //要拦截的方法名字
MyProxy4() {} // obj:传入须要代理的类
//通过Proxy类的newProxyInstance方法来返回代理对象
public Object factory(Object o)
{
this.obj = o;
Class clazz = o.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在这里还应该做一些判断,如:aop没有初始化。
Object mo;
//如果该方法是要拦截的
if(methodName != null && method.toString().indexOf(methodName)!=-1) //这里为什么是用indexOf呢?
//因为method.toString() 返回的是
// public abstract void proxy.TestInterface.add() 形式。
{
aop.start(obj);
//代理调用真实角色处理方法
mo = method.invoke(obj, args);
aop.end(obj);
}
else
{
mo = method.invoke(obj, args);
}
return mo;
}
public AopInterface getAop()
{ return aop; }
public void setAop(AopInterface aop)
{ this.aop = aop; }
public String setMethodName(String methodName)
{
return this.methodName = methodName;
}
}
//用于测试的
interface TestInterface {
public void add();
public void dele();
}
class ImpTest implements TestInterface{
public void add() {
System.out.println("........... add()");
}
public void dele(){
System.out.println("............ acc()");
}
}
//客户端
public class DynamicProxy2 {
public static void main(String [] args) throws Exception
{
MyProxy4 mp = new MyProxy4(); //建立代理对象
mp.setAop(new AopInterfaceImp()); //给代理设置所要代理的类
mp.setMethodName("add"); //设置须要被拦截的方法名
//通过代理建立测试类对象 ,因为要让测试类在执行时,先去代理处
TestInterface ai = (TestInterface)mp.factory(new ImpTest());
ai.add(); //执行要拦截的add()方法
ai.dele(); //执行没有被拦截的dele()方法
}
}