Java结构型设计模式中代理模式示例详解

 更新时间:2022年09月22日 16:25:28   作者:丨Jack_Chen丨  
代理模式(Proxy Parttern)为一个对象提供一个替身,来控制这个对象的访问,即通过代理对象来访问目标对象。本文将通过示例详细讲解一下这个模式,需要的可以参考一下

代理模式

代理模式(Proxy Pattern)属于结构型模式。

它是指为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

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

想在访问一个类时做一些控制的时候就可以是用代理模式。

分类

代理模式属于结构型模式,分为静态代理和动态代理。

1.静态代理:静态定义代理类

静态代理需要自己生成代理类

2.动态代理:动态生成代理类

动态代理不用亲自去实现,通常使用现成的API即可。目前普遍使用的是JDK自带的代理与CGLIB提供的类库。

主要角色

代理模式一般包含三种角色:

1.抽象主题角色(Subject)

抽象主题类的主要职责是声明真实主题与代理的共同接口方法,该类可以是接口也可以是抽象类

2.真实主题角色(RealSubject)

该类也被称为被代理类,该类定义了代理所表示的真实对象,是负责执行系统真正的罗辑业务对象

3.代理主题角色(Proxy)

代理主题也被称为代理类,其内部特有RealSubject的引用,因此具备完全的对RealSubject的代理权。

客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对象前后增加一些处理代码。可以理解为代码增强,实际上就是在原代码罗辑前后增加一些代码逻辑,而使调用者无感知。

作用

1.保护目标对象,将代理对象与真实被调用目标对象分离

2.增强目标对象

3.降低系统耦合性,提升扩展性

静态代理与动态代理的区别

1.静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则。

2.动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原测。

3.若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无须修改代理类的代码。

静态代理的基本使用

静态代理需要自己生成代理类

创建抽象主题

public interface ISubject {
    /**
     * 买票
     */
    void buyTickets();
}

创建真实主题

public class RealSubject implements ISubject {
    public void buyTickets() {
        System.out.println("进行买票操作");
    }
}

创建代理主题

public class Proxy implements ISubject {
    private ISubject subject;
    public Proxy(ISubject subject) {
        this.subject = subject;
    }
    public void buyTickets() {
        before();
        subject.buyTickets();
        after();
    }
    public void before() {
        System.out.println("买票前的操作");
    }
    public void after() {
        System.out.println("买票后的操作");
    }
}

客户端调用

    public static void main(String[] args) {
        Proxy proxy = new Proxy(new RealSubject());
        proxy.buyTickets();
    }

买票前的操作
进行买票操作
买票后的操作

JDK动态代理的基本使用

创建抽象主题

public interface IUser {
    /**
     * 购物
     */
    void shopping();
}

创建真实主题

public class User implements IUser{
    @Override
    public void shopping() {
        System.out.println("user shopping....");
    }
}

创建代理主题

public class JDKProxy implements InvocationHandler {
    private Object tarjet;
    public JDKProxy(Object tarjet) {
        this.tarjet = tarjet;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理user,执行shopping()开始...");
        Object oj = method.invoke(tarjet, args);
        System.out.println("代理user,执行shopping()结束...");
        return oj;
    }
}

客户端调用

    public static void main(String[] args) {
        User user = new User();
        JDKProxy jdkProxy = new JDKProxy(user);
        IUser proxyInstance = (IUser) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), jdkProxy);
        proxyInstance.shopping();
    }

代理user,执行shopping()开始...
user shopping....
代理user,执行shopping()结束...

小优化

在调用时候,传入了一推参数,可进一步优化

public class JDKProxy implements InvocationHandler {
    private Object tarjet;
    public Object JDKProxy(Object target){
        this.tarjet = target;
        Class<?> clazz =  target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理user,执行shopping()开始...");
        Object oj = method.invoke(tarjet, args);
        System.out.println("代理user,执行shopping()结束...");
        return oj;
    }
}
    public static void main(String[] args) {
        JDKProxy jdkProxy = new JDKProxy();
        IUser user= (IUser)jdkProxy.JDKProxy(new User());
        user.shopping();
    }

CGLIB动态代理的基本使用

CGLIB动态代理也不需要生成代理类,实现MethodInterceptor 就可以了。

注意:CGLib不能代理final的方法

创建抽象主题

注意:CGLb代理的目标对象不需要实现任何接口,就可以通过动态继承目标对象实现动态代理。所以此处可以不用创建接口。直接使用真实主题。

public interface IUser {
    public void shopping();
}

创建真实主题

public class User implements IUser{
    @Override
    public void shopping() {
        System.out.println("user shopping....");
    }
}

直接使用真实主题。

public class User {
    public void shopping() {
        System.out.println("user shopping....");
    }
}

创建代理主题

public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理user,执行shopping()开始...");
        Object invokeSuper = methodProxy.invokeSuper(o, objects);
        System.out.println("代理user,执行shopping()结束...");
        return invokeSuper;
    }
}

客户端调用

    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(User.class);
        enhancer.setCallback(cglibProxy);
        IUser iUser = (IUser) enhancer.create();
        iUser.shopping();
    }

小优化

在客户端调用时,稍显复杂,可进一步优化

public class CglibProxy implements MethodInterceptor {
    public Object getInstance(Class<?> clazz) throws Exception{
        //相当于Proxy,代理工具类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理user,执行shopping()开始...");
        Object invokeSuper = methodProxy.invokeSuper(o, objects);
        System.out.println("代理user,执行shopping()结束...");
        return invokeSuper;
    }
}
    public static void main(String[] args) throws Exception {
        IUser user = (IUser) new CglibProxy().getInstance(User.class);
        user.shopping();
    }

CGLIB与JDK动态代理区别

1.执行条件

JDK动态代理实现了被代理对象的接口。CGLb代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现动态代理。

2.实现机制

JDK动态代理调用代理方法是由Java内部的反射机制来实现的,需要读取接口信息。CGLib动态代理是通过FastClass机制来实现的,需要覆盖父类方法。

3.性能

首先都在运行期生成字节码。

JDK动态代理的代理逻辑简单,直接写Class字节码,使用反射机制在生成类的过程中比较高效。

CGLib代理实现更复杂,使用ASM框架写Class字节码,但是asm在生成类之后的相关执行过程中比较高效。但是可以通过将asm生成的类进行缓存,解决asm生成类过程低效问题。

一句话:CGLib生成代理类比JDK动态代理效率低,但是执行效率比JDK动态代理高。

到此这篇关于Java结构型设计模式中代理模式示例详解的文章就介绍到这了,更多相关Java代理模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java复习之集合框架总结

    Java复习之集合框架总结

    本篇文章主要介绍了Java复习之集合框架总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • 使用Java将字符串在ISO-8859-1和UTF-8之间相互转换

    使用Java将字符串在ISO-8859-1和UTF-8之间相互转换

    大家都知道在一些情况下,我们需要特殊的编码格式,如:UTF-8,但是系统默认的编码为ISO-8859-1,遇到这个问题,该如何对字符串进行两个编码的转换呢,下面小编给大家分享下java中如何在ISO-8859-1和UTF-8之间相互转换,感兴趣的朋友一起看看吧
    2021-12-12
  • SpringBoot短链接跳转的代码实现

    SpringBoot短链接跳转的代码实现

    短链跳转是一种通过将长链接转换为短链接的方式,以便在互联网上进行链接共享和传播的技术,短链将原始长链接通过特定算法转换为较短的链接,使得它更容易分享、传播和展示,本文给大家介绍了SpringBoot短链接跳转的代码实现,需要的朋友可以参考下
    2024-03-03
  • Mybatis-Plus3.x的创建步骤及使用教程

    Mybatis-Plus3.x的创建步骤及使用教程

    MyBatis-Plus是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为 简化开发、提高效率而生,这篇文章主要介绍了Mybatis-Plus3.x的使用,需要的朋友可以参考下
    2023-10-10
  • java编程FinalReference与Finalizer原理示例详解

    java编程FinalReference与Finalizer原理示例详解

    这篇文章主要为大家介绍了java编程FinalReference与Finalizer的核心原理以及示例源码的分析详解,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-01-01
  • Java 解析线程的几种状态详解

    Java 解析线程的几种状态详解

    这篇文章主要为大家详细介绍了Java 解析线程的几种状态,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • SpringBoot SSO轻松实现(附demo)

    SpringBoot SSO轻松实现(附demo)

    这篇文章主要介绍了SpringBoot SSO轻松实现(附demo),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • java获取当前日期使用实例

    java获取当前日期使用实例

    本文为大家介绍java日期的使用实例,日期输出格式为date:2013-12-17,大家参考使用吧
    2014-01-01
  • IDEA如何配置本地tomcat启动项目

    IDEA如何配置本地tomcat启动项目

    这篇文章主要介绍了IDEA如何配置本地tomcat启动项目问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • java生成饼图svg及JFreeChart生成svg图表

    java生成饼图svg及JFreeChart生成svg图表

    java生成饼图svg,代码实现感觉有点复杂,个人认为不如用JFreeChart,这篇文章主要介绍java生成饼图svg及JFreeChart生成svg图表,有需要的小伙伴可以参考下
    2015-08-08

最新评论