12_Spring-AOP

news2024/11/28 2:33:31

文章目录

  • 动态代理原理
  • AOP
    • AOP和OOP
    • AOP的特点
    • AOP的应用场景
    • AOP的编程术语
    • AOP的实现
      • SpringAOP
      • AspectJ
        • 切面组件
        • 切入点表达式
        • AspectJ切面
        • JoinPoint连接点
      • 机制

动态代理原理

在这里插入图片描述


AOP

  • Aspect Oriented Programming
  • 面向切面编程,是指通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
    • 面向切面编程:通过切入点指定容器中的组件中的方法,按照通知的方式做增强
  • AOP是OOP(面向对象编程)的延续
  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

AOP和OOP

  • OOP 面向对象编程,是Object Oriented Programming的简称
  • OOP:通过继承来增强
  • AOP:通过切面来增强

AOP的特点

  • AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
  • Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类(委托类)织入增强代码
  • SpringAOP使用的是JDK动态代理和CGlib动态代理
    • 如果有实现接口,使用的就是JDK动态代理,如果没有实现接口使用的就是CGlib动态代理

AOP的应用场景

  • 这场景通常是多次反复出现而又相对来说比较繁琐的部分
    • 事务管理、性能监视、安全检查、缓存、日志等

AOP的编程术语

  • Target:目标类 (需要被代理的类,委托类)
  • Proxy:代理类 (动态代理生成的)
  • JoinPoint:连接点,指被代理对象里那些可能会被增强的点(方法)如所有方法(候选的可能被增强候选点)
    • 获取增强过程中的信息
    • 比如可以获取Proxytargetmethodargs等信息
  • PointCut:切入点,已经被增强的连接点。
    • 获取指定增强的方法
    • AOP的目标 → 容器中的组件能够增强
    • 谁做增强(能够细化到方法级别)→ Pointcut切入点圈定增强范围 做记号
    • 做什么样的增强 → Advice通知 → 指导切入点指定的方法做一个什么样的增强
  • Advice:通知(具体的增强的代码)。代理对象执行到Joinpoint所做的事情。
  • Aspect:切面,是切入点和通知的结合切面是一个特殊的面:一个切入点和一个通知组成一个特殊的面
  • weaver:织入(植入)是指把advice应用到目标对象来创建新的代理对象的过程

AOP的实现

  1. 动态代理
    • 在这里插入图片描述
  2. SpringAOP
    • 在这里插入图片描述
    • Spring官方文档提供的方式,但是它建议你使用另一种方式AspectJ
  3. AspectJ

SpringAOP

在容器中注册3个组件:委托类组件、通知组件、代理组件(ProxyFactoryBean)

eg:

  • 使用注解注册委托类组件和通知组件
@Service
public class UserServiceImpl implements UserService{
    @Override
    public void sayHello(String s) {
        System.out.println("hello" + s);
    }
}
// 计算方法的执行时间
@Component
// 组件id customAdvice
public class CustomAdvice implements MethodInterceptor {
    /**
     * 相当于之前的InvocationHandler的方法
     */

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // 记录开始时间
        long start = System.currentTimeMillis();

        // 执行委托类方法
        // 类似于动态代理的method.invoke
        Object proceed = methodInvocation.proceed();

        // 记录结束时间
        long end = System.currentTimeMillis();

        // 计算消耗时间
        long cost = end - start;
        System.out.println(methodInvocation.getMethod().getName() + "执行时间为:" + cost);

        return proceed;
    }
}
  • 通过代理组件注册代理组件
@Configuration
@ComponentScan("com.coo1heisenberg.demo2")
public class AppConfiguration {

    @Bean
    public ProxyFactoryBean userServiceProxy(UserService userService) {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();

        // 告知其委托类组件有哪些
        proxyFactoryBean.setTarget(userService);

        // 告知其通知组件有哪些 -> 提供的是通知组件的名称、id
        proxyFactoryBean.setInterceptorNames("customAdvice");
        // beanFactory.getBean("customAdvice");

        // 它里面的getObject会根据提供的值生成代理对象
        return proxyFactoryBean;
    }
}
  • 单元测试要从容器中指定代理组件取出
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfiguration.class)
public class SpringAOPTest {
    @Autowired
    @Qualifier("userServiceProxy")
    UserService userService;

    @Test
    public void testProxy() {
        userService.sayHello("zs");
    }
}

AspectJ

切面组件
  • 增加AspectJ的注解开关 → 配置类上增加一个注解@EnableAspectJAutoProxy
  • 把组件标记为切面组件@Aspect

eg:

  • 增加AspectJ的注解开关
@Configuration
@ComponentScan("com.coo1heisenberg.demo3")
@EnableAspectJAutoProxy //AspectJ注解使用的开关
public class AppConfiguration {
}
  • 把组件标记为切面组件
@Component
@Aspect
public class CustomAspect {
}
切入点表达式
  • 引入Aspectjweaver的依赖
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>
  • 在切面组件中使用@Pointcut注解
    • value属性:切入点表达式
    • 方法名:作为切入点id
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Pointcut {
    String value() default "";

    String argNames() default "";
}
  1. execution
@Component
@Aspect
public class CustomAspect {


    /**
     * execution(修饰符 返回值 包名 + 类名 + 方法名(形参))
     */
   @Pointcut(value = "execution(public void com.coo1heisenberg.demo3.service.UserServiceImpl.sayHello(java.lang.String))")

    /**
     * 可以增强多个方法
     *
     * 越具体,匹配范围越小,越宽泛,匹配范围越广
     * 通配符:
     *  - 修饰符:
     *      - 可以省略不写,如果省略不写代表任意修饰符
     *
     *  - 返回值:
     *      - 不能省略,但是可以使用通配符 * ,* 代表任意值
     *      - 如果是引用类型,要写全限定类名;全限定类名也可以出现通配符 *
     *
     *  - 包名、类名、方法名:
     *      - 可以使用 * (任意单词的一部分)来通配,也可以使用 .. (代表一部分值省略)来通配
     *      - 头和尾的位置不能使用.. ,但是可以使用 *
     *
     *  - 形参:
     *      - 省略不写,代表无参方法
     *      - 可以使用 * (任意单个类型的参数)来通配
     *      - 可以使用 .. (代表任意参数,数量任意,类型也任意)来通配
     *      - 如果是引用类型,要写全限定类名,全限定类名中也可以使用 通配符*
     */
    public void pointcut1() {}
}

  1. @annotation
    • @annotation(自定义注解的全限定类名)
    • 该注解写在哪个方法上,哪个方法就被划入到切入点范围内
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnnotation {
}
@Component
@Aspect
public class CustomAspect {
    /**
     * 在容器中的组件中找包含特定注解的方法
     */
    @Pointcut("@annotation(com.coo1heisenberg.demo3.anno.MethodAnnotation)")
    public void pointcut() {

    }
}
  1. @target
    • @annotation(自定义注解的全限定类名)
    • 该注解写在类上,该类下所有的方法都划入到切入点的范围内
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeAnnotation {
}
@Component
@Aspect
public class CustomAspect {
    /**
     * 在容器中的组件中找包含特定注解的方法
     */
    @Pointcut("@annotation(com.coo1heisenberg.demo3.anno.MethodAnnotation)")
    public void pointcut1() {

    }

    /**
     * 在容器中的组件中找到包含特定注解的组件,特定的注解是自己定义的
     */
    @Pointcut("@target(com.coo1heisenberg.demo3.anno.TypeAnnotation)")
    public void pointcut2() {
        
    }
}

总结所有的切入点都是为了找特定的方法的


AspectJ切面
  • 在切面类中配置切面组件和通知方法
  • 切入点和通知要成对出现
@Component
@Aspect
public class CustomAspect {
    @Pointcut("execution(public void com.coo1heisenberg.demo3.service.UserServiceImpl.sayGoodBye(java.lang.String))")
    public void pointcut1() {

    }


    /**
     * before通知
     * 返回值:void
     * 方法名:任意去写
     * 形参:joinPoint连接点 (可写可不写)
     *
     */
    /**
     * 切入点和通知要成对出现
     */
    @Before("pointcut1()")
    public void before() {
        System.out.println("在委托类方法执行之前");
    }

    /**
     * after通知
     * 返回值:void
     * 方法名:任意去写
     * 形参:joinPoint连接点 (可写可不写)
     *
     */
    /**
     * 相当于try-catch中的finally,它是一定会执行到的
     */
    @After("pointcut1()")
    public void after() {
        System.out.println("在委托类方法执行之后");
    }

    /**
     * afterReturning通知
     * 返回值:void
     * 方法名:任意去写
     * 形参:Object(委托类方法的执行结果)
     *
     * 获得委托类方法返回的结果
     * 在return之后执行的
     */
    @AfterReturning(value = "pointcut1()", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("after returning : " + result);
    }

    /**
     * afterThrowing通知
     * 返回值:void
     * 方法名:任意去写
     * 形参:Exception/Throwable(委托类方法执行过程中抛出异常)
     *
     * 能够获得委托类方法抛出的异常
     */

    @AfterThrowing("pointcut1()")
    public void afterThrowing() {
        System.out.println("after throwing : ");
    }

    /**
     * around通知
     * 返回值:Object,就是代理对象的返回结果
     * 方法名:任意去写
     * 形参:ProceedingJoinPoint连接点 (必须写) → 提供了proceed方法,执行的是委托类的代码
     *
     * around通知可以包含before、after、afterReturning、afterThrowing部分
     */
    /**
     * 类似于前面InvocationHandler的invoke,类似于MethodInterceptor的invoke
     */

    @Around("pointcut1()")
    public Object around(ProceedingJoinPoint joinPoint) {
        // before的部分
        System.out.println("before部分");
        Object proceed = null; 
        try {
            proceed = joinPoint.proceed(); // 执行委托类方法,委托类方法的返回值是proceed
            
            // 这里执行afterReturning
            System.out.println("afterReturning部分");
        } catch (Throwable e) {
            // afterThrowing
            System.out.println("afterThrowing部分");
            e.printStackTrace();
        } finally {
            // after部分
            System.out.println("after部分");
        }
        
        return proceed;
    }
}


JoinPoint连接点
  • 获取增强过程中的一些值
    • Signature 方法
    • Arguments 参数
    • This 代理对象
    • Target 委托类对象

eg:

@Before("mypointcut()")
public void before(JoinPoint joinPoint){
    //Signature 方法的描述
    //This 代理对象
    //Target 委托类对象
    //Arguments 参数
    Signature signature = joinPoint.getSignature();
    Object proxy = joinPoint.getThis();
    Object target = joinPoint.getTarget();
    Object[] args = joinPoint.getArgs();

    System.out.println("signature:" + signature.getName());
    System.out.println(proxy.getClass().getName());
    System.out.println(target.getClass().getName());
    System.out.println(Arrays.asList(args));
}

机制

在这里插入图片描述

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

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

相关文章

目标检测——RCNN系列学习(一)

前置知识 包括&#xff1a;非极大值抑制&#xff08;NMS&#xff09;、selective search等 RCNN [1311.2524] Rich feature hierarchies for accurate object detection and semantic segmentation (arxiv.org)https://arxiv.org/abs/1311.2524 1.网络训练 2.推理流程 3.总…

【C++ STL算法】sort 排序

文章目录 【 1. 基本原理 】【 2. sort 的应用 】实例 - sort 函数实现 升序排序和降序排序 函数名用法sort (first, last)基于 快速排序&#xff0c;对容器或普通数组中 [ first, last ) 范围内的元素进行排序&#xff0c;默认进行升序排序&#xff08;从小到大&#xff09;。…

vscode-tasks.json自定义任务

以下所有内容,参考自VScode官方文档: vscode_tasks-docs任务说明文档vscode_variables-reference-docs变量说明文档vscode addtional docs for tasksvscode launch.json 属性设置文档,(下文没有介绍,没有涉及) 浅浅记录一下个人对vscode任务(task)的理解,还谈不上使用. 文章目…

TouchGFX之时钟

代码 #ifndef TOUCHGFX_ABSTRACTCLOCK_HPP #define TOUCHGFX_ABSTRACTCLOCK_HPP #include <touchgfx/containers/Container.hpp> #include <touchgfx/hal/Types.hpp>namespace touchgfx { class AbstractClock : public Container { public:AbstractClock();/* 设…

【IMU系列】什么是传感器的ODR和FSR实际如何配置传感器

使用更高的ODR信号有两个主要缺点&#xff1a;内存限制和功耗 以实际传感器为例

Redis分布式锁误删情况说明

4.4 Redis分布式锁误删情况说明 逻辑说明&#xff1a; 持有锁的线程在锁的内部出现了阻塞&#xff0c;导致他的锁自动释放&#xff0c;这时其他线程&#xff0c;线程2来尝试获得锁&#xff0c;就拿到了这把锁&#xff0c;然后线程2在持有锁执行过程中&#xff0c;线程1反应过…

成都正信晟锦:借了钱不还怎么起诉对方

在民间借贷中&#xff0c;遇到对方借钱不还的情况时&#xff0c;可以依法通过起诉的方式解决纠纷。首先&#xff0c;债权人应与债务人进行沟通&#xff0c;尝试和解。如果协商无果&#xff0c;则需收集证据&#xff0c;包括但不限于借条、转账记录、双方通讯记录等&#xff0c;…

mysql忘记密码如何重置

错误截图 解决方法 1. 关闭mysql服务器 net stop mysql服务名 2. 查找mysql安装路径中bin目录路径 3. cmd 打开该目录 4. 在my.ini所在的目录&#xff0c;新建一个init_pwd.txt文件。文件内容如下 ALTER user rootlocalhost identified by 密码; 5. 在第四步打开的cmd中 输…

vscode的基本使用(简洁版)

1.Vscode配置C/C开发环境 1.1 下载编译器 MinGW-w64 - for 32 and 64 bit Windows - Browse Files at SourceForge.net 下载并解压 -> 放到电脑C盘的根目录下 1.2 配置环境变量 1.3 安装C/C插件 2. 在VScode上编写C语言代码并编译成功 在vscode中写代码时,需要将代码放在…

在Idea里,执行npm命令 : 无法加载文件 ***\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

重建大师进行扫码认证了,接下来怎样才能正常使用?(如下图)

重建大师软件授权已经有了后&#xff0c;新建工程后设置任务目录和监控目录一致就可以运行了。 重建大师是一款专为超大规模实景三维数据生产而设计的集群并行处理软件&#xff0c;输入倾斜照片&#xff0c;激光点云&#xff0c;POS信息及像控点&#xff0c;输出高精度彩色网格…

Tiktok矩阵系统是什么?——Tiktok矩阵系统的优势、功能、及应用场景的介绍

摘要 Tiktok作为全球现象级的短视频平台,其发展前景日益明朗。 Tiktok全世界有多少用户? TikTok作为全球性的社交媒体平台,其用户数量一直在持续增长。根据最新的数据,预计到2024年,TikTok的用户数量将达到数十亿,覆盖全球范围内的各个年龄段和地区。具体来说,根据Ti…

互质数的个数(acwing)

题目描述&#xff1a; 给定 a,b&#xff0c;求 1≤x<a^b 中有多少个 x 与 a^b 互质。 由于答案可能很大&#xff0c;你只需要输出答案对 998244353 取模的结果。 输入格式&#xff1a; 输入一行包含两个整数分别表示 a,b&#xff0c;用一个空格分隔。 输出格式&#xf…

W25QXX系列W25Q64介绍

文章目录 前言介绍W25Q系列硬件电路四重SPI&#xff08;了解&#xff09; 框图Flash操作注意状态寄存器指令集电器特性 前言 W25Q64是一个8MByte大小的非易失性存储器&#xff0c;使用的是SPI协议&#xff0c;本文将全面介绍W25Q64的特性、工作原理以及注意事项 SPI详解见&am…

30万奖金谁能瓜分?OurBMC开源大赛决赛入围名单公示

首届开放原子开源大赛基础软件赛道自今年 1 月开启报名以来&#xff0c;吸引了全国各地 BMC 技术爱好者的广泛关注和踊跃报名。该赛事由开放原子开源基金会牵头&#xff0c; OurBMC 社区及理事长单位飞腾信息技术有限公司联合承办&#xff0c;以 “基于 BMC 技术的服务器故障诊…

CSS导读 (Emmet语法)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 续&#xff1a;七、Chrome调试工具 一、Emmet语法 1.1 快速生成HTML结构语法 1.2 快速生成CSS样式语法 &…

ctfshow web入门 php特性 web108--web115

web108 ereg函数相当于而preg_match()函数 ereg函数的漏洞&#xff1a;00截断。%00截断及遇到%00则默认为字符串的结束 strrev函数就是把字符串倒过来 就是说intval处理倒过来的传参c0x36d&#xff08;877&#xff09;?ca%00778 web109 异常处理类 通过异常处理类Excepti…

DFS-0与异或问题,有奖问答,飞机降落

代码和解析 #include<bits/stdc.h> using namespace std; int a[5][5]{{1,0,1,0,1}}; //记录图中圆圈内的值&#xff0c;并初始化第1行 int gate[11]; //记录10个逻辑门的一种排列 int ans; //答案 int logic(int x, int y, int op){…

3D引擎八叉树构建算法实现

最近&#xff0c;我一直在努力研究我的3D引擎Storm3D。 我花费大量时间的功能之一是开发一种通用且高效的八叉树数据结构&#xff0c;它将用于从碰撞检测到基于体素的渲染等多种用途。 在这里我将介绍构建八叉树的基本算法以及你可能遇到的一些障碍。 NSDT工具推荐&#xff1a;…

实习该选择c++后台开发(写业务逻辑)还是音视频开发(写sdk)?

后台开发:更多是理解需求、分析问题、解决bug等能力、对于逻辑培养有很大的帮助。可以进行软件开发、网络开发、游戏开发、以及之后可能的物联网相关开发。在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「音视频开发的资料从专业入门到高级教程」&#…