JAVA编码(26)——过滤器中使用动态代理(Proxy)来处理get请求乱码问题
一、代理的概念
动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。
动态代理技术就是用来产生一个对象的代理对象的。在开发中为什么需要为一个对象产生代理对象呢?
举一个现实生活中的例子:歌星或者明星都有一个自己的经纪人,这个经纪人就是他们的代理人,当我们需要找明星表演时,不能直接找到该明星,只能是找明星的代理人。比如刘德华在现实生活中非常有名,会唱歌,会跳舞,会拍戏,刘德华在没有出名之前,我们可以直接找他唱歌,跳舞,拍戏,刘德华出名之后,他干的第一件事就是找一个经纪人,这个经纪人就是刘德华的代理人(代理),当我们需要找刘德华表演时,不能直接找到刘德华了(刘德华说,你找我代理人商谈具体事宜吧!),只能是找刘德华的代理人,因此刘德华这个代理人存在的价值就是拦截我们对刘德华的直接访问!
这个现实中的例子和我们在开发中是一样的,我们在开发中之所以要产生一个对象的代理对象,主要用于拦截对真实业务对象的访问。那么代理对象应该具有什么方法呢?代理对象应该具有和目标对象相同的方法
所以在这里明确代理对象的两个概念:
1、代理对象存在的价值主要用于拦截对真实业务对象的访问。
2、代理对象应该具有和目标对象(真实业务对象)相同的方法。刘德华(真实业务对象)会唱歌,会跳舞,会拍戏,我们现在不能直接找他唱歌,跳舞,拍戏了,只能找他的代理人(代理对象)唱歌,跳舞,拍戏,一个人要想成为刘德华的代理人,那么他必须具有和刘德华一样的行为(会唱歌,会跳舞,会拍戏),刘德华有什么方法,他(代理人)就要有什么方法,我们找刘德华的代理人唱歌,跳舞,拍戏,但是代理人不是真的懂得唱歌,跳舞,拍戏的,真正懂得唱歌,跳舞,拍戏的是刘德华,在现实中的例子就是我们要找刘德华唱歌,跳舞,拍戏,那么只能先找他的经纪人,交钱给他的经纪人,然后经纪人再让刘德华去唱歌,跳舞,拍戏。
二、java中的代理
2.1、"java.lang.reflect.Proxy"类介绍
现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。在java中如何用程序去生成一个对象的代理对象呢,java在JDK1.5之后提供了一个"java.lang.reflect.Proxy"类,通过"Proxy"类提供的一个newProxyInstance方法用来创建一个对象的代理对象,如下所示:
1 static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数,ClassLoader loader用来指明生成代理对象使用哪个类装载器,Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。
2.2、编写生成代理对象的类
在java中规定,要想产生一个对象的代理对象,那么这个对象必须要有一个接口,所以我们第一步就是设计这个对象的接口,在接口中定义这个对象所具有的行为(方法)
具体项目:详见360网盘>>经典详例Demo
WebConent文件下的index.html Author:xushuyi 2015-06-15
导入servlet-api.jar servlet.jar包
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登陆演示</title> <script type="text/javascript"> document.write(location.pathname); var path = location.pathname; </script> </head> <body> <form action="controller/ServletDemo01" method="get"> <table> <tr> <th>姓名</th> <td> <input type="text" name="uname" /> </td> <th>密码</th> <td> <input type="password" name="pwd" /> </td> </tr> <tr> <td colspan="2"> <input type="submit" value="点击登陆" /> </td> <td> <input type="reset" value="重置" /> </td> </tr> </table> </form> </body> </html>
package com.sinosoft.controller; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ServletDemo01 extends HttpServlet{ /** * 序列化 用户版本控制 */ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 接收参数 String uname = req.getParameter("uname"); String method = req.getMethod(); PrintWriter out = resp.getWriter(); out.write("请求方式:"+method); out.write("<br/>"); out.write("接收到的参数:"+uname); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>xu_shuyi_test_web</display-name> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.sinosoft.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>charset</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>ServletDemo01</servlet-name> <servlet-class>com.sinosoft.controller.ServletDemo01</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletDemo01</servlet-name> <url-pattern>/controller/ServletDemo01</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
package com.sinosoft.filter; 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; import org.apache.log4j.Logger; public class CharacterEncodingFilter implements Filter{ private Logger logger = Logger.getLogger(CharacterEncodingFilter.class); /** * FilterConfig * 可以获取部署描述文件(web.xml)中分配的过滤器初始化参数 * FilterConfig对象提供对servlet环境及web.xml文件中指派的过滤器名的访问。 * FilterConfig对象具有一个getInitParameter方法,它能够访问部署描述符文件(web.xml)中分配的过滤器初始化参数。 */ private FilterConfig filterConfig = null; /** * 初始化 */ @Override public void init(FilterConfig filterConfig) throws ServletException { logger.info("CharacterEncodingFilter.init()"); this.filterConfig = filterConfig; } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String charset = filterConfig.getInitParameter("charset"); //解决post提交乱码问题 request.setCharacterEncoding(charset); response.setCharacterEncoding(charset); response.setContentType("text/html;charset="+charset); //获取HttpServletRequest对象的代理对象 ServletRequest requestProxy = getHttpServletRequestProxy(request); /** * 传入代理对象requestProxy给doFilter方法 * 这样用户在使用request对象时实际上使用的是HttpServletRequest对象的代理对象requestProxy */ chain.doFilter(requestProxy, response); } /** * @param request * @return HttpServletRequest对象的代理对象 */ private ServletRequest getHttpServletRequestProxy(final HttpServletRequest request) { return (ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果请求方式是get并且调用的是getParameter方法 if (request.getMethod().equalsIgnoreCase("get") && method.getName().equals("getParameter")) { //调用getParameter方法获取参数的值 String value = (String) method.invoke(request, args); if (value==null) { return null; } //解决get请求方法乱码问题 return new String(value.getBytes("ISO-8859-1"),"UTF-8"); } else { //直接调用相应方法进行处理 return method.invoke(request, args); } } }); } /** * 执行销毁 */ @Override public void destroy() { logger.info("CharacterEncodingFilter.destroy()"); this.filterConfig = null; } }
在字符过滤器中使用动态代理压缩服务器响应的内容后再输出到客户端 摘录别人的
package me.gacl.web.filter; 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.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @ClassName: GzipFilter * @Description: 压缩过滤器,将web应用中的文本都经过压缩后再输出到浏览器 * @author: 孤傲苍狼 * @date: 2014-9-15 下午9:35:36 * */ public class GzipFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) req; final HttpServletResponse response = (HttpServletResponse) resp; final ByteArrayOutputStream bout = new ByteArrayOutputStream(); final PrintWriter pw = new PrintWriter(new OutputStreamWriter(bout,"UTF-8")); chain.doFilter(request, getHttpServletResponseProxy(response, bout, pw)); pw.close(); //拿到目标资源的输出 byte result[] = bout.toByteArray(); System.out.println("原始大小:" + result.length); ByteArrayOutputStream bout2 = new ByteArrayOutputStream(); GZIPOutputStream gout = new GZIPOutputStream(bout2); gout.write(result); gout.close(); //拿到目标资源输出的压缩数据 byte gzip[] = bout2.toByteArray(); System.out.println("压缩大小:" + gzip.length); response.setHeader("content-encoding", "gzip"); response.setContentLength(gzip.length); response.getOutputStream().write(gzip); } /** * @Method: getHttpServletResponseProxy * @Description: 获取HttpServletResponse对象的代理对象 * @Anthor:孤傲苍狼 * * @param response * @param bout * @param pw * @return HttpServletResponse对象的代理对象 */ private ServletResponse getHttpServletResponseProxy( final HttpServletResponse response, final ByteArrayOutputStream bout, final PrintWriter pw) { return (ServletResponse) Proxy.newProxyInstance(GzipFilter.class.getClassLoader(), response.getClass().getInterfaces(), new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("getWriter")){ return pw; }else if(method.getName().equals("getOutputStream")){ return new MyServletOutputStream(bout); }else{ return method.invoke(response, args); } } }); } @Override public void destroy() { } class MyServletOutputStream extends ServletOutputStream{ private ByteArrayOutputStream bout = null; public MyServletOutputStream(ByteArrayOutputStream bout){ this.bout = bout; } @Override public void write(int b) throws IOException { bout.write(b); } } }
在web.xml中注册上述的GzipFilter
<filter> <description>配置压缩过滤器</description> <filter-name>GzipFilter</filter-name> <filter-class>me.gacl.web.filter.GzipFilter</filter-class> </filter> <!--jsp文件的输出的内容都经过压缩过滤器压缩后才输出 --> <filter-mapping> <filter-name>GzipFilter</filter-name> <url-pattern>*.jsp</url-pattern> <!-- 配置过滤器的拦截方式--> <!-- 对于在Servlet中通过 request.getRequestDispatcher("jsp页面路径").forward(request, response) 方式访问的Jsp页面的要进行拦截 --> <dispatcher>FORWARD</dispatcher> <!--对于直接以URL方式访问的jsp页面进行拦截,过滤器的拦截方式默认就是REQUEST--> <dispatcher>REQUEST</dispatcher> </filter-mapping> <!--js文件的输出的内容都经过压缩过滤器压缩后才输出 --> <filter-mapping> <filter-name>GzipFilter</filter-name> <url-pattern>*.js</url-pattern> </filter-mapping> <!--css文件的输出的内容都经过压缩过滤器压缩后才输出 --> <filter-mapping> <filter-name>GzipFilter</filter-name> <url-pattern>*.css</url-pattern> </filter-mapping> <!--html文件的输出的内容都经过压缩过滤器压缩后才输出 --> <filter-mapping> <filter-name>GzipFilter</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping>
GzipFilter过滤器会将*.jsp,*.js,*.css,*.html这些文件里面的文本内容都经过压缩后再输出到客户端显示。