Spring及SpringBoot中AOP的使用

news2024/11/15 22:32:58

Spring中AOP示例

<dependencies>

    <!--Spring核心包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.6</version>
    </dependency>

    <!--引入SpringBean-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.6</version>
    </dependency>

    <!--引入context包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.6</version>
    </dependency>

    <!--引入表达式jar包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>5.3.6</version>
    </dependency>

    <!--引入日志依赖-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>

    <!--引入测试包-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>

    <!--引入AOP 以下三个包-->
    <!--引入AOP包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.6</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.6</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.9.6</version>
    </dependency>
    <!--当下所有的jar包都需要手动添加依赖,并且需要确定依赖的关系,对初学者不友好-->
</dependencies>

配置类

/**
 * @author TB
 * @date 2020/2/12 0:14
 */
@Configuration//标识是个配置类
@ComponentScan("com")//包扫描路径
//SpringBoot的AOP是默认开启的,不需要加注解@EnableAspectJAutoProxy” 这里用的Spring所以要加
//让Spring认识加了@Aspect类的注解  不然Spring不认识切面@Aspect
//其实是启动AOP注解创建代理
//默认启用JDK动态代理,当目标对象没有实现接口时则采用CGlib代理
//该注解有个proxyTargetClass属性,默认是false 如果改成@EnableAspectJAutoProxy(proxyTargetClass=true)则使用CGlib代理
//JDK代理创建速度快,运行时稍慢,CBlib代理创建时速度慢,运行速度快
//可以通过属性的true或false来指定JDK代理和CGlib代理  默认CBG代理  找不到实现类的话会自动用CGlib代理
@EnableAspectJAutoProxy
//@EnableAsync//告诉spring框架启动时创建线程池 然后在需要异步请求时在池里拿线程直接用 然后需要用异步请求的方法上加@Async
public class SpringConfig {


}

标注的AOP类

/**
 *  AOP(面向切面编程) 主要利用**动态代理**的模式 **降低程序的耦合度,扩展业务功能方法.**
 * 1.AOP需要被Spring容器管理 @Component
 * 2.标识该类是AOP切面   @Aspect
 * 关于AOP名词介绍
 * 1).连接点: 用户可以被扩展的方法    其实我们将自定义注解放到目标方法上做标识,那么该注解其实就是个连接点
 * 2).切入点: 用户实际扩展的方法      确定了连接点,那么该方法也就是个切入点
 * 3).通知:  扩展方法的具体实现       5个通知
 * 4).切面: 将通知应用到切入点的过程
 *
 *   通知类型(必会)
 * 1. before:  在目标方法执行之前执行
 * 2. afterReturning: 在目标方法执行之后返回时执行
 * 3. afterThrowing:  在目标方法执行之后,抛出异常时执行
 * 4. after:	无论程序是否执行成功,都要最后执行的通知
 * 5. around: 在目标方法执行前后 都要执行的通知(完美体现了动态代理模式)
 * 	功能最为强大 只有环绕通知可以控制目标方法的执行
 *
 * 关于通知方法总结:
 * 	1.环绕通知是处理业务的首选.  可以修改程序的执行轨迹
 * 	2.另外的四大通知一般用来做程序的监控.(监控系统) 只做记录
 * @author TB
 * @date 2020/2/12 0:24
 */
@Component
//虽然标识了该类为AOP切面 但是Spring容器默认不能识别切面注解,需要手动配置
//需要在配置类SpringConfig里加上注解@EnableAspectJAutoProxy
@Aspect
public class SpringAOP {
    /**
     * 切入点表达式
     * 概念:当程序满足切入点表达式,才能进入切面,执行通知方法.
     *
     * 1.bean("bean的ID")  根据beanId进行拦截  只能匹配一个
     * 2.within("包名.类名") 可以使用通配符*?      能匹配多个.
     * 	粒度: 上述的切入点表达式 粒度是类级别的.  粗粒度.
     * 3.execution(返回值类型   包名.类名.方法名(参数列表...))
     * 	粒度: 控制的是方法参数级别. 所以粒度较细.   最常用的.
     * 4.@annotation(包名.注解名)     只拦截注解.
     * 	粒度: 注解是一种标记 根据规则标识某个方法/属性/类    细粒度
     */

    /**
     * 切入点表达式练习
     * within:
     *  1.within(com.jt.*.DeptServiceImpl)   一级包下的类
     *  2.within(com.jt..*.DeptServiceImpl)  ..代表多级包下的类
     *  3.within(com.jt..*)  包下的所有的类
     *
     * execution(返回值类型 包名.类名.方法名(参数列表))
     *  1.execution(* com.jt..*.DeptServiceImpl.add*())
     *  注释: 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类
     *        的add开头的方法 ,并且没有参数.
     *
     *  2.execution(* com.jt..*.*(..))
     *  注释: 返回值类型任意,com.jt包下的所有包的所有类的所有方法 任意参数.
     *
     *  3.execution(int com.jt..*.*(int))
     *  4.execution(Integer com.jt..*.*(Integer))
     *  强调: 在Spring表达式中没有自动拆装箱功能! 注意参数类型
     *
     * @annotation(包名.注解名)
     *     @Before("@annotation(com.jt.anno.Cache)")
     *    只拦截特定注解的内容.
     */


    //1.定义before通知
    //@Before("bean(deptServiceImpl)")//扫描的是一个类 因此该类里所有方法都被扩展到了
    //@Before("within(com.jt.service.DeptServiceImpl)")//和上面效果一样
    //@Before("execution(* com.jt.service.DeptServiceImpl.add*())")//*表示返回值类型任意 add*表示以add开头的方法名 最后()表示参数是空的
    //@Before("@annotation(com.jt.anno.Cache)")//意思有该注解 就作为切入点   因此用注解标识最常用(自定义个注解)

    /**
     * spring为了AOP动态获取目标对象及方法中的数据,则通过Joinpoint
     * JoinPoint是所有通知的公共参数,无论哪种通知里都可以使用
     * 在Before里可以获取
     * 对象做数据传递获取如:
     * 1.获取目标对象的类型
     * 2.获取目标方法的名称
     * 3.获取目标方法的参数
     * @param joinPoint
     */
    @Before("pointcut()")
    public void before(JoinPoint joinPoint){//前置方法一般作用获取参数,方法名,等等
        System.out.println("目标对象的Class类对象: "+joinPoint.getTarget().getClass());
        System.out.println("获取目标方法的方法签名: "+joinPoint.getSignature());
        System.out.println("获取目标对象的类名: "+ joinPoint.getSignature().getDeclaringTypeName());
        System.out.println("获取目标对象方法名: "+ joinPoint.getSignature().getName());
        System.out.println("获取目标方法参数: "+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("我是before通知");
    }


    //1.定义一个切入点
    @Pointcut("@annotation(com.jt.anno.Cache)")
    public void pointcut(){

    }

    //如果每个通知前都加个切入点表达式  那么也太冗余了 因此我们可以定义个切入点 其他通知都围绕切入点
    //@BafterReturning("@annotation(com.jt.anno.Cache)")
    /**
     * JoinPoint参数是所有通知方法公有的
     *AfterReturning是目标方法返回执行之后返回时执行
     * 可以记录方法的返回值
     * AfterReturning注解里 value和pointcut是相同的效果:也就是说
     * @AfterReturning(value="pointcut()",returning="result")和@AfterReturning(pointcut="pointcut()",returning="result")
     * 效果一样
     * returning:将方法的返回值,通过形参result(这个随便取名)来进行传递(Spring会将返回值赋值给你定义的这个变量)
     */
    @AfterReturning(value="pointcut()",returning="result")
    public void afterReturning(JoinPoint joinPoint,Object result){//这里注意 如果有需要用到JointPoint参数 那么必须放在第一个位置  不用可以去掉
        System.out.println("目标返回值结果是: "+result);
        System.out.println("我是AfterReturning的通知");
    }

    @AfterThrowing(pointcut = "pointcut()",throwing="e")//当目标方法执行时,抛出异常时,可以用AfterThrowing记录
    public void afterThrowing(Exception e){
        System.out.println("获取目标异常信息: "+e.getMessage());
        System.out.println("获取目标异常类型: "+e.getClass());
        System.out.println("我是AfterThrowing的通知,出现异常了");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("我是After的通知");
    }


    /**
     * 关于环绕通知的说明
     * 作用: 可以控制目标方法是否执行.
     * 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.
     * 注意事项:
     *  ProceedingJoinPoint 只能适用环绕通知
     * @return
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){//注意多参情况ProceedingJoinPoint要放第一位
        Object result = null;
        try {
            System.out.println("环绕通知开始");
            //1.执行下一个通知  2.执行目标方法 3.接收返回值
            Long start = System.currentTimeMillis();
            result = joinPoint.proceed();
            Long end = System.currentTimeMillis();
            System.out.println("耗时:"+(end-start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕通知结束");
        return result;
    }
}

切入点注解类

//控制注解的生命周期,什么时候起作用
@Retention(RetentionPolicy.RUNTIME)
//注解的作用对象:  类上   方法上 变量上
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
public @interface Cache {

}

测试

SpringBoot中使用AOP

/**
 * @author hrui
 * @date 2023/10/30 21:58
 */
@Component
@Aspect
public class SpringAOP {

    @Before("pointcut()")
    public void before(JoinPoint joinPoint){//前置方法一般作用获取参数,方法名,等等
//        System.out.println("目标对象的Class类对象: "+joinPoint.getTarget().getClass());
//        System.out.println("获取目标方法的方法签名: "+joinPoint.getSignature());
//        System.out.println("获取目标对象的类名: "+ joinPoint.getSignature().getDeclaringTypeName());
//        System.out.println("获取目标对象方法名: "+ joinPoint.getSignature().getName());
//        System.out.println("获取目标方法参数: "+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("我是before通知");
    }


    //1.定义一个切入点
    @Pointcut("@annotation(com.example.demo.aop.AnyAnno)")
    public void pointcut(){

    }


    @AfterReturning(value="pointcut()",returning="result")
    public void afterReturning(JoinPoint joinPoint, Object result){//这里注意 如果有需要用到JointPoint参数 那么必须放在第一个位置  不用可以去掉
       // System.out.println("目标返回值结果是: "+result);
        System.out.println("我是AfterReturning的通知");
    }

    @AfterThrowing(pointcut = "pointcut()",throwing="e")//当目标方法执行时,抛出异常时,可以用AfterThrowing记录
    public void afterThrowing(Exception e){
//        System.out.println("获取目标异常信息: "+e.getMessage());
//        System.out.println("获取目标异常类型: "+e.getClass());
        System.out.println("我是AfterThrowing的通知,出现异常了");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("我是After的通知");
    }


    /**
     * 关于环绕通知的说明
     * 作用: 可以控制目标方法是否执行.
     * 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.
     * 注意事项:
     *  ProceedingJoinPoint 只能适用环绕通知
     * @return
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){//注意多参情况ProceedingJoinPoint要放第一位
        Object result = null;
        try {
            System.out.println("环绕通知开始");
            //1.执行下一个通知  2.执行目标方法 3.接收返回值
            result = joinPoint.proceed();
            System.out.println("环绕通知里方法执行完了");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕通知结束");
        return result;
    }
}

切入点注解类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface AnyAnno {

}

自己随便测试下

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

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

相关文章

Java工具库——commons-lang3的50个常用方法

未来的你&#xff0c;我亲爱的女孩&#xff0c;愿此刻无忧无虑&#xff0c;开心&#xff0c;快乐… 工具库介绍 Apache Commons Lang 3&#xff08;通常简称为Commons Lang 3&#xff09;是Apache Commons项目中的一个Java工具库&#xff0c;它提供了一系列实用的工具类和方法…

3.6每日一题(线性方程求通解)

1、判断类型选择方法&#xff1a;发现以y为未知函数&#xff0c;以x为自变量&#xff0c;不符合我们学过的类型 2、此时有两种方法&#xff1a; &#xff08;1&#xff09;x 与 y 对调&#xff0c;此时 x 为未知函数&#xff0c;y 为自变量 &#xff08;2&#xff09;变量代换…

【Mybatis-Plus】常见的@table类注解

目录 引入Mybatis-Plus依赖 TableName 当实体类的类名在转成小写后和数据库表名相同时 当实体类的类名在转成小写后和数据库表名不相同时 Tableld TableField 当数据库字段名与实体类成员不一致 成员变量名以is开头&#xff0c;且是布尔值 ​编辑 成员变量名与数据库关…

轻量级 IDE 文本编辑器 Geany 发布 2.0

Geany 是功能强大、稳定、轻量的开发者专用文本编辑器&#xff0c;支持 Linux、Windows 和 macOS&#xff0c;内置支持 50 多种编程语言。 2005 年Geany 发布首个版本 0.1。上周四刚好是 Geany 诞生 18 周年纪念日&#xff0c;官方发布了 2.0 正式版以表庆祝。 下载地址&#…

小红书app拉新上线了 适合网推社群和校园渠道作业

小红书app签到拉新上线了可以通过“聚量推客”进行申请&#xff0c;下面大概是要求和流程 要求网推社群渠道或者地推校园渠道&#xff0c;其它类型渠道禁止

R语言绘图-5-条形图(修改坐标轴以及图例等)

0. 说明&#xff1a; 1. 绘制条形图&#xff1b; 2. 添加文本并调整位置&#xff1b; 3. 调整x轴刻度的字体、角度及颜色&#xff1b; 4. 在导出pdf时&#xff0c;如果没有字体&#xff0c;该怎么解决问题&#xff1b; 1. 结果&#xff1a; 2. 代码&#xff1a; library(ggp…

sql--索引使用 ---覆盖索引

覆盖索引 Select 后接 * 走id索引才是最优&#xff0c;使用二级索引则需要回表&#xff08;性能稍差&#xff09; 前缀索引 Create index 索引名 on 表名( 字段名( n ) ) n数字 n代表提取这个字符串的n个构建索引 &#xff1f;&#xff1f;那么 n 为几性能是最好的呢&…

测试C#调用Aplayer播放视频(1:加载Aplayer控件)

微信公众号“Dotnet跨平台”的文章《开源精品&#xff0c;使用 C# 开发的 KTV 点歌项目》中使用了迅雷开源APlayer播放引擎。最近在学习有哪些能拿来播放视频的组件或控件&#xff0c;于是准备试试&#xff0c;根据文章中的介绍&#xff0c;在迅雷APlayer播放引擎网站中下载了A…

计算机出现msvcr110.dll文件丢失问题的五种常见方法

msvcr110.dll是Microsoft Visual C Redistributable的一部分&#xff0c;它是Windows操作系统上的一个动态链接库文件。它包含了许多与C编程相关的函数和库&#xff0c;用于支持应用程序的运行。如果电脑上缺少msvcr110.dll文件&#xff0c;可能会导致一些应用程序无法正常运行…

TiDB x 汉口银行丨分布式数据库应用实践

汉口银行是一家城市商业银行&#xff0c;近年来专注科技金融、民生金融等领域。在数据库国产化改造中&#xff0c;汉口银行引入了 TiDB 数据库&#xff0c;并将其应用在重要业务系统&#xff1a;头寸系统中&#xff0c;实现了一栈式的数据服务&#xff0c;同时满足了高并发、低…

论文范文:论面向服务的架构

网络文章,仅供学习,建议您考试不要抄范文哦!摘要:2016年8月,我参与了胶凝砂砾石坝施工质量监控系统的开发工作,该系统旨在帮助水利工程建设法人单位、施工企业、监理机构及相关政府部门解决水利工程建设施工质量监控和工程项目管理等问题。我在该项目中担任系统架构设计师…

MDM如何实现不同场景的数据管理

MDM基础数据平台主要是进行清洗和治理企业的主数据&#xff0c;使企业的主数据具有唯一性、准确性、一致性和及时性&#xff0c;但平台在不同的场景中起到的作用也不同。 不同项目的MDM作用不同&#xff0c;在数据中台中MDM主要是作为维度表和一些基础数据的数据上报功能&…

Linux——Linux权限

Linux权限 前言一、shell命令以及运行原理二、Linux权限的概念Linux权限管理文件访问者的分类&#xff08;人&#xff09;文件类型和访问权限&#xff08;事物属性&#xff09;文件权限值的表示方法文件访问权限的相关设置方法 file指令目录的权限粘滞位 总结 前言 linux的学习…

Android多张图片rotation旋转角度叠加/重叠堆放

Android多张图片rotation旋转角度叠加/重叠堆放 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto"…

Panda3d 介绍

Panda3d 介绍 文章目录 Panda3d 介绍Panda3d 的安装Panda3d 的坐标系统介绍Panda3d 的运行Panda3d 加载一个熊猫父节点和子节点之间的关系 验证Panda3d 的坐标系统X 轴的平移Y 轴的平移Z 轴的平移X 轴的旋转Y 轴的旋转Z 轴的旋转 Panda3D是一个3D引擎:一个用于3D渲染和游戏开发…

C++ 自引用指针this(整理)

使用例子&#xff1a; #include <iostream> #include <Windows.h> using namespace std; class A { public:A(int x1){x x1;}void disp(){cout<<"this"<<this<<" when x"<<this->x<<endl;} private:int x;…

MSQL系列(十二) Mysql实战-为什么索引要建立在被驱动表上

Mysql实战-left/right/inner join 使用详解 前面我们讲解了BTree的索引结构&#xff0c;也详细讲解下 left Join的底层驱动表 选择原理&#xff0c;那么今天我们来实战一下 left join&#xff0c;right join&#xff0c;inner join 等sql命令&#xff0c;看看到底如何用以及如…

MySQL主从复制延时

主从复制原理 MySQL主从复制是MySQL数据库中一种数据备份和数据同步的方式。它将一台MySQL服务器&#xff08;主服务器&#xff09;的数据同步到另外一台MySQL服务器&#xff08;从服务器&#xff09;。主服务器是数据的提供者&#xff0c;而从服务器是数据的接收者。主从复制…

23秋季学期期中考核(密码题)

师兄新学的Decode姿势 题目给出的 const SM4 require("gm-crypt").sm4; var payload "inu7s0XGslLFsPe0xok/nxqwtceG0cuMoh7hBbpwyNBrn3g/lMdcFYobv/KeRgWwI/TIA4qMtjqpNKOet E3TD0gWoSknGF6OhkRJ3LxrNUZ/Mi8ISz3xrDLawIY8NwbalYcQUGWtVjdC3nJL…

对象补充-原型和函数原型-创建对象

defineProperties可以定义多个属性描述符 var obj {// 私有属性&#xff08;js里面是没有严格意义的私有属性&#xff09;_age: 18,_eat: function() {} }Object.defineProperties(obj, {name: {configurable: true,enumerable: true,writable: true,value: "why"}…