代理
>>静态代理(编译时,代理对象实现接口)
>>动态代理(运行时,代理对象独立)
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);
}
}
});
}
}