代理模式

代理模式:有时,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理可以静默的解决一些业务无关的问题,比如远程、安全、事务、日志、资源关闭等,而开发者只关心他的业务实现。比如一个复杂的业务操作可能需要好几个方法来完成,但是我们可以用一个总的方法来依次调用这几个方法即可,这个方法代理了真正的实现。

void sum(){
  int a = method1();
  String b = method2();
  ...
}

int method1(){
  // 执行业务1
  return 10;
}

String method2(int a){
  // 执行业务2
  return 'AA;
}

一般形式为一个类代表另一个类的功能。

比如老板,秘书,员工。老板比较忙,平时员工是见不到的,就把审批等请求给秘书,然后秘书再找老板处理,处理完毕后交给员工。

如新员工要转正,需要老板批准,但是老板很忙,只负责查看评语然后做出批复。但是老板的秘书可以去找新员工的直接领导了解员工表现,整理直接领导对员工的评语。然后加上评语和新员工转正申请,一起给老板。老板同意之后,给予新员工答复,同时秘书会把转正申请归档。这就可以在老板批复的动作前后增加工作,增强这个事件。

实际工作中,真正的实现类,只负责处理核心业务逻辑,然后代理类就可以在核心方法调用前后,做增强工作。比如记录日志,监控性能,安全检查等。

image.jpeg
image.png

优点:

  • 职责清晰
  • 代理对象作为中介,保护了目标对象
  • 高扩展性

组成结构:

  • 抽象角色:通过接口或抽象类声明真是角色实现的业务方法
  • 代理角色:实现抽象接口,在自己的实现方法中调用真实角色对应的方法
  • 真实角色:实现抽象接口,真正业务逻辑

代理模式又分为静态代理和动态代理
1)静态代理 :就是普通的编码,已经写好了。
2)动态代理:在运行阶段创建

在不改变原有实现的基础上,实现如下的功能

  • 日志记录
  • 权限控制
  • 事务处理

静态代理

静态代理就是我们平时写的代码,比如有一个真正实现的类A,但是客户端不去直接调用A,而是通过B这个类,然后B再调用A。比如spring MVC中,要插入一条数据。首先是控制器调用服务层,然后服务层再去调用DAO层,数据在DAO层真正插入数据库。服务层就是一个代理,并且可以在服务层做一些特殊处理,比如数据转换等。

public interface Subject {
    /**
     * 简单测试
     */
    void visit();

    /**
     * 求和
     *
     * @param a
     * @param b
     * @return 求和后的值
     */
    int sum(int a, int b);


    /**
     * 返回当前名称
     *
     * @return
     */
    String getName();

    /***
     * 问候
     * @param name
     */
    void greeting(String name);
}
public class RealSubject implements Subject {

    private String name = "真正的实现类";

    @Override
    public void visit() {
        System.out.println(name);
    }

    @Override
    public int sum(int a, int b) {
        return a + b;
    }

    @Override
    public String getName() {
        return "我是真正的实现";
    }

    @Override
    public void greeting(String name) {
        System.out.println("Hello " + name);
    }
}

public class ProxySubject implements Subject {

    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void visit() {
        this.subject.visit();
    }

    @Override
    public int sum(int a, int b) {
        return this.subject.sum(a, b);
    }

    @Override
    public String getName() {
        return this.subject.getName();
    }

    @Override
    public void greeting(String name) {
        this.subject.greeting(name);
    }
}

public class Client {

    public static void main(String[] args) {
        ProxySubject proxySubject = new ProxySubject(new RealSubject());
        proxySubject.visit();
        System.out.println(proxySubject.getName());
        proxySubject.greeting("詹姆斯");
        int result = proxySubject.sum(1, 2);
        System.out.println(result);
    }
}

动态代理

为什么要有动态代理?

  • 代理类需要有和真实实现类一样有所有的方法实现,当方法较多时,好多重复性的,机械的工作。代码相似冗余,不易维护
  • 一般一个实现类需要关联一个代理类。
  • 每当一个新的类实现接口,就需要创建对应的代理类

动态代理的本质就是在运行时 动态 的生成一个新类。前提是需要有一个接口。为什么呢?因为这个新生成的类要有和目标类一样的功能。通过接口可以保证目标类必须包含那些方法。

具体实现手段是反射,通过Method的调用和InvocationHandler

目前实现动态代理的方式有jdk Proxy和cglib,其中jdk自身实现有如下优点:

  • 亲儿子,本身就支持,不存在依赖问题和第三方版本问题
  • 代码实现简单

这二种的本质区别是,jdk需要一个接口,然后根据这个接口来在运行时-运行时-运行时动态-动态-动态的生成一个代理类出来,而cglib是创建一个目标类的子类,因此目标类的关键方法不能声明为final。

组成结构:

  • 接口对象
  • 具体实现类
  • 实现InvocationHandler的通用类
public class AnimalJdkDynamicProxy implements InvocationHandler {

    /**
     *  01.我们不确定委托类是谁?委托类的类型 是Object
     *   和委托类建立关联关系
     */
    private Object target;


    /**
     * 02.给我一个委托类,我返回一个代理类对象
     */
    public Object createProxy(Object target){
        //根据传递的参数 进行对象的关联
        this.target=target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    /**
     *
     * @param proxy :代理对象
     * @param method :方法名
     * @param args : 参数列表
     * @return
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("主人在召唤"); //系统级业务   开始事务
        Object result= method.invoke(target,args);  // 主业务
        System.out.println("主人离开"); //系统级业务     日志处理  关闭事务
        return result;
    }


    //创建测试方法
    public static void main(String[] args) {
        AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
        Animal dog= (Animal) proxy.createProxy(new Dog());
        dog.eat();
        System.out.println("**************************");
        dog.sleep();

    }
}

动态代理一般用在什么地方?主要用于各种通用框架中,平时实际项目中用到的可能不多。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容