04 设计模式之--代理设计模式

来源:互联网 发布:布兰迪斯大学 知乎 编辑:程序博客网 时间:2024/06/06 18:03


代理
  >>静态代理(编译时,代理对象实现接口)
  >>动态代理(运行时,代理对象独立)


  1)为什么需要代理对象
    因为需要防止对目标对象的无条件直接访问  


  2)代理谁
     代理目标对象,即"春哥"
     目标对象的sing()方法:是含有真实业务的
     代理对象的sing()方法:只是调用目标对象的sing()方法


  3)如何代理
     调用任务业务方法,都会被代理类拦截,从而调用invoke()方法
     动态代理的步骤:
     NO1:写一个普通类
     NO2:写一个实例变量,记录代理谁
     NO3:使用构造方法为上述实例变量设置值
    *NO4:写一个普通方法,返回接口,该方法的返回值就是动态代理对象,该动态代理对象也实现了这个接口
         该方法内部使用Proxy.newProxyInstance()来动态创建代理对象     


  4)代理能解决什么问题
    例如:网站GET和POST编码的统一处理--见例
          页面响应的字符压缩 
    装饰设计模式能做的事情,代理都能完成;
    代理能做的事情,装饰不一定;
    Spring  IOC思想  AOP思想(动态代理)  




动态代理(代理类产生代理对象)
明确两个概念:
代理对象存在的价值:主要用于拦截对真实业务对象的访问。
代理对象有什么方法?与真实对象相同,只是无业务代码。
现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。
如何编写生成代理对象的类,两个要素:
代理谁
如何生成代理对象
代理谁?
设计一个类变量,以及一个构造函数,记住代理类 代理哪个对象。
如何生成代理对象?(invoke方法)
设计一个方法生成代理对象(在方法内编写代码生成代理对象是此处编程的难点)


Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:
1.生成代理对象使用哪个类加载器
2.生成哪个对象的代理对象,通过接口指定
3.生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定。
初学者必须记住的2件事情:
Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。

详情见例


-静态代理-----------------


//接口 -- 演艺公司interface Star {// 提供了唱歌方法 需要传钱进来public void sing(String money);}// ---------------------------------------// 目标对象 -- 春哥 歌手class Liyuchun implements Star {// 实现演艺公司唱歌方法public void sing(String money) {System.out.println("春哥唱歌");}}// ---------------------------------------// 代理对象 -- 经纪人class MyProxy implements Star {// 代理对象之一 春哥 歌手private Liyuchun liyuchun = new Liyuchun();// 歌迷找经纪人要 歌手唱歌public void sing(String money) {// 经纪人进行过渡数据信息 满足条件放行,类似与javaEE中Web过滤器Filterif ("3万".equals(money)) {System.out.println("代理对象找目标对象");// 经纪人本身不会唱歌,也是进行调用歌手唱歌 调用春哥,唱歌方法liyuchun.sing(money);} else {System.out.println("出场费不够");}}public Star findLiyuchun() {return this;}}// ---------------------------------------//粉丝public class Fans {public static void main(String[] args) {//粉丝找 经济人MyProxy myProxy = new MyProxy();// 经济人找春哥Star star = myProxy.findLiyuchun();//调用 经济人 唱歌方法 star.sing("2万");}}

--------------------------------------------------------



-动态代理--------------------------


import java.lang.reflect.InvocationHandler;//反射包import java.lang.reflect.Method;import java.lang.reflect.Proxy;//接口 -- 演艺公司interface Star {// 提供了唱歌方法 需要传钱进来public String sing(String money);}// --------------------------------------------------------------------// 目标对象 class Liyuchun implements Star {// 实现演艺公司唱歌方法public String sing(String money) {System.out.println("春哥唱歌");return "谢谢";}}// --------------------------------------------------------------------// 代理对象  //NO1:写一个普通类 class MyProxy {// 代理对象之一 春哥 歌手  //NO2:写一个实例变量,记录代理谁private Liyuchun liyuchun;//NO3:使用构造方法为上述实例变量设置值public MyProxy(Liyuchun liyuchun) {this.liyuchun = liyuchun;}//返回代理对象//NO4:写一个普通方法,返回接口,该方法的返回值就是动态代理对象,//该动态代理对象也实现了这个接口public Star findLiyuchun() { //该方法内部使用Proxy.newProxyInstance(参数一,参数二,参数三)来动态创建代理对象,return (Star) Proxy.newProxyInstance(MyProxy.class.getClassLoader(),//参数一.代理对象由哪个类加载器负责加载liyuchun.getClass().getInterfaces(),//参数二.目标对象的方法集合//参数三.返回值是代理对象.InvocationHandler是一个接口,里面只有一个invoke方法,使用匿名内部类实现 new InvocationHandler() {//接口中唯一一个方法 匿名内部类实现 invoke(参数1,参数2,参数3)public Object invoke(Object proxy,//参数1.动态产生的代理对象本身,类似this Method method,//参数2.业务方法,通常使用getName()来取得方法名Object[] args//参数3.业务方法的参数,第一个参数位于数据下标为0的位置,无参数为null) throws Throwable {//如果是调用的是sing方法if (method.getName().equals("sing")) {String money = (String) args[0];//获取参数即 moneyif ("3万".equals(money)) {System.out.println("代理对象找目标对象");return method.invoke(liyuchun, args);} else {System.out.println("出场费不够");}} else {System.out.println("春哥不会其它技能了");}return proxy;}});}}// 粉丝-------------------------public class Fans {public static void main(String[] args) {// 创建目标对象Liyuchun liyuchun = new Liyuchun();// 经济人MyProxy myProxy = new MyProxy(liyuchun);// 经济人找春哥Star star = myProxy.findLiyuchun();// 经济人唱歌String value = star.sing("3万");// 显示System.out.println(value);}}

----------------------------------------------------

例--------JavaEE-代理设计模式Web应用-----网站GET和POST编码的统一处理----------------------------

import java.io.IOException;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class EncodingFiler implements Filter {public void destroy() {}public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;// 响应有效response.setContentType("text/html;charset=UTF-8");// 创建MyRequest 装饰设计模式的增强方法//MyRequest myRequest = new MyRequest(request);//动态代理方式的增强方法//创建代理类MyRequest myRequest = new MyRequest(request);//动态产生代理对象HttpServletRequest myRequestPoxy = myRequest.getProxy();// 放行请求时,传入代理对象chain.doFilter(myRequestPoxy, response);}}//1.class MyRequest {//2.private HttpServletRequest request ;//3.public MyRequest(HttpServletRequest request ){this.request = request ;}//4.public HttpServletRequest getProxy(){return (HttpServletRequest)Proxy.newProxyInstance(MyRequest.class.getClassLoader(),//request.getClass().getInterfaces(),//匿名内部类new InvocationHandler(){//InvocationHandler接口实现invoke方法public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {//如果调用的是获取参数getParameter方法if (method.getName().equals("getParameter")) {//获取请求的类型String met = request.getMethod();//Post请求if (met.equalsIgnoreCase("post")) {//设置编码方式为UTF-8request.setCharacterEncoding("UTF-8");//获取表单参数String str = (String) args[0];//返回正确编码后的值return request.getParameter(str);//如果是get请求}else if (met.equalsIgnoreCase("get")) {//获取表单参数 当前中文乱码 进行手动编码String str = (String) args[0];//还原数据byte[] b  = str.getBytes("ISO8859-1");//重新编码str = new String(b,"UTF-8");//返回编码后的数据return request.getParameter(str);}}else{//如果是调用其它方法原样返回,不进行处理return method.invoke(request, args);}return null;}});}}

----------------------------------------------------------

例--------JavaEE-代理设计模式Web应用二----网站Gzip压缩处理---------------


import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.zip.GZIPOutputStream;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class GzipFilter implements Filter {public void destroy() {}public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {//目标对象HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;//创建代理类MyGzipResponse MyResponse = new MyGzipResponse(response);//产生代理对象 并发行请求chain.doFilter(request, MyResponse.getProxy());//获取出缓存中的数据byte[] b = MyResponse.getData();//显示压缩前大小 System.out.println("显示压缩前大小 :"+b.length);//Gzip格式压缩ByteArrayOutputStream baos = new ByteArrayOutputStream();GZIPOutputStream gzos = new GZIPOutputStream(baos);gzos.write(b);gzos.flush();gzos.close();//获取压缩后缓存中的数据b = baos.toByteArray();//显示压缩后大小System.out.println("显示压缩后大小"+b.length);//数据发送到浏览器//通知浏览器发送的数据是Gzip格式的需要进行解压.response.setHeader("content-encoding","gzip");//通知浏览器数据长度,部分浏览器不设置这句会出问题response.setHeader("content-length",b.length+"");//输出到浏览器response.getOutputStream().write(b);}}// 1.代理对象class MyGzipResponse {// 2.private HttpServletResponse response;private PrintWriter pw ;//打印输出字符流// 3.public MyGzipResponse(HttpServletResponse response) {this.response = response;}//定义一个缓存private ByteArrayOutputStream baos = new ByteArrayOutputStream(); //返回封装的数据public byte[] getData(){if (pw!=null) {//确保数据全部进入到缓存流pw.flush();}//返回缓存return baos.toByteArray();}//匿名内部类public HttpServletResponse getProxy() {return (HttpServletResponse) Proxy.newProxyInstance(//参数一.代理对象由哪个类加载器负责加载.MyGzipResponse.class.getClassLoader(),//参数二.目标对象的方法集合response.getClass().getInterfaces(),//参数三.返回代理对象 匿名内部类 new InvocationHandler() {//实现接口中唯一的invoke方法 参数中1.proxy表示动态代理对象本身,2.method业务方法,3.args业务参数public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {//如果调用的是getWriter方法  if (method.getName().equals("getWriter")) {//将字节转换成字符并设置编码方式防止中文乱码 返回 pw = new PrintWriter(new OutputStreamWriter(baos,"UTF-8"));return pw;}else{//如果调用其他方法则原样返回return method.invoke(response, args);}}});}}


















原创粉丝点击