Java23种设计模式之代理模式的学习

源码链接(Gitee码云):https://gitee.com/oldou/javadesignpatterns
这里有我整理好的Java23种设计模式的源码以及博客教程,博客教程中介绍了Java23种设计的模式的各种实现方式以及应用场景,非常适用于学习以及提高我们的设计思维,如果对大家有所帮助,请记得star一下给予作者一定的精神支持,你的star是我写出更好的博客的动力,谢谢大家。

代理模式(proxy pattern)

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

核心作用:

  • 通过代理,控制对对象的访问!
  • 可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。(即:AOP的微观实现!)
  • 实现将统一流程代码放到代理类中处理。

AOP(Aspect Oriented Programming)面向切面编程)的核心实现机制!

核心角色

  • 抽象角色
    – 定义代理角色和真实角色的公共对外方法

  • 真实角色
    – 实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
    – 关注真正的业务逻辑!

  • 代理角色
    – 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
    – 将统一的流程控制放到代理角色中处理

应用场景

  • 安全代理:屏蔽对真实角色的直接访问。
  • 远程代理:通过代理类处理远程方法调用(RMI)
  • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
  • 比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。

分类

(1)静态代理(静态定义代理类)

(2)动态代理(动态生成代理类)

  • JDK自带的动态代理
  • javaassist字节码操作库实现动态代理
  • CGLIB动态代理
  • ASM(底层使用指令,可维护性较差)

本文只介绍静态代理模式和JDK动态代理模式的使用。

案例

比如说,我们以生活中的租房为案例,我们租房一般都是找的中介,而中介就是一个代理的角色,他代表房东帮助他把房子租出去,房东只需要提供租房即可,无需提供其他的操作,而房东和中介的最终都是要将房子租出去,因此他们有一个共同的接口,下面我们用代码来实现一下。
在这里插入图片描述

静态代理模式

提供租房的接口:

/**
 * 租房
 */
public interface Rent {
    
    
    public void rent();//租房这个事情
}

真实的角色:房东

/**
 * 真实的角色:房东
 * 需要将房子租出去
 */
public class FangD implements Rent {
    
    
    @Override
    public void rent() {
    
    
        System.out.println("出租XXX房,两室一厅....");
    }
}

代理角色:中介

/**
 * 代理角色:中介
 * 需要代理房东去租房,因此也要实现租房的方法
 */
public class Proxy implements Rent {
    
    
    private FangD fd;
    //提供一个房东的有参构造
    public Proxy(FangD fd) {
    
    
        this.fd = fd;
    }
    public Proxy(){
    
    }

    @Override
    public void rent() {
    
    
        seeHouse(); //中介这里扩展的操作  看房
        fd.rent();//调用房东的租房方法
        hetong();//签合同
        fare();//收取中介费
    }

    //看房
    public void seeHouse(){
    
    
        System.out.println("中介带你看房..");
    }
    //收中介费
    public void fare(){
    
    
        System.out.println("中介收你中介费100元..");
    }
    //签合同
    public void hetong(){
    
    
        System.out.println("签合同...");
    }
}

客户:租房的人

/** 静态代理模式
 * 真实的人:需要租房的人--客户
 */
public class Client {
    
    

    public static void main(String[] args) {
    
    
        //房东要出租房子
        FangD fd = new FangD();

        //代理,中介要帮房东出租房子,并且中介还可以做一些附属操作
        Proxy proxy = new Proxy(fd);

        //不用找房东,直接找中介租房
        proxy.rent();
    }

}

运行输出:
在这里插入图片描述

静态代理模式在业务中的实现

我们平时使用代理模式时,加入需要给我们执行的业务增添一个日志的功能,而在公司中改动源码是大忌,因此我们需要将日志给添加进行就可以通过代理模式进行横向扩展,如下所示:

业务接口:

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

业务接口的实现类:

public class UserServiceImpl implements UserService {
    
    
    @Override
    public void add() {
    
    
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
    
    
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
    
    
        System.out.println("修改了一个用户");
    }

    @Override
    public void query() {
    
    
        System.out.println("查询了一个用户");
    }
}

业务代理(实现增加日志功能):

/**
 * 代理类,我们在此添加一个日志的功能
 */
public class UserServiceProxy implements UserService {
    
    
    private UserServiceImpl userService;
    public void setUserService(UserServiceImpl userService) {
    
    
        this.userService = 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();
    }
    public void log(String msg){
    
    
        System.out.println("[Debug]-->使用了"+msg+"方法");
    }

}

测试:

public class Client {
    
    
    public static void main(String[] args) {
    
    
        UserServiceImpl userService = new UserServiceImpl();

        UserServiceProxy proxy = new UserServiceProxy();
		
        proxy.setUserService(userService);

        proxy.add();
    }

}

输出:
在这里插入图片描述

静态代理的优点

  • 房东只需要找到一个代理出租房子,其他操作就交给代理对象了
  • 这样可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务交给代理角色,实现业务分工
  • 公共业务发生扩展的时候,方便集中管理
  • 我们在不改变原有业务的基础上,给项目扩展一些功能,代理模式就非常适合,相当于横向开发
    在这里插入图片描述

缺点

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

动态代理模式

  • 动态代理和静态代理角色一样;
  • 动态代理的代理类是动态生成的,不是我们直接写好的;
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理;

动态代理模式的实现方式主要有以下:

基于接口:JDK动态代理
基于接口:java字节码实现动态代理(javasist)

基于类:CGlib动态代理

ASM(底层使用指令,可维护性较差)

动态代理的优点(相比于静态代理)
抽象角色中(接口)声明的所以方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

下面逐一介绍。

JDK实现动态代理

JDK的动态代理有两个核心的类:Proxy和InvocationHandler

  • java.lang.reflect.Proxy: 作用:动态生成代理类和对象

  • java.lang.reflect.InvocationHandler(处理器接口)
    • 可以通过invoke方法实现对真实角色的代理访问。
    • 每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象

下面我们还是以租房为案例,来实现以下JDK的动态代理。

代码测试

租房的接口:

/**
 * 租房
 */
public interface Rent {
    
    
    public void rent();//租房这个事情
}

真实的角色:房东

/**
 * 真实的角色:房东
 * 需要将房子租出去
 */
public class FangD implements Rent {
    
    
    @Override
    public void rent() {
    
    
        System.out.println("出租XXX房,两室一厅....");
    }
}

自动生成代理类的类(重点):

/**
 * 这个类会自动的生成代理类
 */
public class ProxyInvocationHandler implements InvocationHandler {
    
    
    //被代理的接口
    private Rent rent;
    public void setRent(Rent rent) {
    
    
        this.rent = rent;
    }

    //生成得到代理类
    //newProxyInstance:返回代理类的一个实例
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }


    //处理代理实例并返回结果
    //proxy指的是代理类
    //method指的是被代理的方法
    //args指的是该方法的参数对象
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //动态代理的本质就是使用反射机制实现的
        seeHouse(); //扩展操作
        Object result = method.invoke(rent, args);
        writeHeTong();//扩展操作
        return result;
    }

    //扩展操作
    public void seeHouse(){
    
    
        System.out.println("中介带你看房子");
    }
    public void writeHeTong(){
    
    
        System.out.println("签署合同");
    }
}

客户端:

/**
 * 客户
 */
public class Client {
    
    

    public static void main(String[] args) {
    
    
        //真实角色
        FangD fd = new FangD();

        //代理角色:现在是没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(fd);
        //下面的proxy就是动态生成的代理对象
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();

    }

}

测试输出:
在这里插入图片描述

总结
动态代理步骤:
1、创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2、创建被代理的类以及接口
3、通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理类
4、通过代理调用方法

从JDK的动态代理实现可以看出,实际上就是利用了反射的机制。
首先通过实现 InvocationHandler 接口创建自己的调用处理器,然后通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类,之后再通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型,最后通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

JDK动态代理在业务中的实现

我们还是以上面给业务添加日志的功能为例,同时将自己的调用处理器代码做一定的修改,变成一个相当于工具类的形式,如下所示:
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);
    }


    //处理代理实例并返回结果
    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 msg){
    
    
        System.out.println("[Debug]--->执行了"+msg+"方法");
    }

}

客户端:

public class Client {
    
    

    public static void main(String[] args) {
    
    
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();

        //代理角色  不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        //设置要代理的对象
        pih.setTarget(userService);

        //动态的为其生成代理对象
        UserService proxy = (UserService)pih.getProxy();

        proxy.add();
    }

}

输出:
在这里插入图片描述

开发框架中的应用场景

  • struts2中拦截器的实现
  • 数据库连接池关闭处理
  • Hibernate中延时加载的实现
  • mybatis中实现拦截器插件
  • AspectJ的实现
  • spring中AOP的实现
    • 日志拦截
    • 声明式事务处理
  • web service
  • RMI远程方法调用
  • 实际上,随便选择一个技术框架都会用到代理模式

猜你喜欢

转载自blog.csdn.net/weixin_43246215/article/details/108599790