10、【代理模式】通过引入一个代理对象来控制对原始对象的访问的方式。

devtools/2024/9/23 23:12:10/

你好,我是程序员雪球。

 

今天我们了解代理模式的原理、静态代理和动态代理的区别、Spring AOP 和动态代理的关系、代理模式的使用场景,以及用 Java 实现一个动态代理示例

 684f0c326cc94a1d9960930ae42d5419.png

 

一、代理模式的原理

 

代理模式是一种设计模式,它提供了一种通过引入一个代理对象来控制对原始对象的访问的方式。代理对象在客户端和原始对象之间充当中间人,它可以在调用原始对象的方法之前或之后执行额外的操作,如日志记录、缓存、权限检查等。

 

在代理模式中,有三个主要角色:

 

1. 抽象主题(Subject):定义了原始对象和代理对象的公共接口,包含了原始对象的所有方法。

2. 具体主题(ConcreteSubject):实现了抽象主题接口,是实际的业务逻辑对象。

3. 代理(Proxy):实现了抽象主题接口,它持有一个具体主题对象的引用,并在调用具体主题的方法时可以添加额外的逻辑。

 

代理对象通常通过组合或继承的方式来实现对原始对象的包装。在调用代理对象的方法时,它会将请求转发给原始对象,并可以在调用前后执行自定义的逻辑。

 

二、静态代理和动态代理的区别

 

静态代理是在编译时创建的代理类,它通过继承原始对象的类或实现其接口,并在其中重写需要代理的方法来实现。静态代理在运行时会生成一个新的类,因此它的性能可能会受到一定的影响。

 

动态代理是在运行时动态创建的代理类,它通过使用反射机制在运行时生成代理对象的字节码,并将其加载到 JVM 中。动态代理通常使用 Java 的反射 API 和动态字节码生成技术来实现,如 Java 的 Proxy 类或 CGLIB 库。动态代理的优点是不需要在编译时生成代理类,因此可以更加灵活地处理各种代理需求。

 

三、Spring AOP 和动态代理的关系

 

Spring AOP(Aspect-Oriented Programming,面向方面编程)是 Spring 框架提供的一种强大的横切关注点技术,它基于动态代理实现。通过 Spring AOP,我们可以在不修改原始代码的情况下,将横切关注点(如日志记录、事务管理、权限检查等)编织到业务逻辑代码中。

 

Spring AOP 使用了动态代理来创建代理对象,并在调用原始对象的方法时自动执行横切逻辑。Spring AOP 支持两种类型的动态代理:基于 JDK 的动态代理和基于 CGLIB 的动态代理。基于 JDK 的动态代理要求被代理的对象必须实现至少一个接口,而基于 CGLIB 的动态代理则不需要接口。

 

在 Spring AOP 中,我们可以通过配置 XML 文件或使用注解来定义切入点(Pointcut)和横切逻辑(Advice),Spring 会在运行时根据切入点和横切逻辑自动创建代理对象,并将其注入到应用的上下文中。

 

四、代理模式的使用场景

 

代理模式在以下场景中非常有用:

 

1. 远程调用(RPC):在分布式系统中,代理对象可以作为远程服务的本地代表,将远程方法调用转换为本地调用,并处理网络通信和序列化等细节。

2. 缓存:通过使用代理对象,可以在调用原始对象的方法之前或之后检查缓存中是否已经存在结果,并返回缓存中的结果,以提高系统性能。

3. 权限检查:代理对象可以在调用原始对象的方法之前进行权限检查,只有具备足够权限的用户才能访问受保护的资源。

4. 日志记录:通过代理对象,可以在调用原始对象的方法之前或之后记录日志信息,以便跟踪和审计系统的操作。

5. 事务管理:代理对象可以在调用原始对象的方法之前开始事务,在方法成功执行后提交事务,或者在发生异常时回滚事务,以保证数据的一致性。

 

五、用 Java 实现一个动态代理示例

 

以下是一个简单的 Java 动态代理示例,使用了 JDK 的动态代理机制来创建一个代理对象,它会在调用原始对象的方法前后打印日志信息:

 

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

 

public class DynamicProxyExample {

 

    public static void main(String[] args) {

        // 创建一个原始对象

        HelloWorldImpl helloWorldImpl = new HelloWorldImpl();

 

        // 创建一个动态代理对象

        HelloWorldProxy helloWorldProxy = new HelloWorldProxy(helloWorldImpl);

 

        // 使用代理对象执行方法

        helloWorldProxy.sayHello();

    }

 

    // 原始对象的接口

    interface HelloWorld {

        void sayHello();

    }

 

    // 原始对象的实现类

    static class HelloWorldImpl implements HelloWorld {

        @Override

        public void sayHello() {

            System.out.println("Hello, World!");

        }

    }

 

    // 动态代理类

    static class HelloWorldProxy implements InvocationHandler {

        private HelloWorld helloWorld;

 

        public HelloWorldProxy(HelloWorld helloWorld) {

            this.helloWorld = helloWorld;

        }

 

        @Override

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

            // 在调用方法之前打印日志信息

            System.out.println("Before method invocation: " + method.getName());

 

            // 调用原始对象的方法

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

 

            // 在调用方法之后打印日志信息

            System.out.println("After method invocation: " + method.getName());

 

            return result;

        }

    }

 

    // 使用 JDK 的动态代理机制创建代理对象

    public static Object createProxy(Object target, Class<?>[] interfaces) {

        return Proxy.newProxyInstance(

                target.getClass().getClassLoader(),

                interfaces,

                new HelloWorldProxy((HelloWorld) target)

        );

    }

}

 

 

在上述示例中,我们定义了一个原始对象  HelloWorldImpl ,它实现了  HelloWorld  接口。然后,我们创建了一个  HelloWorldProxy  类,它实现了  InvocationHandler  接口,用于处理代理对象的方法调用。在  invoke()  方法中,我们可以在调用原始对象的方法前后执行自定义的逻辑,如打印日志信息。

 

在  main()  方法中,我们创建了一个原始对象  helloWorldImpl ,并使用  createProxy()  方法创建了一个代理对象  helloWorldProxy 。最后,我们使用代理对象执行  sayHello()  方法,并观察控制台输出的日志信息。

 

请注意,使用动态代理需要导入  java.lang.reflect.Proxy  类。

 

总结

我们来总结一下今天学习的内容:

一、代理模式的原理:

代理模式是一种设计模式,它提供了一种通过引入一个代理对象来控制对原始对象的访问的方式。

二、静态代理与动态代理的区别:

静态代理是在编译时创建的代理类,它通过继承原始对象的类或实现其接口,并在其中重写需要代理的方法来实现。

动态代理是在运行时动态创建的代理类,它通过使用反射机制在运行时生成代理对象的字节码,并将其加载到 JVM 中。

三、使用场景

1、远程调用;

2、缓存;

3、记录日志;

4、事务管理;

5、权限检查;

 

希望这个示例能帮助你理解代理模式的原理和使用场景。如果你有任何其他问题,请随时提问。


http://www.ppmy.cn/devtools/5156.html

相关文章

WPS二次开发专题:WPS SDK事件回调

作者持续关注 WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 目录 1.什么是WPS SDK事件回调&#xff1a; 2.WPS …

java通过maven导入本地jar包的三种方式

一、引入lib下加载&#xff08;加载过后打包&#xff0c;以后再次使用不用再次导入&#xff09; 首先创建一个用于创建jar包的项目&#xff0c;并测试能否成功运行 讲项目打包 在需要引入的项目中创建lib目录 并把刚才打包的jar复制进去 通过dependency引入jar包 groupId、art…

案例分析-redis

案例需求&#xff1a;在7002这个slave节点执行手动故障转移&#xff0c;重新夺回master地位 步骤如下&#xff1a; 1&#xff09;利用redis-cli连接7002这个节点 2&#xff09;执行cluster failover命令 如图&#xff1a; 效果&#xff1a; 4.5.RedisTemplate访问分片集群 …

LeetCode——965. 单值二叉树

题目- 力扣&#xff08;LeetCode&#xff09; 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。 只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;[1,1,1,1,1,null,1] 输出&a…

git撤销提交

要在Git中撤销最近的一次提交&#xff0c;可以使用以下命令&#xff1a; git reset --soft HEAD^&#xff1a;这将撤销最后一次提交&#xff0c;但保留更改内容在暂存区。 git reset --mixed HEAD^&#xff1a;默认选项&#xff0c;撤销提交和暂存区的更改&#xff0c;不过不删…

计算机科学与导论 第十六章 安全

文章预览&#xff1a; 16.1引言16.1.1 安全目标16.1.2 攻击 16.2机密性16.2.1 对称密钥密码术16.2.2 非对称密钥密码术 16.1引言 为了安全&#xff0c;信息需要避开未授权的使用(机密性),保护信息不受到未授权的篡改(完整性)&#xff0c;并且对于得到授权的实体来说是需要时可…

《战神4》和《战神5》有什么联系吗 苹果电脑如何运行《战神4》苹果电脑玩战神 Mac玩游戏 战神5攻略 crossover激活码

《战神4》&#xff08;God of War 2018&#xff09;和《战神5》&#xff08;God of War: Ragnark&#xff09;是一对引人注目的游戏作品&#xff0c;它们不仅在游戏界引起了广泛的关注&#xff0c;也给玩家带来了深入探索北欧神话世界的机会。这两部游戏之间的联系不仅体现在剧…

ChatGPT:携手完成功能论文写作

ChatGPT无限次数:点击直达 ChatGPT&#xff1a;携手完成功能论文写作 随着人工智能技术的发展&#xff0c;如今我们已经能够利用先进的自然语言处理模型来辅助我们进行论文写作。ChatGPT作为其中一款非常强大的语言生成模型&#xff0c;为我们提供了许多便利&#xff0c;帮助我…