代理模式

代理模式:借用百度百科的定义{

  抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

}

简单来说就是,有个人要做某件事,让另一个人代替他去做(比如一个人要去追求一个女生,要送情书,但是害羞,就叫好哥们去送。。。-_-||当然是选择原谅她)

我们用代码实现:

  有个美丽的姑娘:

package com.lin.proxy;

public class Girl {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

  有个送情书的汉子:(将送情书,礼物的行为定义为接口,追求者实现接口)

package com.lin.proxy;

public interface GiveGift {
	public void sendFlower();
	public void sendLoveLetter();
}
package com.lin.proxy;

public class Boy implements GiveGift {
	
	private Girl mm;
	
	public Boy(Girl mm){
	  this.mm = mm;
	}
	
	@Override
	public void sendFlower() {
		// TODO 送花
		System.out.println(mm.getName()+"送你一朵花");
	}

	@Override
	public void sendLoveLetter() {
		// TODO 送情书
		System.out.println(mm.getName()+"送你一封信");
	}

}

 

这个时候,由于种种原因,boy没有自己出面,让别人代替(这时候是代替者与girl见面,girl看不到boy),如下所示:

代替者也实现GiveGift接口,但是礼物是boy提供的,所以,代替者只能调用boy的方法:

package com.lin.proxy;

public class ProxyBoy implements GiveGift {
	
	private Boy boy;
	
	public ProxyBoy(Boy boy){
		this.boy = boy;
	}
	
	@Override
	public void sendFlower() {
		// TODO 代替送花
		boy.sendFlower();
	}

	@Override
	public void sendLoveLetter() {
		// TODO 代替送情书
		boy.sendLoveLetter();
	}

	public Boy getBoy() {
		return boy;
	}

	public void setBoy(Boy boy) {
		this.boy = boy;
	}
}

 

那么,以下情况就会发生:

package com.lin.proxy;

public class Test {

	public static void main(String[] args) {
		// TODO 原谅的颜色在飘
		Girl mm = new Girl();
		mm.setName("小红");
		Boy boy = new Boy(mm);
		ProxyBoy proxyBoy = new ProxyBoy(boy);
		proxyBoy.sendFlower();
		proxyBoy.sendLoveLetter();
	}

}

 这便是最简单形式的代理模式,我们可以发现如果代替者想记录boy送了哪些东西,要记日志,那么他就可以再自己的方法内做操作,不会影响boy。(扩充:如果没有inteface,可以使用cglib实现代理)

 

复杂版(动态代理):

现在假设这个代理者开展了一个代替送东西的业务(快递),他要为任何人送任何东西,那么因为每个人要送的东西不一样,送的对象也不一样,所以代理者必须实现,根据目标的不同进行动态调整:

参考上面,代替者是实现被代理对象的接口来进行代理,那么,如果这个步骤能够变成根据接口,生成代码并编译使用,那么就能动态了。(java 的api已经为我们提供了动态代理的类 Proxy)。

 JDK的API提供的使用例子

InvocationHandler handler = new MyInvocationHandler(...);
     Class proxyClass = Proxy.getProxyClass(
         Foo.class.getClassLoader(), new Class[] { Foo.class });
     Foo f = (Foo) proxyClass.
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });

 

我们模仿一个试试(反射是个好东西):

我们需要一个动态代理类MyProxy,需要一个拥有接口的被代理的对象(没有接口的对象想实现代理使用CGLib,继承实现),,需要一个InvocationHandler,用于生成实现对应功能的代理类

先实现InvocationHandler:

package com.lin.proxy;

import java.lang.reflect.Method;

public interface InvocationHandler {
	void invoke(Object obj,Method method,Object[] args);
}

 假设代理者需要在送物品前后记录数据:

package com.lin.proxy;

import java.lang.reflect.Method;

public class RecodeHandler implements InvocationHandler {
	private Object target;
	
	public RecodeHandler(Object target) {
		this.target = target;
	}

	@Override
	public void invoke(Object obj,Method method,Object[] args) {
		long start = System.currentTimeMillis();
		System.out.println("Start Time"+start);
		System.out.println(obj.getClass().getName());
		//记录数据·········
		try {
			//送出物品
			method.invoke(target, args);
		}  catch (Exception e) {
			e.printStackTrace();
		}
		//送出后进行记录········
		long end = System.currentTimeMillis();
		System.out.println("Moving time:"+(end-start));
	}

}

 Myproxy:

package com.lin.proxy;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class MyProxy {
    
    public static Object newProxyInstance(Class interfaces,Object[] args,InvocationHandler handler) throws Exception {
        //拼凑出代理类的源代码文件
        String methodStr = "";
        String rt = "\r\n";
        Method[] methods = interfaces.getMethods();
        
        //根据interface获取所有方法(模仿jdk 使用handler调用)
        for(Method m : methods) {
            methodStr +="    @Override"+ rt +
                    "    public void "+m.getName()+"() {"+ rt +
                    "        try{"+ rt +
                    "            java.lang.reflect.Method md = "+interfaces.getName()+".class.getMethod(\""+m.getName()+"\");"+ rt +
                    "            handler.invoke(this,md,args);"+ rt +
                    "        }catch(Exception e){e.printStackTrace();}"+
                    
                    "    }";
        }
        //源代码文件字符串拼接
        String src = 
                "package com.lin.proxy;"+ rt +
                "public class MySendProxy implements "+interfaces.getName()+"{" + rt +
                "    com.lin.proxy.InvocationHandler handler;" + rt +
                "    Object[] args;" + rt +
                "    public MySendProxy(com.lin.proxy.InvocationHandler handler,Object[] args) {" + rt +
                "        this.handler = handler;" + rt +
                "        this.args = args;" + rt +
                "    }" + rt +
                    methodStr + rt +
                "}";
        //源代码保存路径(看需求而定)
        String fileName = "d:/src/com/lin/proxy/MySendProxy.java";
        
        //将数据写入文件
        File f = new File(fileName);
        if(!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }
        FileWriter fw = new FileWriter(f);
        
        fw.write(src);
        fw.flush();
        fw.close();
        
        //编译刚才生成的代理类的文件
        
        //获取系统的java编译器(JDK提供)
        JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
        
        //编译
        StandardJavaFileManager javaFileManager = complier.getStandardFileManager(null, null, null);
        //fileName = "d:/src/com/lin/proxy/MySendProxy.java";
        Iterable units = javaFileManager.getJavaFileObjects(fileName);
        CompilationTask t = complier.getTask(null, javaFileManager, null, null, null, units);
        t.call();
        javaFileManager.close();
        
        
        
        //将class加载入内存并且创建一个实例
        URL[] urls = new URL[]{new URL("file:/"+"d:/src/")};
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("com.lin.proxy.MySendProxy");
        
        Constructor constructor = c.getConstructor(InvocationHandler.class,Object[].class);
        Object m = constructor.newInstance(handler,args);
        //将代理类实例返回
        //此处由于java文件和class文件已经不再使用,因此我们可以将其删除
        //实现没有任何文件残留
        
        /*
         *············
         *·······
         * */
        return m;
    }
}

 

 现在我们的代理功能已经实现:

那么之前的送礼物代码可以改成这样:

 

package com.lin.proxy;

public class Test {

	public static void main(String[] args) throws Exception {
		// TODO 原谅的颜色在飘
		Girl mm = new Girl();
		mm.setName("小红");
		Boy boy = new Boy(mm);
//		ProxyBoy proxyBoy = new ProxyBoy(boy);
//		proxyBoy.sendFlower();
//		proxyBoy.sendLoveLetter();
		GiveGift proxy = (GiveGift)MyProxy.newProxyInstance(GiveGift.class,new Object[]{}, new RecodeHandler(boy));
		proxy.sendFlower();
		proxy.sendLoveLetter();
	}

}

 执行结果:

Start Time1498981076580
com.lin.proxy.MySendProxy
小红送你一朵花
Moving time:0
Start Time1498981076580
com.lin.proxy.MySendProxy
小红送你一封信
Moving time:0

 

这就是动态代理的简单模仿实现。

 

 

 

 

  

posted on 2017-07-02 15:43  一只野生程序猿  阅读(186)  评论(0)    收藏  举报

导航