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

8 篇文章 0 订阅
6 篇文章 0 订阅


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


  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过滤器Filter
		if ("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];//获取参数即 money
							if ("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-8
								request.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);
						}
					}
				});
	}
}


















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值