网站方案讲解技巧,ppt的免费网站,网站建设费 大创,哪里有男男做受网站2019独角兽企业重金招聘Python工程师标准 一、代理的概念 动态代理技术是整个java技术中最重要的一个技术#xff0c;它是学习java框架的基础#xff0c;不会动态代理技术#xff0c;那么在学习Spring这些框架时是学不明白的。 动态代理技术就是用来产生一个对… 2019独角兽企业重金招聘Python工程师标准 一、代理的概念 动态代理技术是整个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中规定要想产生一个对象的代理对象那么这个对象必须要有一个接口所以我们第一步就是设计这个对象的接口在接口中定义这个对象所具有的行为(方法) 1、定义对象的行为接口 package cn.gacl.proxy;/**
* ClassName: Person
* Description: 定义对象的行为
* author: 孤傲苍狼
* date: 2014-9-14 下午9:44:22
*
*/
public interface Person {/*** Method: sing* Description: 唱歌* Anthor:孤傲苍狼** param name* return*/ String sing(String name);/*** Method: sing* Description: 跳舞* Anthor:孤傲苍狼** param name* return*/ String dance(String name);
} 2、定义目标业务对象类 package cn.gacl.proxy;/**
* ClassName: LiuDeHua
* Description: 刘德华实现Person接口那么刘德华会唱歌和跳舞了
* author: 孤傲苍狼
* date: 2014-9-14 下午9:22:24
*
*/
public class LiuDeHua implements Person {public String sing(String name){System.out.println(刘德华唱name歌);return 歌唱完了谢谢大家;}public String dance(String name){System.out.println(刘德华跳name舞);return 舞跳完了多谢各位观众;}
} 3、创建生成代理对象的代理类 package cn.gacl.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/**
* ClassName: LiuDeHuaProxy
* Description: 这个代理类负责生成刘德华的代理人
* author: 孤傲苍狼
* date: 2014-9-14 下午9:50:02
*
*/
public class LiuDeHuaProxy {//设计一个类变量记住代理类要代理的目标对象private Person ldh new LiuDeHua();/*** 设计一个方法生成代理对象* Method: getProxy* Description: 这个方法返回刘德华的代理对象Person person LiuDeHuaProxy.getProxy();//得到一个代理对象* Anthor:孤傲苍狼** return 某个对象的代理对象*/ public Person getProxy() {//使用Proxy.newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h)返回某个对象的代理对象return (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class.getClassLoader(), ldh.getClass().getInterfaces(),new InvocationHandler() {/*** InvocationHandler接口只定义了一个invoke方法因此对于这样的接口我们不用单独去定义一个类来实现该接口* 而是直接使用一个匿名内部类来实现该接口new InvocationHandler() {}就是针对InvocationHandler接口的匿名实现类*//*** 在invoke方法编码指定返回的代理对象干的工作* proxy : 把代理对象自己传递进来 * method把代理对象当前调用的方法传递进来 * args:把方法参数传递进来* * 当调用代理对象的person.sing(冰雨);或者 person.dance(江南style);方法时* 实际上执行的都是invoke方法里面的代码* 因此我们可以在invoke方法中使用method.getName()就可以知道当前调用的是代理对象的哪个方法*/Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {//如果调用的是代理对象的sing方法if (method.getName().equals(sing)) {System.out.println(我是他的经纪人要找他唱歌得先给十万块钱);//已经给钱了经纪人自己不会唱歌就只能找刘德华去唱歌return method.invoke(ldh, args); //代理对象调用真实目标对象的sing方法去处理用户请求}//如果调用的是代理对象的dance方法if (method.getName().equals(dance)) {System.out.println(我是他的经纪人要找他跳舞得先给二十万块钱);//已经给钱了经纪人自己不会唱歌就只能找刘德华去跳舞return method.invoke(ldh, args);//代理对象调用真实目标对象的dance方法去处理用户请求}return null;}});}
} 测试代码 package cn.gacl.proxy;public class ProxyTest {public static void main(String[] args) {LiuDeHuaProxy proxy new LiuDeHuaProxy();//获得代理对象Person p proxy.getProxy();//调用代理对象的sing方法String retValue p.sing(冰雨);System.out.println(retValue);//调用代理对象的dance方法String value p.dance(江南style);System.out.println(value);}
} 运行结果如下 Proxy类负责创建代理对象时如果指定了handler处理器那么不管用户调用代理对象的什么方法该方法都是调用处理器的invoke方法。 由于invoke方法被调用需要三个参数代理对象、方法、方法的参数因此不管代理对象哪个方法调用处理器的invoke方法都必须把自己所在的对象、自己调用invoke方法的方法、方法的参数传递进来。 三、动态代理应用 在动态代理技术里由于不管用户调用代理对象的什么方法都是调用开发人员编写的处理器的invoke方法这相当于invoke方法拦截到了代理对象的方法调用。并且开发人员通过invoke方法的参数还可以在拦截的同时知道用户调用的是什么方法因此利用这两个特性就可以实现一些特殊需求例如拦截用户的访问请求以检查用户是否有访问权限、动态为某个对象添加额外的功能。 3.1、在字符过滤器中使用动态代理解决中文乱码 在平时的JavaWeb项目开发中我们一般会写一个CharacterEncodingFilter(字符过滤器)来解决整个JavaWeb应用的中文乱码问题如下所示 package me.gacl.web.filter;import java.io.IOException;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;/**
* ClassName: CharacterEncodingFilter
* Description: 解决中文乱码的字符过滤器
* author: 孤傲苍狼
* date: 2014-9-14 下午10:38:12
*
*/
public class CharacterEncodingFilter implements Filter {Overridepublic void init(FilterConfig filterConfig) throws ServletException {}Overridepublic void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {//解决以Post方式提交的中文乱码问题request.setCharacterEncoding(UTF-8);response.setCharacterEncoding(UTF-8);response.setContentType(text/html;charsetUTF-8);chain.doFilter(request, response);}Overridepublic void destroy() {}
} 但是这种写法是没有办法解决以get方式提交中文参数时的乱码问题的我们可以用如下的代码来证明上述的解决中文乱码过滤器只对以post方式提交中文参数时有效而对于以get方式提交中文参数时无效 jsp测试页面如下 % page languagejava pageEncodingUTF-8%
%--引入jstl标签库 --%
%taglib urihttp://java.sun.com/jsp/jstl/core prefixc%
!DOCTYPE HTML
htmlheadtitle使用字符过滤器解决解决get、post请求方式下的中文乱码问题/title/headbody%--使用c:url标签构建url构建好的url存储在servletDemo1变量中--%c:url value/servlet/ServletDemo1 scopepage varservletDemo1%--构建的url的附带的中文参数 参数名是username值是孤傲苍狼--%c:param nameusername value孤傲苍狼/c:param/c:url%--使用get的方式访问 --%a href${servletDemo1}超链接(get方式请求)/ahr/%--使用post方式提交表单 --%form action${pageContext.request.contextPath}/servlet/ServletDemo1 methodpost用户名input typetext nameusername value孤傲苍狼 /input typesubmit valuepost方式提交/form/body
/html 处理请求的ServletDemo1代码如下 package me.gacl.web.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 ServletDemo1 extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 接收参数String username request.getParameter(username);// 获取请求方式String method request.getMethod();// 获取输出流PrintWriter out response.getWriter();out.write(请求的方式 method);out.write(br/);out.write(接收到的参数 username);}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}
} 在web.xml中注册上述的CharacterEncodingFilter和ServletDemo1 filterfilter-nameCharacterEncodingFilter/filter-namefilter-classme.gacl.web.filter.CharacterEncodingFilter/filter-class/filterfilter-mappingfilter-nameCharacterEncodingFilter/filter-nameurl-pattern/*/url-pattern/filter-mappingservletservlet-nameServletDemo1/servlet-nameservlet-classme.gacl.web.controller.ServletDemo1/servlet-class/servletservlet-mappingservlet-nameServletDemo1/servlet-nameurl-pattern/servlet/ServletDemo1/url-pattern/servlet-mapping 测试结果如下所示 从运行结果可以看出上述的过滤器的确是不能解决以get方式提交中文参数的乱码问题下面使用动态代理技术改造上述的过滤器使之能够解决以get方式提交中文参数的乱码问题改造后的过滤器代码如下 package me.gacl.web.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;/**
* ClassName: CharacterEncodingFilter
* Description: 解决中文乱码的字符过滤器
* author: 孤傲苍狼
* date: 2014-9-14 下午10:38:12
*
*/
public class CharacterEncodingFilter implements Filter {Overridepublic void init(FilterConfig filterConfig) throws ServletException {}Overridepublic void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException {final HttpServletRequest request (HttpServletRequest) req;HttpServletResponse response (HttpServletResponse) resp;//解决以Post方式提交的中文乱码问题request.setCharacterEncoding(UTF-8);response.setCharacterEncoding(UTF-8);response.setContentType(text/html;charsetUTF-8);//获取获取HttpServletRequest对象的代理对象ServletRequest requestProxy getHttpServletRequestProxy(request);/*** 传入代理对象requestProxy给doFilter方法* 这样用户在使用request对象时实际上使用的是HttpServletRequest对象的代理对象requestProxy*/chain.doFilter(requestProxy, response);}/*** Method: getHttpServletRequestProxy* Description: 获取HttpServletRequest对象的代理对象* Anthor:孤傲苍狼** param request* return HttpServletRequest对象的代理对象*/ private ServletRequest getHttpServletRequestProxy(final HttpServletRequest request){ServletRequest proxy (ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(),request.getClass().getInterfaces(),new InvocationHandler(){Overridepublic 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(valuenull){return null;}//解决以get方式提交的中文乱码问题return new String(value.getBytes(iso8859-1),UTF-8);}else {//直接调用相应的方法进行处理return method.invoke(request, args);}}});//返回HttpServletRequest对象的代理对象return proxy;}Overridepublic void destroy() {}
} 我们在过滤器中使用动态代理技术生成一个HttpServletRequest对象的代理对象requestProxy然后把代理对象requestProxy进行chain.doFilter(requestProxy, response)传递给用户使用这样用户实际上使用的就是HttpServletRequest对象的代理对象requestProxy。然而这一过程对于用户来说是透明的用户是不知道自己使用的HttpServletRequest对象是一个代理对象requestProxy由于代理对象requestProxy和目标对象HttpServletRequest具有相同的方法当用户调用getParameter方法接收中文参数时实际上调用的就是代理对象requestProxy的invoke方法因此我们就可以在invoke方法中就判断当前的请求方式以及用户正在调用的方法如果判断当前的请求方式是get方式并且用户正在调用的是getParameter方法那么我们就可以手动处理get方式提交中文参数的中文乱码问题了。 测试结果如下所示 3.2、在字符过滤器中使用动态代理压缩服务器响应的内容后再输出到客户端 压缩过滤器的代码如下 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 {Overridepublic void init(FilterConfig filterConfig) throws ServletException {}Overridepublic 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(){Overridepublic 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);}}});}Overridepublic void destroy() {}class MyServletOutputStream extends ServletOutputStream{private ByteArrayOutputStream bout null;public MyServletOutputStream(ByteArrayOutputStream bout){this.bout bout;}Overridepublic void write(int b) throws IOException {bout.write(b);}}
} 在web.xml中注册上述的GzipFilter filterdescription配置压缩过滤器/descriptionfilter-nameGzipFilter/filter-namefilter-classme.gacl.web.filter.GzipFilter/filter-class/filter!--jsp文件的输出的内容都经过压缩过滤器压缩后才输出 --filter-mappingfilter-nameGzipFilter/filter-nameurl-pattern*.jsp/url-pattern!-- 配置过滤器的拦截方式--!-- 对于在Servlet中通过request.getRequestDispatcher(jsp页面路径).forward(request, response) 方式访问的Jsp页面的要进行拦截 --dispatcherFORWARD/dispatcher!--对于直接以URL方式访问的jsp页面进行拦截过滤器的拦截方式默认就是REQUEST--dispatcherREQUEST/dispatcher/filter-mapping!--js文件的输出的内容都经过压缩过滤器压缩后才输出 --filter-mappingfilter-nameGzipFilter/filter-nameurl-pattern*.js/url-pattern/filter-mapping!--css文件的输出的内容都经过压缩过滤器压缩后才输出 --filter-mappingfilter-nameGzipFilter/filter-nameurl-pattern*.css/url-pattern/filter-mapping!--html文件的输出的内容都经过压缩过滤器压缩后才输出 --filter-mappingfilter-nameGzipFilter/filter-nameurl-pattern*.html/url-pattern/filter-mapping GzipFilter过滤器会将*.jsp*.js*.css*.html这些文件里面的文本内容都经过压缩后再输出到客户端显示。 转载于:https://my.oschina.net/HerrySun/blog/656559