java设计模式6——代理模式

时间:2021-04-13 23:04:43

java设计模式6——代理模式

1、代理模式介绍:

1.1、为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc)

1.2、代理模式的分类:

  • 静态代理
  • 动态代理

1.3、代理模式关系图(以租房子为例)

java设计模式6——代理模式

2、静态代理

2.1、角色分析:

  1. 抽象角色:一般会使用接口或者抽象类来解决
  2. 真实角色:被代理的角色
  3. 代理客户:代理真实角色。代理真实角色后,我们一般会做一些附属的操作
  4. 客户:访问代理对象的人

2.2、例1(租房子演示)

2.2.1、抽象角色实现(房东抽象类)

package com.xgp.company.结构性模式.代理模式.静态代理.Demo1;

/**
  • 租房

    */

    public interface Rent { void rent();

    }

2.2.2、真实角色实现(房东的实现类)

package com.xgp.company.结构性模式.代理模式.静态代理.Demo1;

/**
  • 房东

    */

    public class Host implements Rent { @Override

    public void rent() {

    System.out.println("房东要出租房子!");

    }

    }

2.2.3、不通过代理进行租房

package com.xgp.company.结构性模式.代理模式.静态代理.Demo1;

public class Client {

public static void main(String[] args) {

//直接找房东

Host host = new Host();

host.rent();

}

}

运行结果:

房东要出租房子!

2.2.4、弊端

  1. 在实际的生活场景中,房东可能只卖房,而不负责租房
  2. 如果要提高租房子的成功率,必然还需要一些其他的服务,如:看房、谈价钱、签合同等,而这些房东并不想参与,因此产生了中介,也就是代理。

2.2.5、代理出现

package com.xgp.company.结构性模式.代理模式.静态代理.Demo1;

public class Proxy implements Rent {
private Host host;

public Proxy() {

}

public Proxy(Host host) {
this.host = host;
} @Override
public void rent() {
seeHouse();
host.rent();
fare();
hetong();
} //看房
public void seeHouse() {
System.out.println("中介代理看房");
} //收中介费
public void fare() {
System.out.println("收中介费");
} public void hetong() {
System.out.println("签合同");
}

}

2.2.6、通过代理来租房子

package com.xgp.company.结构性模式.代理模式.静态代理.Demo1;

public class Client {

public static void main(String[] args) {

/* //直接找房东

Host host = new Host();

host.rent();*/
    //通过代理去租房子
//房东要租房子
Host host = new Host();
//代理
//中介帮房东租房子,但是,中介一半都会有一些附属操作
Proxy proxy = new Proxy(host);
proxy.rent();
}

}

运行结果:

中介代理看房
房东要出租房子!
收中介费
签合同

2.3、分析:

代理模式的好处

  1. 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务。
  2. 公共的业务交给了代理角色去完成,实现了业务的分工。
  3. 公共业务发生扩展的时候,方便集中管理。

缺点:

  • 一个真实角色就会产生一个代理的角色,代码量翻倍,开发效率变低。

2.4、例1(增删改查业务的演示)

2.4.1、编写service的抽象类

package com.xgp.company.结构性模式.代理模式.静态代理.Demo2;

public interface UserService {

void add();

void del();

void update();

void query();

}

2.4.2、编写service的实现类

package com.xgp.company.结构性模式.代理模式.静态代理.Demo2;

/**
  • 真实对象

    */

    public class UserServiceImpl implements UserService {

    @Override

    public void add() {

    System.out.println("增加了一个用户");

    } @Override

    public void del() {

    System.out.println("删除了一个用户");

    } @Override

    public void update() {

    System.out.println("更新了一个用户");

    } @Override

    public void query() {

    System.out.println("查询了一个用户");

    }

    }

2.4.3、如果此时想不改变原有的代码,而增加方法日志打印的功能,此时代理诞生

package com.xgp.company.结构性模式.代理模式.静态代理.Demo2;

/**
  • 代理角色,增加日志的功能

    */

    public class UserServiceProxy implements UserService { private UserService userService; public void setUserService(UserService userService) {

    this.userService = userService;

    } @Override

    public void add() {

    userService.add();

    log("add");

    } @Override

    public void del() {

    userService.del();

    log("del");

    } @Override

    public void update() {

    userService.update();

    log("update");

    } @Override

    public void query() {

    userService.query();

    log("query");

    } public void log(String msg) {

    System.out.println("使用了" + msg + "方法!");

    }

    }

2.4.4、编写客户端类,使用service的方法,并打印日志

package com.xgp.company.结构性模式.代理模式.静态代理.Demo2;

public class Client {

public static void main(String[] args) {

UserService userService = new UserServiceImpl();
    UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService); proxy.add();
proxy.del();
proxy.update();
proxy.query();
}

}

运行结果:

增加了一个用户
使用了add方法!
删除了一个用户
使用了del方法!
更新了一个用户
使用了update方法!
查询了一个用户
使用了query方法!

2.5、反思:在实际的业务中,横向开发的好处

java设计模式6——代理模式

可以保证原有代码的可行性,不会对软件原来的版本进行破坏,向过去兼容。

3、动态代理

3.1、动态代理的特点

  1. 动态代理和静态代理一样
  2. 动态代理的代理类是动态生成的,不是我们直接写的

3.2、动态代理实现方式的分类

  • 基于接口——JDK动态代理(本节需要讲述的方式)
  • 基于类:cglib
  • java字节码实现:javasist

3.3、需要了解的两个类

  • Proxy:代理类
  • InvocationHandler:调用处理程序类

具体的使用参照java的api

3.4、例1——房东租房的例子

3.4.1、房东的接口和实现类不变

3.4.2、实现自动生成代理的类

package com.xgp.company.结构性模式.代理模式.动态代理.Demo1;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy; /**
  • 等下我们会用这个类,自动生成代理类

    */

    public class ProxyInvocationHandler implements InvocationHandler { /**
    • 被代理的接口

      */

      private Rent rent;
    public void setRent(Rent rent) {

    this.rent = rent;

    } /**
    • 获得一个代理
    • @return

      */

      public Object getProxy() {

      return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);

      }
    /**
    • 处理代理实例,并放回结果
    • @param proxy
    • @param method
    • @param args
    • @return
    • @throws Throwable

      */

      @Override

      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      //用反射来执行方法

      seeHouse(); //调用代理自身的方法

      //动态代理的本质,就是使用反射机制来实现

      Object result = method.invoke(rent, args);

      fare();

      hetong();

      return result;

      }
    public void seeHouse() {

    System.out.println("中介带看房子!");

    } //收中介费

    public void fare() {

    System.out.println("收中介费");

    } public void hetong() {

    System.out.println("签合同");

    }

    }

3.4.3、实现客户端使用代理来租房子

package com.xgp.company.结构性模式.代理模式.动态代理.Demo1;

public class Client {

public static void main(String[] args) {

//真实角色

Host host = new Host();
    //代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色,来处理我们要调用的接口对象
pih.setRent(host); //放回代理类
//这里的代理类就是动态生成的,我们并没有写他
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}

}

运行结果:

中介带看房子!
房东要出租房子!
收中介费
签合同

3.5、例2——增删改查的例子

3.5.1、先来实现一个万能的代理生成类

package com.xgp.company.结构性模式.代理模式.动态代理.Demo2;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy; /**
  • 万能代理类

    */

    public class ProxyInvocationHandler implements InvocationHandler { /**
    • 被代理的接口

      */

      private Object target;
    public void setTarget(Object target) {

    this.target = target;

    } /**
    • 获得一个代理
    • @return

      */

      public Object getProxy() {

      return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);

      }
    /**
    • 处理代理实例,并放回结果
    • @param proxy
    • @param method
    • @param args
    • @return
    • @throws Throwable

      */

      @Override

      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      //用反射来执行方法

      //动态代理的本质,就是使用反射机制来实现

      Object result = method.invoke(target, args);

      log(method.getName());

      return result;

      }
    public void log(String msg) {

    System.out.println("使用了" + msg + "方法!");

    }

    }

也就是将上一个例子的具体实现类——房东类,替换成Object类

3.5.2、UserService的接口和实现类不变

3.5.3、编写客户端类使用代理调用增删改查方法

package com.xgp.company.结构性模式.代理模式.动态代理.Demo2;

public class Client {

public static void main(String[] args) {

//真实角色

UserService userService = new UserServiceImpl();

//代理角色

ProxyInvocationHandler pih = new ProxyInvocationHandler();

pih.setTarget(userService);

UserService proxy = (UserService) pih.getProxy();
    proxy.add();
proxy.del();
proxy.update();
proxy.query();
}

}

运行结果:

增加了一个用户
使用了add方法!
删除了一个用户
使用了del方法!
更新了一个用户
使用了update方法!
查询了一个用户
使用了query方法!

3.6、弊端分析:

虽然使用动态代理能够节省代码量,并且实现静态代理的全部优点。但是,动态代理的核心是反射技术,通过反射技术调用方法效率较大,因此也可能影响系统效率。

3.7、动态代理的好处

一个动态代理的是一个接口,一般就是对应的一类业务。