java 代理模式

来源:互联网 发布:数学 知乎 编辑:程序博客网 时间:2024/05/22 04:07

1.  简介

代理模式(Proxy Pattern)是GoF 23种Java常用设计模式之一。代理模式的定义:Provide a surrogate or placeholder for another object to controlaccess to it(为其他对象提供一种代理以控制对这个对象的访问)。使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能

2.  UML类图


3.  模式中包含的角色及其职责

Subject:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。

RealSubject:具体主题角色,也叫被委托角色、被代理角色。是业务逻辑的具体执行者。

Proxy:代理主题角色,也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。(最简单的比如打印日志)

4.  代码实现

(1)    Subject

package com.jackie.designpatterns.proxy;/** * 抽象主题,定义主要功能 */publicinterface Subject {   publicvoid operate();}

(2)    RealSubject

package com.jackie.designpatterns.proxy;/** * 具体主题 */publicclass RealSubjectimplements Subject{    @Override   publicvoid operate() {        System.out.println("realsubject operatestarted......");   }}

(3)     Proxy

package com.jackie.designpatterns.proxy;/** * 代理类 */publicclass Proxyimplements Subject{    private Subjectsubject;    public Proxy(Subject subject) {        this.subject = subject;   }    @Override   publicvoid operate() {        System.out.println("before operate......");        subject.operate();        System.out.println("after operate......");   }}

(4)     Client

package com.jackie.designpatterns.proxy;/** * 客户 */publicclass Client {   /**    * @param args    */   publicstaticvoid main(String[] args) {        Subject subject = new RealSubject();        Proxy proxy = new Proxy(subject);        proxy.operate();   }}

运行结果:    beforeoperate......

realsubject operate started......

afteroperate......

5.   应用场景

现实世界中,秘书就相当于一个代理,老板开会,那么通知员工开会时间、布置会场、会后整理会场等等开会相关工作就可以交给秘书做,老板就只需要开会就行了,不需要亲自做那些事。同理,在我们程序设计中也可使用代理模式来将由一系列无关逻辑组合在一起的代码进行解耦合,比如业务代码中的日志代码就可以在代理中进行。Spring的AOP就是典型的动态代理应用。


再来看一下动态代理: 
JDK动态代理中包含一个类和一个接口: 
InvocationHandler接口: 
public interface InvocationHandler { 
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数 

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。 

Proxy类: 
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法: 
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) 
                               throws IllegalArgumentException 
参数说明: 
ClassLoader loader:类加载器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子类实例 

Ps:类加载器 
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器; 
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的; 
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类; 
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。 

动态代理 
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 

动态代理示例: 
1、BookFacade.java 

    package net.battier.dao;            public interface BookFacade {          public void addBook();      }  

 

2、BookFacadeImpl.java 

    package net.battier.dao.impl;            import net.battier.dao.BookFacade;            public class BookFacadeImpl implements BookFacade {                @Override          public void addBook() {              System.out.println("增加图书方法。。。");          }            }            、BookFacadeProxy.java            package net.battier.proxy;            import java.lang.reflect.InvocationHandler;      import java.lang.reflect.Method;      import java.lang.reflect.Proxy;            /**      * JDK动态代理代理类      *       * @author student      *       */      public class BookFacadeProxy implements InvocationHandler {          private Object target;          /**          * 绑定委托对象并返回一个代理类          * @param target          * @return          */          public Object bind(Object target) {              this.target = target;              //取得代理对象              return Proxy.newProxyInstance(target.getClass().getClassLoader(),                      target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)          }                @Override          /**          * 调用方法          */          public Object invoke(Object proxy, Method method, Object[] args)                  throws Throwable {              Object result=null;              System.out.println("事物开始");              //执行方法              result=method.invoke(target, args);              System.out.println("事物结束");              return result;          }            }  

 

3、TestProxy.java 

    package net.battier.test;            import net.battier.dao.BookFacade;      import net.battier.dao.impl.BookFacadeImpl;      import net.battier.proxy.BookFacadeProxy;            public class TestProxy {                public static void main(String[] args) {              BookFacadeProxy proxy = new BookFacadeProxy();              BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());              bookProxy.addBook();          }            }  

 

但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。 

Cglib动态代理 
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 
示例 
1、BookFacadeCglib.java 

    package net.battier.dao;            public interface BookFacade {          public void addBook();      }  

 

2、BookCadeImpl1.java 

    package net.battier.dao.impl;            /**      * 这个是没有实现接口的实现类      *       * @author student      *       */      public class BookFacadeImpl1 {          public void addBook() {              System.out.println("增加图书的普通方法...");          }      }  


3、BookFacadeProxy.java 

    package net.battier.proxy;            import java.lang.reflect.Method;            import net.sf.cglib.proxy.Enhancer;      import net.sf.cglib.proxy.MethodInterceptor;      import net.sf.cglib.proxy.MethodProxy;            /**      * 使用cglib动态代理      *       * @author student      *       */      public class BookFacadeCglib implements MethodInterceptor {          private Object target;                /**          * 创建代理对象          *           * @param target          * @return          */          public Object getInstance(Object target) {              this.target = target;              Enhancer enhancer = new Enhancer();              enhancer.setSuperclass(this.target.getClass());              // 回调方法              enhancer.setCallback(this);              // 创建代理对象              return enhancer.create();          }                @Override          // 回调方法          public Object intercept(Object obj, Method method, Object[] args,                  MethodProxy proxy) throws Throwable {              System.out.println("事物开始");              proxy.invokeSuper(obj, args);              System.out.println("事物结束");              return null;                      }            }  


4、TestCglib.java 

package net.battier.test;    import net.battier.dao.impl.BookFacadeImpl1;  import net.battier.proxy.BookFacadeCglib;    public class TestCglib {            public static void main(String[] args) {          BookFacadeCglib cglib=new BookFacadeCglib();          BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());          bookCglib.addBook();      }  }  



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 宝宝解小便地方有湿疹怎么办 婴儿湿疹怎么办长在脸上 广州奥龙堡游泳卡过期了怎么办 大学生在学校当兵户口怎么办 茶叶梗枕头太硬怎么办 茶梗枕头太硬怎么办 照片放久了变红怎么办 乳腺萎缩和韧带松弛怎么办 航海王启航服务器爆满怎么办 LOL记分板没了怎么办 辅导孩子做作业没有耐心怎么办 宝宝住院三天回家不吃母乳怎么办 锁频君把应用变暗了怎么办 95的油加成92的怎么办 倒库一边宽了怎么办 倒库老是倒不好怎么办 倒库方向打早了怎么办 倒库左边小了怎么办 倒车入库小于30公分怎么办 倒库大于30公分怎么办 有行车记录仪遇到碰瓷怎么办 狗换了主人不吃怎么办 遇到扔东西碰瓷怎么办 碰见碰瓷的人怎么办 开店遇上碰瓷的顾客怎么办 我刮到别人的车怎么办 新车被刮了漆怎么办 停车擦到别人车怎么办 骑自行车被汽车撞了怎么办 车停在小区被刮怎么办 机动车被自行车撞了怎么办 单车撞小车后被起诉怎么办 给小车撞到电动单车怎么办 车停在路边被自行车撞怎么办 撞了碰瓷的人怎么办 谷丙转氨酶46该怎么办 渣土车开飞机了怎么办 自己车撞自己车怎么办 撞了人没钱赔怎么办 闯红灯扣了6分怎么办 开共享汽车闯红灯了怎么办