代理模式

代理模式主要分为两种。

  1. 静态代理

  2. 动态代理

学习Spring的AOP之前,需要了解代理模式。

基于租房这一事件,抽象角色与行为。

房东、房屋中介、租客

房东和房屋中介的共同行为:出租房屋。
image

静态代理举例一

静态代理角色分析(括号内是本次举例中的角色)

  • 抽象角色:一般使用接口或者抽象类来实现。(出租房屋接口)

  • 真实角色: 被代理的角色。(房东)

  • 代理角色:代理真实角色。代理真实角色后,会做一些附属的操作。(中介)

  • 客户:使用代理角色来进行一些操作。(租客)

编写接口Rent

//房东和中介的共同行为,抽象成接口或抽象类
public interface Rent {
    void rent();//出租房屋
}

编写房东Host

public class Host implements Rent {//我是房东
    @Override
    public void rent() {
        System.out.println("房东出租房屋");
    }
}

编写房屋中介,也就是所谓的代理Proxy

public class Proxy implements Rent {//我是房屋中介
    private Host host;

    public Proxy() {
    }

    //我可以作为房东的代理,直面租客 出租房屋
    public Proxy(Host host) {
        this.host = host;
    }

    public void rent() {//中介还有其他附属操作。
        look();
        host.rent();//出租的房屋实际上是房东的
        hetong();
    }

    public void look(){
        System.out.println("看房");
    }

    public void hetong(){
        System.out.println("签合同");
    }

}

编写租客,也就是客户端Client

public class Client {//我是租客
    public static void main(String[] args) {
        Host host = new Host();//现有一个房东想出租房屋。
        Proxy proxy = new Proxy(host);//房屋中介,代理了这个房东

        proxy.rent();//租客成功租房,房屋出租
    }
}

输出结果

看房
房东出租房屋
签合同

分析

  1. 在这个过程中,租客直接接触的就是中介。

  2. 就如同现实生活中的样子,租客看不到房东。

  3. 但是租客依旧通过代理租到了房东的房子。

  4. 这就是所谓的代理模式。

静态代理举例二

  1. 编写抽象角色,增删改查用户接口
public interface UserService {
   void add();
   void delete();
   void update();
   void query();
}
  1. 编写真实角色,增删改查用户实现类
public class UserServiceImpl implements UserService {

   public void add() {
       System.out.println("增加了一个用户");
  }

   public void delete() {
       System.out.println("删除了一个用户");
  }
   //...省略改查
}
  1. 编写代理角色,实现用户的增删改查前,调用日志。
public class UserServiceProxy implements UserService {
   private UserServiceImpl userService;

   public void setUserService(UserServiceImpl userService) {
       this.userService = userService;
  }

   public void add() {
       log("add");
       userService.add();
  }

   public void delete() {
       log("delete");
       userService.delete();
  }
   //...省略改查
   public void log(String msg){
       System.out.println("执行了"+msg+"方法");
  }

}
  1. 编写测试
public class Test  {
   public static void main(String[] args) {
       //真实对象
       UserServiceImpl userService = new UserServiceImpl();
       //代理对象
       UserServiceProxy proxy = new UserServiceProxy();
       //代理类实现日志功能
       proxy.setUserService(userService);
       proxy.add();
  }
}
/*
执行了add方法
增加了一个用户
*/

静态代理的优点

  • 可以使真实角色更加纯粹。(房东)

  • 公共的业务由代理来完成,实现了业务的分工。

  • 公共业务发生扩展时变得更加集中和方便。
    image

静态代理的缺点

  • 每个代理类之间不可复用,工作量变大,开发效率降低。

动态代理

想要静态代理的好处,又不想要静态代理的缺点。所以。就有了动态代理。

需要两个类:

  1. InvocationHandler接口

  2. Proxy类

Proxy

使用Proxy类的静态方法,可以返回创建一个代理类(代理角色)的实例。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  • loader:代理角色的类加载器(代理类.class.getClassLoader())

  • interfaces:抽象角色(抽象角色.class)

  • h:调用处理程序(每个代理类的实例都有一个关联的调用处理程序,可以理解为无情的方法执行者)

InvocationHandler

接口功能唯一,调用代理实例的方法并返回结果。

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

proxy:代理实例(👆Proxy返回的东西)
method:同👆抽象角色
args:方法参数列表

基于静态代理举例一,主要修改代理角色。

编写代理角色,ProxyInvocationHandler

package com.example.Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyInvocationHandler implements InvocationHandler {//我是房屋中介
    private Rent rent;

    public ProxyInvocationHandler() {
    }

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    public Object getProxy(){
      return  Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        look();
        Object result = method.invoke(rent,args);
        hetong();
        return result;
    }

    public void look(){
        System.out.println("看房");
    }

    public void hetong(){
        System.out.println("签合同");
    }

}

编写客户端Client

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();
    }
}
/*
看房
房东出租房屋
签合同
*/

基于静态代理举例二,主要修改代理角色。

编写代理角色,ProxyInvocationHandler

public class ProxyInvocationHandler implements InvocationHandler {
   private Object target;

   public void setTarget(Object target) {
       this.target = target;
  }

   //生成代理类
   public Object getProxy(){
       return Proxy.newProxyInstance(this.getClass().getClassLoader(),
               target.getClass().getInterfaces(),this);
  }

   // proxy : 代理类
   // method : 代理类的调用处理程序的方法对象.
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       log(method.getName());
       Object result = method.invoke(target, args);
       return result;
  }

   public void log(String methodName){
       System.out.println("执行了"+methodName+"方法");
  }

}

编写测试

public class Test  {
   public static void main(String[] args) {
       //真实对象
       UserServiceImpl userService = new UserServiceImpl();
       //代理对象
       UserServiceProxy proxy = new UserServiceProxy();
       //代理类实现日志功能
       proxy.setUserService(userService);
       proxy.add();
  }
/*
执行了add方法
增加了一个用户
*/

动态代理额外的优点:

  • 一个动态代理可以代理多个类,代理的是接口!
posted @ 2024-03-18 18:54  rowbed  阅读(8)  评论(0编辑  收藏  举报