Spring Boot 学习第七天:动态代理机制与Spring AOP

news2024/11/19 11:24:12

1 概述

        在Java的世界中,实现AOP的主流方式是采用动态代理机制,这点对于Spring AOP也一样。代理机制的主要目的就是为其他对象提供一种dialing以控制对当前对象的访问,用于消除或缓解直接访问对象带来的问题。通过这种手段,一个对象就代表另一个对象的部分功能,我们创建包含当前对象的对象,以便向外界提供功能接口。本篇将关注目前主流的动态代理实现技术,并分析Spring AOP中的代理实现方式。

        在Spring中,采用的代理机制有两种,即JDK动态代理和CGLIB动态代理。为了介绍动态代理机制,引入一个具体的应用场景。考虑一个Account接口,它包含一个用于图片展示的open方法,代码如下:

public interface Account {
    void open();
}

        然后针对该接口有一个实现类RealAccount,其中的方法只用于模拟,不包括具体业务,代码如下:

public class RealAccount implements Account {
    private String name;

    public RealAccount(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void open() {
        System.out.println("打开:" + name + ",账户");
    }
}

        现在,假设需要在执行RealAccount的open()方法的前后分别打印日志信息。接下来讨论如何分别基于JDK动态代理和CGLIB动态代理来实现这个目标。

2 JDK动态代理

        在JDK自带的动态代理中存在一个InvocationHandler接口,首先要做的就是提供一个该接口的实现类,代码如下:

public class AccountHandler implements InvocationHandler {
    private Object obj;

    public AccountHandler(Object obj) {
        super();
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        doBefore();
        result = method.invoke(obj,args);
        doAfter();
        return result;
    }

    private void doAfter() {
        System.out.println("开户后");
    }

    private void doBefore() {
        System.out.println("开户前");
    }
    
}

        InvocationHandler接口中包含一个invoke()方法,必须实现这个方法。在这个方法中,通常需要调用method.invoke()方法执行原有对象的代码逻辑,然后可以在该方法前后添加相应的代理实现。在上述代码中,只是简答的打印了日志。然后编写测试类来验证执行结果,测试类代码如下:

public class Test {
    public static void main(String[] args) {
        Account account = new RealAccount("zhangsan");
        AccountHandler handler = new AccountHandler(account);
        Account proxy = (Account) Proxy.newProxyInstance(
                account.getClass().getClassLoader(),
                account.getClass().getInterfaces(),
                handler
        );
        proxy.open();
    }
}

运行结果如下:

        这里的Proxy.newProxyInstance()方法的作用就是生成代理类。当该方法被调用时,RealAccount类的实例被传入。然后当代理类的open()方法被调用时,AccountHandler中invoke方法就会被触发,从而实现代理机制。这里的类层次结构图如下:

        咨询分析上述代码结构,可以发现其遵循“设计并实现业务接口——实现Handler——创建代理类”这个流程,然后在Handler中构建具体的代理逻辑。上述流程也是代表了最基本的代理机制实现流程。联想一下,很多基于AOP机制的拦截器底层实际上就是类似的原理。

3 CGLIB动态代理

        CGLIB是一个Java字节码生成库,提供了易用的API对Java字节码进行创建和修改。现在尝试拥CGLIB来代理前面的RealAccount类,代码清单如下:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class AccountCglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    
    public Object getProxy(Class<?> clazz){
        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("之前");
        methodProxy.invokeSuper(o,objects);
        System.out.println("之后");
        return o;
    }
}

         上述代码中的Enhancer类是CGLIB中最常用的一个类,类似于前面介绍的JDK动态代理中的Proxy类。和Proxy只能代理接口不同,Enhancer既能够代理类接口,也能够代理不同类,但不能拦截final类和方法。在这里,我们实现了MethoInterceptor中的intercept()方法以提供代理逻辑。AccountCglibProxy类的使用方法也比较简单,测试代码如下:

public class CglibProxyTest {
    public static void main(String[] args) {
        AccountCglibProxy proxy = new AccountCglibProxy();
        RealAccount account = (RealAccount) proxy.getProxy(RealAccount.class);
        account.open();
    }
}

        作为对比,下表展示了JDK动态代理和CGLIB动态代理之间的区别。

4 ProxyFactoryBean

        JDK自带的动态代理以及基于CGLIB的动态代理在Spring框架中都得到了应用,最典型的应用场景就是实现AOP。Spring专门提供了一个ProxyFactoryBean类用于手动创建对象代理,并将创建的代理对象作为目标对象的AOP代理。

        ProxyFactoryBean提供了一组配置属性用于指定代理的执行行为,比较常见的包括proxyTargetClass和exposeProxy。如果proxyTargetClass属性为true,则仅使用CGLIB创建代理。如果该属性未设置,那么有两种情况:如果目标实现了接口,则将使用JDK创建代理;反之,将使用CGLIB创建代理。而exposeProxy属性用于设置是否将当前代理暴露给ThreadLocal。如果该属性为true,那么开发人员可以使用AopContext.currentProxy()方法来获取代理对象。

        接下来,将演示如何使用ProxyFactoryBean来创建和管理对象。继续使用前面的场景,现在为MethodBeforeAdvice接口提供了一个实现类。显然从命名上看,这个实现类是方法执行前通知的,代码清单如下:

import com.sun.istack.internal.logging.Logger;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class AccountTransactionInterceptor implements MethodBeforeAdvice {
    private static final Logger LOGGER = Logger.getLogger(AccountTransactionInterceptor.class);
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        LOGGER.info("账户交易被拦截");
    }
}

           接着通过Java代码创建一个通知,实现方式如下:

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;

public class MyAdvisor {
    @Bean
    public Advisor accountServiceAdvisor(){
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(*com.jay.aop.service.AccountService.doAccountTransaction(..))");
        return new DefaultPointcutAdvisor(pointcut,new AccountTransactionInterceptor());
    }
}

        最后,创建一个ProxyFactoryBean实例,并设置相关属性,代码如下:

@Bean
    public ProxyFactoryBean accountService(){
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(new AccountServiceImpl());
        proxyFactoryBean.addAdvisor(accountServiceAdvisor());
        proxyFactoryBean.setExposeProxy(true);
        return proxyFactoryBean;
    }

        注意,这里设置目标类为AccountService接口的实现类AccountServiceImpl,并把exposeProxy属性设置为true。这样,在AccountServiceImpl中就可以使用Spring AOP提供的AopContext.currentProxy方法来获取这个代理对象,实例代码如下:

public class AccountServiceImpl implements AccountService {
    private static  final Logger LOGGER = Logger.getLogger(AccountServiceImpl.class);
    @Override
    public boolean doAccountTransaction(Account source, Account dest, int amount) throws MinimumAccountException {
        ((AccountService)(AopContext.currentProxy())).doAccountTransaction(source,dest,amount);
        return true;
    }
}

 

              

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1856669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java | Leetcode Java题解之第169题多数元素

题目&#xff1a; 题解&#xff1a; class Solution {public int majorityElement(int[] nums) {int count 0;Integer candidate null;for (int num : nums) {if (count 0) {candidate num;}count (num candidate) ? 1 : -1;}return candidate;} }

(2011-2022年) 全国各省快递业务量与快递业务收入面板数据

中国快递业近年来随着电子商务的蓬勃发展而迅速壮大&#xff0c;成为现代生活中不可或缺的一部分。快递业务量与收入的面板数据为我们提供了一个观察中国快递市场繁荣与多元化的窗口。 数据来源 中国统计年鉴 参考文献 胡润哲, 魏君英, 陈银娥. 数字经济发展对农村居民服务…

如何把文件扫描成电子版pdf?这4个方法包教包会~

大多数时候&#xff0c;我们为了更加方便存档与文件共享&#xff0c;都会借助专用的扫描仪来把文件扫描成PDF文档~但其实现在想要扫描文件并不是只有扫描仪能够搞定&#xff0c;还有很多的方法和工具都能够代替它来一一实现。 就好比下面罗列出来的四大文件扫描方法&#xff0…

【STM32学习】基于I2C协议的OLED显示(利用U8G2库)

目录 一、I2C协议、时序协议 1.1 I2C协议&#xff1a; 1.2时序协议&#xff1a; 二、OLED原理 2.1主流显示面板技术 2.2OLED发光原理&#xff1a; 2.3汉字点阵显示原理 三、U8G2 四、代码移植 4.1创建工程&#xff08;HAL库&#xff09;&#xff1a; 4.1.1CubexMX配置…

Gemalto加密狗的优势

Gemalto加密狗&#xff0c;作为硬件加密技术的杰出代表&#xff0c;为软件开发商和用户提供了一种高效、安全的解决方案。这种加密狗不仅拥有卓越的加密性能&#xff0c;还具备易用性和可靠性&#xff0c;是保护软件知识产权和防止非法复制的重要工具。 一、Gemalto加密狗的核心…

华为仓颉语言:编程语言的新篇章

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

【数据结构】【版本1.4】【线性时代】——队列

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、队列的概念二、队列的模拟实现2.1 定义2.2 初始化2.3 销毁2.4 入队2.5 判空2.6 出队2.7 获取队头元素…

ImportError: No module named createrepo

我在用createrepo命令创建本地源时&#xff0c;出现如下&#xff1a; ImportError: No module named createrepo原因估计就是之前升级python2.6为2.7时导致&#xff08;系统为centos7&#xff09;&#xff0c;看网上很多说&#xff0c; 修改/usr/share/createrepo/genpkgmeta…

7.系统工具——黑马程序员Java最新AI+若依框架项目

目录 前言一、表单构建任务&#xff1a;设计添加课程表单 二、 代码生成1.任务&#xff1a;将部门表在页面端显示改为树形结构 三、系统接口任务&#xff1a;使用sagger进行接口测试 前言 提示&#xff1a;本篇讲解若依框架 系统工具 一、表单构建 功能&#xff1a;完成前端…

ASP.Net.WebAPI和工具PostMan

1.WebAPI概述 1.1 WebAPI WebAPI 是一种传统的方式&#xff0c;用于构建和暴露 RESTUI风格的Web服务。它提供了丰富的功能和灵活性&#xff0c;可以处理各种HTTP请求&#xff0c;并支持各种数据格式&#xff0c;如JSON、XML等。 WebAPI使用控制器(Controllers)和动作方法(Ac…

关于最强模型Claude 3.5 Sonnet,你需要知道的10条总结!

大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,所以创建了“AI信息Gap”这个公众号,专注于分享AI全维度知识,包括但不限于AI科普,AI工具测评,AI效率提升,AI行业洞察。关注我,AI之…

VMware Workstation克隆虚拟机详细步骤

克隆虚拟机 首先我们先创建一台虚拟机&#xff0c;将该虚拟机关闭后&#xff0c;然后右键该虚拟机按照图下所示点击 克隆 下一页 下一页 这里按照需求选择克隆类型&#xff0c;我选择创建完整克隆。点击下一步 设置好虚拟机名称和位置&#xff0c;点击完成 稍微等待一会 点击 …

IPython小白教程:提升你的Python交互式编程技巧,通俗易懂!

IPython是一个增强的Python交互式shell&#xff0c;它提供了丰富的功能和便捷的交互方式&#xff0c;使得Python开发和数据分析工作更加高效。本文将详细介绍IPython的基本概念、使用方法、主要作用以及注意事项。 一、IPython简介 1. IPython的起源 IPython由Fernando Prez于…

.NET 通过UserInit键实现Windows权限维持

01阅读须知 此文所节选自小报童《.NET 内网实战攻防》专栏&#xff0c;主要内容有.NET在各个内网渗透阶段与Windows系统交互的方式和技巧&#xff0c;对内网和后渗透感兴趣的朋友们可以订阅该电子报刊&#xff0c;解锁更多的报刊内容。 02基本介绍 本文内容部分节选自小报童…

C++项目——负载均衡在线OJ

目录 前言 一、所用技术与开发环境 二、项目宏观结构 三、日志服务设计 四、compiler 编译服务设计 1.编译服务 2.运行服务 3.编译并运行服务 4.引入cpp-httplib库 4.1升级gcc版本 4.2cpp-httplib 4.3httplib的使用 五、oj_server服务设计 1. http路由选择 2.文…

预测准确率达95.7%,ChatMOF利用LLM预测和生成金属有机框架,包含人工智能词汇表(AI glossary)

预测准确率达95.7%,ChatMOF利用LLM预测和生成金属有机框架,包含人工智能词汇表(AI glossary)。 金属有机框架(MOF)因其孔隙率大、表面积大和出色的可调性而用于许多化学应用。然而,在利用 AI 深入探索 MOF 设计与性能优化的研究征途中,科学家们正面临着前所未有的挑战。…

2024中国第三方算力中心服务商发展研究报告

来源&#xff1a;科智咨询 近期历史回顾&#xff1a;《江苏省绿色建筑评价标识实施细则》(1).pdf 《江苏省绿色建筑评价标识实施细则》.pdf 【计算工具】钢铁企业碳排放各工序数据收集表.xlsx 【深度报告】钢铁产品碳足迹核算及报告指南.pdf 【专家PPT】宝钢低碳钢铁技术策划及…

Win10用户必看:最好用最稳定的版本在此,值得一试!

在Win10电脑操作中&#xff0c;用户可以根据的需要&#xff0c;下载安装不同的系统版本。现在&#xff0c;许多用户好奇Win10哪个版本最好用最稳定&#xff1f;接下来小编给大家推荐最好用最稳定的Win10版本&#xff0c;这些系统版本经过优化升级&#xff0c;相信会给大家带来最…

Win11最适合打游戏的版本推荐:畅玩游戏,告别卡顿!

在Win11电脑操作中&#xff0c;用户不仅可以进行办公、学习等操作&#xff0c;也可以畅玩喜欢的游戏。如果喜欢打游戏的用户&#xff0c;就可以安装上适合打游戏的系统版本。但许多新手用户不知道去哪里找到最适合打游戏的Win11系统版本&#xff1f;以下小编就给大家带来这样的…

视频集市新增支持多格式流媒体拉流预览

流媒体除了常用实时流外还有大部分是以文件的形式存在&#xff0c;做融合预览必须要考虑多种兼容性能力&#xff0c;借用现有的ffmpeg生态可以迅速实现多种格式的支持&#xff0c;现在我们将按需拉流预览功能进行了拓展&#xff0c;正式支持了ffmpeg的功能&#xff0c;可快捷方…