代理模式:即Proxy Pattern,常用的设计模式之一。代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。

代理概念 :为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。

下面以明星为例模拟需求说明静态代理和动态代理。

一、首先看静态代理

看下图:歌迷希望明星许巍唱歌(许巍即是目标对象),但不可能直接找到许巍,只能通过许巍经纪人,然后经纪人让许巍唱歌。这里的经纪人即是许巍的一个代理对象,这样就可以阻止对目标对象的直接访问。

代理接口

 1 package com.lizhou.test.proxy;
 2
 3 /**
 4  * 代理接口:明星
 5  * @author bojiangzhou
 6  * @date 2016年5月5日
 7  */
 8 public interface Star {
 9
10     /**
11      * 明星唱歌
12      */
13     void sing(String song);
14
15 }

委托类:真正执行任务的类

 1 package com.lizhou.test.proxy;
 2
 3 /**
 4  * 委托类:真正执行任务的类(许巍),实现了代理接口
 5  * @author bojiangzhou
 6  * @date 2016年5月5日
 7  */
 8 public class Xuwei implements Star {
 9
10     public void sing(String song) {
11         System.out.println("许巍唱歌:"+song);
12     }
13
14 }

静态代理类,实现了代理接口:许巍经纪人

 1 package com.lizhou.test.proxy;
 2
 3 /**
 4  * 静态代理类,实现了代理接口:许巍经纪人
 5  * @author bojiangzhou
 6  * @date 2016年5月5日
 7  */
 8 public class XuweiProxy implements Star {
 9
10     /**
11      * 代理类持有一个委托类的对象引用
12      */
13     private Star star;
14
15     public XuweiProxy(Star star){
16         this.star = star;
17     }
18
19     /**
20      * 代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理
21      */
22     public void sing(String song) {
23         //代理类负责请求的预处理
24         System.out.println("-----预处理:分析打电话给许巍经纪人要许巍唱歌-----");
25         System.out.println("-----预处理:经纪人要求许巍唱歌-----");
26
27         //将请求分派给委托类处理
28         star.sing(song);
29
30         //委托类执行完请求后的后续处理
31         System.out.println("-----后处理:许巍唱歌完毕-----");
32     }
33
34 }

粉丝:通过代理对象让目标对象执行任务

 1 package com.lizhou.test.proxy;
 2
 3 /**
 4  * 粉丝呼吁许巍唱歌
 5  * 粉丝不可能直接找许巍唱歌,需要先联系经纪人,经纪人再让许巍唱歌
 6  * @author bojiangzhou
 7  * @date 2016年5月5日
 8  */
 9 public class Fans {
10
11     public static void main(String[] args) {
12         //创建代理对象(即经纪人)
13         XuweiProxy xuweiProxy = new XuweiProxy(new Xuwei());
14
15         xuweiProxy.sing("完美生活");
16     }
17
18 }

这就是一个静态代理。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

静态代理的缺点: 
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。 
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

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

二、动态代理

看下图:动态代理时,代理类(即经纪人)是没有实现代理接口的(Star),是一个独立的类。那代理类如何代理目标对象呢?此时代理类则借助Proxy在运行时动态指向目标对象。

Java提供了一个Proxy类,Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

Proxy类:

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

还是以明星为例模拟需求动态代理

代理接口不变:

 1 package com.lizhou.test.proxy;
 2
 3 /**
 4  * 代理接口:明星
 5  * @author bojiangzhou
 6  * @date 2016年5月5日
 7  */
 8 public interface Star {
 9
10     /**
11      * 明星唱歌
12      */
13     void sing(String song);
14
15 }

目标对象不变:

 1 package com.lizhou.test.proxy;
 2
 3 /**
 4  * 委托类:真正执行任务的类(许巍),实现了代理接口
 5  * @author bojiangzhou
 6  * @date 2016年5月5日
 7  */
 8 public class Xuwei implements Star {
 9
10     public void sing(String song) {
11         System.out.println("许巍唱歌:"+song);
12     }
13
14 }

代理类:通过Proxy来动态产生目标对象

动态代理类开发步骤:
1)写一个普通类,无需任何继承或实现
2)写一个实例变量,记住代理谁,即目标对象
3)使用构造方法为实例变量赋值
4)写一个普通方法,该方法的返回值是接口,该接口是目标对象的实现接口

 1 package com.lizhou.test.proxy;
 2
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6
 7 /**
 8  * 代理类:没有任何实现、继承
 9  * @author bojiangzhou
10  * @date 2016年5月5日
11  */
12 public class XuweiProxy {
13
14     /**
15      * 代理类持有一个委托类的对象引用
16      */
17     private Star star;
18
19     public XuweiProxy(Star star){
20         this.star = star;
21     }
22
23     /**
24      * 动态产生代理对象
25      * @return
26      */
27     public Star getProxy(){
28         //参数一:loader表示动态代理对象是由哪个类加载器完成的。
29         //参数二:interface表示动态代理对象与目标对象有一样的方法。
30         //参数三:InvocationHandler处理器,接口中只有一个方法,这里使用匿名内部类实现
31         return (Star) Proxy.newProxyInstance(
32                 XuweiProxy.class.getClassLoader(),
33                 star.getClass().getInterfaces(),
34                 new InvocationHandler() {
35                     //invoke方法是代理的重点,会在invoke里面处理大量的业务逻辑判断
36                     //proxy:动态产生的代理对象本身
37                     //method:表示方法
38                     //args:表示参数
39                     public Object invoke(
40                             Object proxy,
41                             Method method,
42                             Object[] args) throws Throwable {
43                         //预处理
44                         System.out.println("----------预处理:粉丝要求经纪人要许巍唱歌----------");
45                         if("sing".equals(method.getName())){
46                             method.invoke(star, args);
47                         }
48                         //后处理
49                         System.out.println("----------后处理:许巍唱歌完毕----------");
50                         return null;
51                     }
52                 }
53             );
54     }
55
56
57
58 }

粉丝类:

 1 package com.lizhou.test.proxy;
 2
 3 /**
 4  * 粉丝呼吁许巍唱歌
 5  * 粉丝不可能直接找许巍唱歌,需要先联系经纪人,经纪人再让许巍唱歌
 6  * @author bojiangzhou
 7  * @date 2016年5月5日
 8  */
 9 public class Fans {
10
11     public static void main(String[] args) {
12         //创建代理对象(即经纪人)
13         XuweiProxy xuweiProxy = new XuweiProxy(new Xuwei());
14         //通过代理对象找目标对象
15         Star star = xuweiProxy.getProxy();
16         star.sing("完美生活");
17     }
18
19 }

结果:可以看得出来,虽然调用的是star.sing(),但并不是直接调用的。因为此处star是一个代理对象,先调用的是代理对象的invoke方法(invoke表示动态代理对象的拦截方法,每次调用目标对象都会执行该invoke()),我们在里面可以做一些预处理及后处理等操作。

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。

动态代理优点: 动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

JDK动态代理总结:

1,JAVA动态代理是使用java.lang.reflect包中的Proxy类与InvocationHandler接口这两个来完成的。

2,要使用JDK动态代理,必须要定义接口。

3,JDK动态代理将会拦截所有pubic的方法(因为只能调用接口中定义的方法),这样即使在接口中增加了新的方法,不用修改代码也会被拦截。

4,如果只想拦截一部分方法,可以在invoke方法中对要执行的方法名进行判断。

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

三、列举一个动态代理的实际应用例子:解决网站POST和GET的统一编码问题

思路:使用过滤器拦截请求,自定义一个代理request的类,然后将代理对象返回

看代码:

 1 package com.lizhou.test.proxy;
 2
 3 import java.io.IOException;
 4 import java.lang.reflect.InvocationHandler;
 5 import java.lang.reflect.Method;
 6 import java.lang.reflect.Proxy;
 7
 8 import javax.servlet.Filter;
 9 import javax.servlet.FilterChain;
10 import javax.servlet.FilterConfig;
11 import javax.servlet.ServletException;
12 import javax.servlet.ServletRequest;
13 import javax.servlet.ServletResponse;
14 import javax.servlet.http.HttpServletRequest;
15
16 /**
17  * 使用代理类解决网站POST和GET的统一编码问题
18  * @author bojiangzhou
19  * @date 2016年5月5日
20  */
21 public class CharacterEncodingFilter implements Filter {
22
23     public void destroy() {
24
25     }
26
27     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
28         //获取request的代理对象,这里request的代理对象对字符编码进行了处理的
29         RequestProxy proxy = new RequestProxy((HttpServletRequest) request);
30         //然后将代理对象传过去即可
31         chain.doFilter(proxy.getProxy(), response);
32     }
33
34     public void init(FilterConfig fConfig) throws ServletException {
35
36     }
37
38 }
39
40 /**
41  * request代理类
42  * @author bojiangzhou
43  * @date 2016年5月5日
44  */
45 class RequestProxy {
46     //明确代理对象
47     private HttpServletRequest request;
48
49     public RequestProxy(HttpServletRequest request){
50         this.request = request;
51     }
52
53     /**
54      * 返回request的代理对象
55      * @return
56      */
57     public ServletRequest getProxy(){
58
59         return (ServletRequest) Proxy.newProxyInstance(
60                 RequestProxy.class.getClassLoader(),
61                 request.getClass().getInterfaces(),
62                 new InvocationHandler() {
63
64                     public Object invoke(
65                             Object proxy,
66                             Method method,
67                             Object[] args) throws Throwable {
68                         System.out.println("执行代理对象方法");
69                         if("getParameter".equals(method.getName())){
70                             //如果调用了request的getParameter方法,则对其进行编码处理
71
72                             String param = (String) args[0];
73                             if("get".equalsIgnoreCase(request.getMethod())){
74                                 //如果是get请求方式
75                                 String value = request.getParameter(param);
76                                 return new String(value.getBytes("ISO8859-1"), "UTF-8");
77                             } else{
78                                 //post方式
79                                 request.setCharacterEncoding("UTF-8");
80                                 return request.getParameter(param);
81                             }
82                         } else{
83                             //放行资源
84                             return method.invoke(request, args);
85                         }
86                     }
87                 });
88     }
89
90 }

Servlet类:

 1 package com.lizhou.test.proxy;
 2
 3 import java.io.IOException;
 4 import javax.servlet.ServletException;
 5 import javax.servlet.annotation.WebServlet;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9
10 public class LoginServlet extends HttpServlet {
11
12     private static final long serialVersionUID = 1L;
13
14     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
15         //注意此处的request实际上是一个代理对象,在调用getParmeter方法时,实际调用的是代理对象的invoke方法。
16         String name = request.getParameter("name");
17         System.out.println("doGet 姓名:"+name);
18     }
19
20     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
21         this.doGet(request, response);
22     }
23
24 }

个人理解:代理的作用其实就类似于过滤器、Struts2的拦截器、Spring的切面/通知,在进行某个请求的时候,拦截该请求,做一些预处理、过滤、后置处理等,代理最重要的就是让你不能直接访问到实际的目标对象,使用的是目标对象的一个代理对象。

本文部分参考:java静态代理和动态代理

本文纯属个人学习笔记,因为代理比较少用到,所以这次再次学习后做个笔记以便以后需要的时候快速学习。

如有不当之处,敬请指出O(∩_∩)O~

转载于:https://www.cnblogs.com/chiangchou/p/java-proxy.html

Java代理模式/静态代理/动态代理相关推荐

  1. java中的静态、动态代理模式以及Spring中的CgLib动态代理解读(面试必问)

    java中的静态.动态代理模式以及Spring中的CgLib动态代理解读(面试必问) 静态代理 动态代理 CgLib动态代理     基础知: 反射知识 代理(Proxy)是一种设计模式,提供了对目标 ...

  2. 代理模式之详谈动态代理模式(Spring的AOP实现)

    java动态代理实现与原理详细分析 1.代理模式 关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 代理 ...

  3. 【设计模式】代理模式之JDK动态代理与CGLIb代理区别

    一.什么是代理? 代理模式是Java中常见的一种模式,英文名字叫走Proxy或者Surrogate,代理的本意是一个人代表另一个人,或者一个机构代表另一个机构,采取行动,因而,代理和现实生活中的中介有 ...

  4. 代理模式及JDK动态代理(InvocationHandler)的简单实现与分析

    在慕课网上学习了讲解代理模式的一个课程--<模式的秘密--代理模式>,感叹于David老师屌炸天的PPT,同时,老师一步一步模仿JDK源码去写code,教我们去简单实现JDK中的动态代理, ...

  5. 代理模式之jdk动态代理的实现

    学习动态代理之前需要了解静态代理 并且牢记静态代理的缺点 //首先被代理的类需要实现一个接口 public interface ProxyInterface {public void say(Stri ...

  6. 代理模式 、JDK动态代理、cglib动态代理

    代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢? 因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思. 再如我 ...

  7. 代理模式中的动态代理

    动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强.如果还以找对象为例,使用动态代理相当于是能够适应复杂的业务场景.不仅仅只是父亲给儿子找对象,如果找对象这项业务 ...

  8. 第六周 Java语法总结_设计原则_工厂模式_单例模式_代理模式(静态代理_动态代理)_递归_IO流_网络编程(UDP_TCP)_反射_数据库

    文章目录 20.设计原则 1.工厂模式 2.单例模式 1)饿汉式 2)懒汉式 3.Runtime类 4.代理模式 1)静态代理 2)动态代理 动态代理模板 21.递归 22.IO流 1.File 2. ...

  9. Java代理模式——静态代理动态代理

    proxy mode 1. 什么是代理 1.1 例子解释 1.2 作用 2. 静态代理 2.1 优缺点分析 2.2 以厂家卖u盘用代码说明 3. 动态代理 3.1 什么是动态代理 3.2 jdk实现原 ...

最新文章

  1. 简历英文 计算机水平,计算机英文 简历
  2. VS2013工具箱中使用WindowsMediaPlyer控件
  3. 随便玩玩之PostgreSQL(第一章)PostgreSQL简介
  4. zookeeper 和 kafka 集群搭建
  5. Spring boot 通过ApplicationRunner在启动完成后按指定顺序执行任务
  6. 由System.getProperty(user.dir)引发的联想
  7. java字符串怎么拼接字符串_Java中String使用+ 拼接字符串的原理是什么?
  8. Centos单网卡多IP的配置
  9. 在win7物理机,使用vmware,3台centos7系统,分别部署httpd,php-fpm,mariadb
  10. PAIP。JS调用DLL的解决方案
  11. ant Design Vue2.0+vite+vue3+typescript+node后台项目实现使用upload一个表单上传多个图片
  12. 【听课笔记】复旦大学遗传学_03基因与基因突变
  13. QT5.12.1 ARM开发环境搭建 并 移植到RK3399 ubuntu16.04系统运行【完整版】
  14. Linux(ubuntu)内容整理(常用命令)
  15. 星界边境联机服务器未响应,星界边境Starbound联机注意事项
  16. 计算机提高游戏运行速度的方法,怎么提高游戏运行速度
  17. 按分数段统计学生人数python_用Excel统计各分数段学生数
  18. 开发第一步之SMTP协议发送邮件,获取手机的详细信息
  19. 如何优雅的修改 Kubernetes Master 节点 IP?可没你想象中那么简单!
  20. Web前端是什么?主要是干什么的

热门文章

  1. 开发者必备的12个JavaScript库
  2. llinux基本操作
  3. 写给正在入坑linux系统的伙伴
  4. 互联网公司面试必问的mysql题目(下)
  5. Maven内置变量说明
  6. 深入理解java虚拟机---读后笔记(垃圾回收)
  7. Beanstalkd使用
  8. c#中页面之间传值传参的六种方法
  9. boost::trait::is_reference 的研究与修改
  10. MapReduce的自制Writable分组输出及组内排序