代理模式

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

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();

    }
}

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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容