代理模式

概述

定义

  • 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

优点

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

缺点

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

分类

  • 静态代理
  • 动态代理

静态代理

案例A

我们来分析租房,首先房东可能手里有很多的房子可以出租,但是他不想自己亲自打广告,接待访客,他只想给房子然后拿钱,那么他就应该去找房屋中介,对于我们的房客而言,我们将不在直接接触房东,我们只能找到中介,然后中介再找房东

那么结果这么分析,我们可以分析出以下几个角色

  • 抽象角色:一般会使用接口或者抽象类
  • 真实角色(房东):被代理的角色
  • 代理角色(中介):代理真实角色,通常我们会在这个过程中新增一些附属操作,如签合同,看房子,拿提成
  • 客户(房客):寻找代理对象的人

那么我们开始代码实现,房东可以做很多事情,如卖房子,租房子,所以出租这个事情应该定义为接口让房东自己去实现

//租房
public interface Rent {
    void rent();
}

然后定义房东

public class Host implements Rent{
    @Override
    public void rent() {
        System.out.print("房东要出租房子!");
    }
}

然后定义代理对象,首先他应该也有租房这一个方法,而中介的租房能力是来自房东的,且中介在租房这个过程中应该还需要联系房东,他不能直接去继承房东,我们应该用组合的思想,将房东交给中介,而且中介还会做一些其他附属操作

public class Proxy implements Rent {
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    public void seeHouse() {
        System.out.println("中介带你去看房");
    }

    @Override
    public void rent() {
        host.rent();
        System.out.println("来自中介");
    }
	
    public void pare() {
        System.out.println("中介收取租金");
    }
}

模拟房客找中介租房

public static void main(String[] args) {
    Host host = new Host();
    Proxy proxy = new Proxy(host);
    proxy.seeHouse();
    proxy.pare();
    proxy.rent();
}

代理模式

案例B

对于我们经常写的Service,其中实现了若干的方法,当我们现在需要增加日志功能,在每一个方法前面打印一行语句,按照以往的经验,我们就需要去手动在每一个方法前面再新增一行日志语句,但是这样很明显违背了我们的开闭原则,更好的解决方案就是使用代理,这也是面向切面编程的Spring AOP的核心思想

代码实现,首先是接口类

public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}

然后是对接口进行实现

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("add");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void query() {
        System.out.println("query");
    }
}

调用

代理模式

现在需要在每一行语句前面新增一个日志,就不用一行一行去加了,新建一个代理

@Data
public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;

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

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

    private void log(String logName){
        System.out.println("使用了"+logName+"方法");
    }
}

调用

public static void main(String[] args) {
    UserServiceImpl userService = new UserServiceImpl();
    UserServiceProxy userServiceProxy = new UserServiceProxy();
    userServiceProxy.setUserService(userService);
    userServiceProxy.add();
    userServiceProxy.delete();
    userServiceProxy.update();
    userServiceProxy.query();
}

代理模式

动态代理

对于静态代理而言,我们没出现一个被代理都想,都得去新建一个代理,这样代码量就会翻倍,开发效率变低,那么我们就使用动态代理去解决这个问题,我们可以利用反射动态的去管理对象,也就是动态代理类是动态生成的,而不是直接写好的

对于动态代理分为两大类

  • 基于接口:JDK的原生动态代理
  • 基于类:cglib
  • 基于Java字节码:javasist

基于接口

需要了解两个类

  • proxy(代理):提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
  • InvocationHandler(调用处理程序):是由代理实例的调用处理程序实现的接口。每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

新增一个InvocationHandlerProxy对象,我们需要用它来帮我们动态生成代理类

@Data
public class InvocationHandlerProxy implements InvocationHandler {
    //被代理的接口
    private 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 {
        //动态代理的本质就是使用反射机制实现
        return method.invoke(rent, args);
    }
}

调用

public static void main(String[] args) {
    Host host = new Host();
    //代理角色,现在没有
    InvocationHandlerProxy invocationHandlerProxy = new InvocationHandlerProxy();
    //通过调用程序处理角色来处理我们要调用的接口对象
    invocationHandlerProxy.setRent(host);
    //这里就是动态生成的代理
    Rent rent = (Rent) invocationHandlerProxy.getProxy();
    rent.rent();
}

代理模式

现在中介需要做一些附加操作,就直接放在调用处理程序中即可

@Data
public class InvocationHandlerProxy implements InvocationHandler {
    //被代理的接口
    private 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 {
        seeHouse();
        //动态代理的本质就是使用反射机制实现
        Object o = method.invoke(rent, args);
        System.out.println("来自中介");
        pare();
        return o;
    }

    public void seeHouse() {
        System.out.println("中介带你看房子");
    }

    public void pare() {
        System.out.println("收取中介费");
    }
}

代理模式

现在想要给静态代理的案例B也应用这个,我们就还需要修改InvocationHandlerProxy类,这个修改无关紧要,因为他与我们程序本身业务没有关联,可以做到无侵入式编程,为了这个类更加的同样,我们可以把那个代理接口直接编程object类,修改后代码如下

/**
 * @author PengHuAnZhi
 * @createTime 2021/3/7 17:42
 * @projectName DesignPrinciples
 * @className InvocationHandlerProxy.java
 * @description TODO
 */
@Data
public class InvocationHandlerProxy implements InvocationHandler {
    //被代理的接口
    private Object object;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        //动态代理的本质就是使用反射机制实现
        return method.invoke(object, args);
    }

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

再次测试

public static void main(String[] args) {
    UserServiceImpl userService = new UserServiceImpl();
    InvocationHandlerProxy invocationHandlerProxy = new InvocationHandlerProxy();
    invocationHandlerProxy.setObject(userService);
    UserService userServiceProxy = (UserService) invocationHandlerProxy.getProxy();
    userServiceProxy.add();
    userServiceProxy.delete();
    userServiceProxy.update();
    userServiceProxy.query();
}

代理模式

动态代理代理的就是一个接口,接口下的一系列业务都会被代理,也就是一个动态代理类可以代理很多的类,可以不用再一对一的手动实现代理类

上一篇:Spring基础入门---【静态/动态代理模式】---超细节!


下一篇:代理模式(静态代理)