【学习笔记】SpringAOP的用法全解

文章目录

  • Spring的AOP
    • 一、 Spring对AOP的实现包括以下3种方式
      • **什么是AspectJ?**
    • 二、使用Spring的AOP
      • 1、准备工作
      • 2、尝试写一个简单的AOP demo
      • 3、代码如下:
        • spring.xml
        • 业务类
        • 切面类
        • 测试类
      • 4、复习切面表达式
        • 1)所有方法
        • 2)指定路径下某个包及其子包的所有方法
        • 3)限定public的,某个包及其子包的,带login开头的,且参数是Stirng 的 方法
      • 5、5种不同的通知如何写?
        • 思考1,5种通知都存在时,执行的顺序是什么样的?如果抛出异常,执行的顺序是什么样的?
        • 思考2:切面的先后顺序(如果一个业务存在多个切面,如何排序?如何执行
        • 测试2:同数字或者都无Order注解的情况,是如何执行的
        • 测试2:不同优先级的切面先后执行顺序
      • 6、写法优化(切点优化pointcut)
      • 7、拓展用法,JointPoint
      • 8、全注解开发优化
          • 修改后的配置类
          • 测试类
      • 9、基于XML开发的AOP

Spring的AOP

一、 Spring对AOP的实现包括以下3种方式

  • 第一种方式: Spring框架结合AspectJ框架实现的AOP,基于注解方式。(需要重点掌握, 核心用法
  • 第二种方式: Spring框架结合AspectJ框架实现的AOP,基于XML方式第三种方式:
  • Spring框架自己实现的AOP,基于XML配置方式。(少用不做介绍)

什么是AspectJ?

什么是AspectJ?

(Edlipse组织的一个支持AOP的架。Aspect/架是独立于Spring架之外的一个框架,Spring框架用了AspectJ)

Aspedtu项目起源于洛阿你托 (Palo Ato 研究中心(写为PARC) ,该中由Xeox建团资,reor kczales导,以1997年开力于Aspec开发,1998年第-次发布外部用户,2001年发布1.0 release为了Aspe技术和发展,PAR(在200年3月将Aspec/项目给了dipse织,因为Aspec的发展和受关注度大大超出了PARC的预期,他们已经力继维持它的发展。

二、使用Spring的AOP

1、准备工作

要使用Spring的AOP,需要导入以下依赖

  • spring context 核心依赖 包含 spring-aop
  • spring-aspects 依赖

image-20230405233638103

并在resource目录下新建配置文件

image-20230405234643640

2、尝试写一个简单的AOP demo

思路整理

  1. 写一个业务类模拟实际业务

  2. 定义一个切面类 (切面=通知+切点),需要添加 @Aspect 直接,告诉spring这是一个切面类

  3. 将上述的两个类,纳入Spring容器中 (注解 + 添加扫描配置信息)

  4. 在切面类中定义具体的通知

    • 通知就是增强,就是具体要编写的增强代码
    • 这里通知Advice以方法的形式出现,因为方法中可以写代码
  5. 在该方法上加入 @Before 注解,表示这是一个 前置通知

    • 这个注解注解写 ——> 切点表达式
  6. 在spring配置中,开启aspecj的自动代理 <aop:aspectj-autoproxy />

    • 开启自动dialing后,Spring容器会自动扫描该类上是否带有@Aspect注解,如果有,则生成代理对象。
    • 这里标签可以添加一个属性proxy-target-class
      • 为true时表示强制使用CGLIB
      • 反之,则根据接口情况使用JDK动态代理或者CGLIB
  7. 测试

3、代码如下:

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--组件扫描-->
    <context:component-scan base-package="com.zhc.aop"/>

    <!--开启aspectj的自动代理-->
    <aop:aspectj-autoproxy  proxy-target-class="true"/>

</beans>

业务类

package com.zhc.aop.service;

import org.springframework.stereotype.Service;

@Service("userService")
public class UserService {

    public void login(){
        System.out.println("系统正在登陆。。。");
    }
}

切面类

package com.zhc.aop.aspects;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component("logAspect")
@Aspect     // 声明这是一个切面
public class LogAspect {


    @Before("execution(* com.zhc.aop.service..*(..))")
    public void beforePoint(){
        System.out.println("前置通知");
    }
}

测试类

import com.zhc.aop.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    @Test
    public void TestProxy(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.login();
    }
}

测试结果

image-20230406142455120

4、复习切面表达式

切面表达式在这章中比较重要,迅速复习一遍即可,要记得几个常用的表达式

image-20230406135351271

1)所有方法

权限修饰符略,异常略,全限定名略

execution(* *(..))

2)指定路径下某个包及其子包的所有方法

excution(* com.zhc..*(..))

3)限定public的,某个包及其子包的,带login开头的,且参数是Stirng 的 方法

excution(public * com.zhc..login*(String))

5、5种不同的通知如何写?

  • 前置 —— @Before
  • 后置—— @AfterReturning
  • 环绕——@Around
  • 异常——@AfterThrowing
  • 最终——@After

注意 ,后置通知的注解并不是@After哦,别搞混 了。

写法其实差不太多,需要注意是的环绕通知的写法。

image-20230406001825607

思考1,5种通知都存在时,执行的顺序是什么样的?如果抛出异常,执行的顺序是什么样的?

引用老杜的案例:无异常的执行顺序如下

image-20230406002226310

一旦捕获异常,后置通知和后环绕就被抛弃了!

  • 后环绕也有特殊之处——如果是try…catch内捕获的话,后环绕也会执行

image-20230406001952142

思考2:切面的先后顺序(如果一个业务存在多个切面,如何排序?如何执行

在实际业务中,往往存在多个切面,如何做好排序呢?

其实Spring的开发者已经想到了这种情况,解决方法也很简单——

在切面类上加入注解@Order(int num),Order注解的数字越小,优先级越高!

  • 这里老杜没有提到的细节,比如同数字或者都无Order注解的情况,以及不同优先级的切面先后执行顺序是怎么样的?(比如环绕通知的先后执行顺序是什么样的?

测试2:同数字或者都无Order注解的情况,是如何执行的

结论:不是随机的,而是按照某个顺序进行执行,规律不明但是固定

测试2:不同优先级的切面先后执行顺序

测试设置 log的order为0,auth的order为1,测试结果如下:

结论:不同切面的执行顺序的:

  • 优先级高的前置/前环绕通知 会先执行
  • 优先级高的后置/后环绕通知 会后执行

image-20230406144853905

6、写法优化(切点优化pointcut)

写的切面多了,发现切面表达式重复写了很多,这里老杜介绍了 如何优化这个问题,具体实现如下

  • 在切面类中定义一个方法(任意名称)
  • 在该方法上加入@Pointcut注解,并在注解内写入 切面表达式
  • 在其他的切面方法上使用注解,就可以直接传入该方法名即可

这个简化的写法也可以跨类使用,只需要写上全限定类名(包名+类名)即可

优化后的切面类

package com.zhc.aop.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component("logAspect")
@Aspect     // 声明这是一个切面
public class LogAspect {

    // 优化写法
    @Pointcut("execution(* com.zhc.aop.service.UserService.*(..))")
    public void pointcut(){

    }


    @Before("pointcut()")   // 前置通知
    public void beforePoint(JoinPoint joinPoint){
        System.out.println("前置通知");
//        System.out.println(joinPoint);  // 打印结果:execution(void com.zhc.aop.service.UserService.loginA())
    }

    @AfterReturning("pointcut()")
    public void afterPoint(){
        System.out.println("后置通知");
    }

    @Around("pointcut()")
    public void aroundPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前。。。");
        joinPoint.proceed();
        System.out.println("环绕后。。。");
    }


    @AfterThrowing("pointcut()")
    public void throwPoint(){
        System.out.println("异常通知");
    }

    @After("pointcut()")
    public void finallyPoint(){
        System.out.println("最终通知");
    }
}

7、拓展用法,JointPoint

前面的环绕通知有用到了一个 ProcessJoinPoint ,其实在编写其他类型的通知类时,也可以通过JoinPoint来获取目标方法

  • 比如直接尝试打印该joinpoint对象——可以看出来打印的结果就是一个切面表达式:

    打印结果:execution(void com.zhc.aop.service.UserService.loginA())
    

8、全注解开发优化

可以优化掉配置文件spring.xml

声明一个配置类,配置思路如下

  1. 声明一个配置类 加上@Configuration
  2. 声明组件扫描
  3. 声明aspectj的自动扫描
  4. 修改对应的测试/调用代码
修改后的配置类
package com.zhc.aop.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.zhc.aop")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}

测试类
@Test
public void TestProxyByAnno(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.loginA();

}

9、基于XML开发的AOP

略,作为了解。主要是就是xml文件的书写和阅读,截图老杜写好的xml文件

image-20230406151405523

依然推荐大家去看动力节点的视频,更有助于理解

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开心档之C++ 运算符

目录 C 运算符 算术运算符 实例 实例 关系运算符 实例 实例 逻辑运算符 实例 实例 位运算符 实例 实例 赋值运算符 实例 实例 杂项运算符 C 中的运算符优先级 实例 实例 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 内置了丰富的运算符&…

算法设计-二分

一、有序和单调 ​ 二分本质上是一种更加智能的搜索状态空间的方式&#xff0c;他需要状态空间的状态呈现一种“有序的一维数组”的形式&#xff0c;然后再进行搜索。所以一开始的排序是无法避免的。 ​ 因为二分的写法问题&#xff0c;所以应当怎样排序也是有一定讲究的&…

黑马程序员 linux 学习笔记入门部分合集

ubuntu 安装 本课程使用 ubuntu 系统。 ubuntu 官网 - download。 上面会显示有两个版本&#xff0c;每年 ubuntu 发布两个版本&#xff0c;LTS 是长期维护版&#xff0c;所以相对会较稳定。 介绍 Linux 发行版本 不管什么版本&#xff0c;内核都是一样的。 RPM based&a…

“遥感+”蓝碳储量估算、红树林信息提取与论文写作

详情点击链接&#xff1a;“遥感”蓝碳储量估算、红树林信息提取与论文 一&#xff0c;光谱遥感数据及预处理 .1高光谱遥感数据 高光谱分辨率遥感是用很窄而连续的光谱通道对地物持续遥感成像的技术。在可见光到短波红外波段其光谱分辨率高达纳米数量级。高光谱图像数据…

Linux-Vim

一、Vim 配置 ​ vim界面打开以后很丑就不提了&#xff0c;关键有很多基本功能没有办法实现&#xff0c;所以需要自己配置&#xff0c;如果是linux系统&#xff0c;那么应该找到 /usr/share/vim/.vimrc​ 如果是windows装完git以后会自动一个vim&#xff0c;此时应该找到 Gi…

电子招标采购系统—企业战略布局下的采购寻源

​ 智慧寻源 多策略、多场景寻源&#xff0c;多种看板让寻源过程全程可监控&#xff0c;根据不同采购场景&#xff0c;采取不同寻源策略&#xff0c; 实现采购寻源线上化管控&#xff1b;同时支持公域和私域寻源。 询价比价 全程线上询比价&#xff0c;信息公开透明&#xff…

vue + table原生实现表格单元列列宽可重置

const tableMixin {data() {return {dragState: {}, // 记录子表的列宽移动的一些数值dragging: false // 子表是否在重置列宽}},methods: {handleMouseMove(event) {let target event.targetwhile (target && target.tagName ! TH) {target target.parentNode}if (…

算法竞赛ICPC、CCPC、NIO、蓝桥杯、天梯赛

算法竞赛前言一、为什么学习算法竞赛二、学习算法的阶段三、算法竞赛具体学习内容1、基础数据结构1.1、链表1.1.1、动态链表1.1.2、静态链表1.1.3、STL list1.2、队列1.2.1、STL queue1.2.2、手写循环队列1.2.3、双端队列和单调队列1.2.4、优先队列1.3、栈1.3.1、STL stack1.3.…

23 - x的平方根,快速幂,超级次方

文章目录1. x的平方根2. 快速幂3. 超级次方1. x的平方根 二分查找 class Solution { public:int mySqrt(int x) {int left 1, right x;while(left < right){int mid left (right - left) / 2;if(mid > x / mid){right mid - 1;}else if(mid < x / mid){left mi…

OpenShift 4 - Red Hat 是如何对容器镜像的安全风险进行评估分级的

《OpenShift / RHEL / DevSecOps 汇总目录》 文章目录RedHat 对 CVE 的风险级别的评级通用漏洞评分系统 CVSS红帽严重性分级RedHat 对容器镜像的整体风险的分级云原生应用的运行载体是容器镜像&#xff0c;因此容器镜像的安全便是云原生应用安全的关键因素。为此&#xff0c;Re…

联合解决方案|亚信科技AntDB携手蓝凌软件,助推企业数字化办公转型升级

随着企业数字化转型的深入&#xff0c;企业对于协同办公、移动门户、数字运营、智能客服等方面的需求越来越高&#xff0c;数智化正成为催生新动能和新优势的关键力量。数字化的办公平台可以帮助企业实现各类信息、流程的集中化、数字化和智能化管理&#xff0c;为企业管理者提…

老板,你的绩效管理该升级了!

中小企业的绩效考核&#xff0c;一直是一个备受关注的话题。虽然传统的绩效考核理论已经非常成熟&#xff0c;但是在实际应用中&#xff0c;我们往往会遇到各种各样的问题。因此&#xff0c;在选择绩效考核工具和方法时&#xff0c;我们应该注重实用性&#xff0c;不断探索新的…

32位单片机MM32G0140免费申请样品及开发板

灵动微MM32G系列MCU搭载ArmCortex-M0或安谋科技“星辰”STAR-MC1处理器&#xff0c;率先推出的产品支持64KB到128KB Flash存储范围&#xff0c;提供从20脚到64脚封装选项&#xff0c;适用于广泛的智能工业与电机&#xff0c;物联网&#xff0c;智能家居和消费类等应用。其中&am…

比亚迪车载Android开发岗三面经历~

前言 首先&#xff0c;我想说一下我为什么会想去比亚迪这样的车企做车载Android开发。我是一名有5年经验的Android开发工程师&#xff0c;之前一直在互联网软件公司工作&#xff0c;做过移动端App和IoT产品的开发。但我一直对汽车领域很感兴趣&#xff0c;也希望自己的技术能应…

【python+requests】接口自动化测试

这两天一直在找直接用python做接口自动化的方法&#xff0c;在网上也搜了一些博客参考&#xff0c;今天自己动手试了一下。 一、整体结构 上图是项目的目录结构&#xff0c;下面主要介绍下每个目录的作用。 Common:公共方法:主要放置公共的操作的类&#xff0c;比如数据库sql…

前端算法codewhy第一章:队列

目录 认识队列 生活中的队列 开发中队列的应用 队列类的创建 队列的常见操作 击鼓传花 import ArrayQueue from "./01_实现队列结构Queue";function hotPotato(names: string[], num: number): number {if (names.length 0) return -1;// 1.创建队列结构const queue…

数据库安装与使用、mysql、sqlite、mongodb

一、MongoDB MongoDB Server 安装 优秀文章&#xff1a; link1 link2 MongoDB 是一个文档数据库&#xff0c;旨在简化开发和扩展。 下载 官网(社区版) &#xff1a;https://www.mongodb.com/try/download/community 下载完后一路安装即可。 添加环境变量 开启 mongodb服务…

[Linux]环境变量

一.什么是环境变量 为了满足不同的运行场景&#xff0c;操作系统预先设置了一大批全局变量&#xff0c;这种可以指定操作系统运行环境的变量就是环境变量。 我们平常使用的指令本质上也是用C语言实现的一个个小程序&#xff0c;但是我们在执行我们自己的可执行程序时往往是类…

go调用docker远程API(二)-docker API 的容器操作

文章目录1 获取容器列表2 查看指定容器信息3. 查看容器日志4 创建容器4.1 简单使用4.1.1 语法4.1.2 完整示例4.2 端口映射4.2.1 语法4.2.2 完整示例4.3 挂载本机目录/文件4.3.1 语法4.3.2 完整代码5. 启动容器6 停止容器7 删除&#xff08;已停止的&#xff09;容器8 进入容器执…

线程池的7种创建方式

文章目录普通方式创建线程存在的问题什么是线程池线程池的好处线程池设计思路线程池相关类的继承关系线程池的创建方式固定容量线程池——FixedThreadPool相关构造方法示例运行结果缓存线程池——CachedThreadPool相关构造方法示例运行结果单线程线程池——SingleThreadExecuto…
最新文章