Spring AOP - 配置文件方式实现

news2024/11/14 2:25:16

目录

AOP基础概念

示例1:模拟在com.text包及子包项下所有类名称以ServiceImpl结尾的类的所有方法执行前、执行后、执行正常后返回值、执行过程中出异常的情况

示例2:统计com.text包及子包项下所有类名称以DaoImpl结尾的类的所有方法执行时长情况


AOP基础概念

AOP:Aspect Oriented Programming 面向切面编程

使用场景:将一些通用的功能封装成切面类,切面类作用在目标类方法的前后,并通过自动插拔实现目标类方法的前后逻辑,示意图如下:

上述的示意图显示,Aspect切面类横向贯穿了3个目标类方法的执行逻辑之前,由此可以看出,AOP实现需要如下组件:

  1. 切面类(Aspect类)
  2. 切点(Pointcut),即上图的各个目标方法,通过切点表达式(execution)实现
  3. 连接点(JoinPoint) ,切面和切点之间的连接信息,可以理解为横切面和切点的交汇处
  4. 通知(Advice) ,在目标类方法的之前、之后还是环绕执行切面逻辑

Spring AOP实现需要引入aspectjweaver依赖

<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.6</version>
</dependency>

示例1:模拟在com.text包及子包项下所有类名称以ServiceImpl结尾的类的所有方法执行前、执行后、执行正常后返回值、执行过程中出异常的情况

1、配置文件:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<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"
       xmlns="http://www.springframework.org/schema/beans"
       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.text"/>-->
    <bean id="studentService" class="com.text.service.impl.StudentServiceImpl"/>
    <!-- 切面类交给IOC容器实例 -->
    <bean id="methodAspect" class="com.text.aspect.MethodAspect"/>
    <!-- aop配置-->
    <aop:config>
        <!-- 切点配置,expression表示com.text所有包及子包项下所有服务层(以ServiceImpl结束的类)的所有方法-->
        <aop:pointcut id="pointcut" expression="execution(public * com.text..*ServiceImpl.*(..))"/>
        <!-- 切面配置-->
        <aop:aspect ref="methodAspect">
            <!-- 切点方法执行之前执行-->
            <aop:before method="methodInvokeBefore" pointcut-ref="pointcut"/>
            <!-- 切点方法执行之后执行-->
            <aop:after method="methodInvokeAfter" pointcut-ref="pointcut"/>
            <!-- 切点方法执行之后带返回值-->
            <aop:after-returning method="methodInvokeAfterReturn" returning="ret" pointcut-ref="pointcut"/>
            <!-- 切点方法执行时出现异常-->
            <aop:after-throwing method="methodInvokeException" throwing="thr" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>

2、服务类及方法(pointcut)

package com.text.service.impl;

import com.text.entity.Course;
import com.text.entity.Student;
import com.text.service.StudentService;

public class StudentServiceImpl implements StudentService {

    @Override
    public void save(Student student) {
        System.out.println(student + "正在被保存...");
    }

    @Override
    public void deleteById(String id) {
        System.out.println("学生id=" + id + "的记录已被删除...");
    }

    @Override
    public void updateById(String id) throws Exception{
        System.out.println("学生id=" + id + "的记录正在被修改...");
        throw new Exception("修改学生信息出异常");
    }

    @Override
    public Student searchById(String id) {
        System.out.println("已查询到学生id=" + id + "的记录...");
        return new Student("张三",20,new Course("计算机"));
    }
}

3、切面类(aspect) 

package com.text.aspect;

import org.aspectj.lang.JoinPoint;

/**
 * 定义方法切面类
 */
public class MethodAspect {
    public void methodInvokeBefore(JoinPoint joinPoint) {
        String targetClassName = joinPoint.getTarget().getClass().getName();//获取切面执行的目标类
        String methodName = joinPoint.getSignature().getName();
        System.out.println(targetClassName + "." + methodName + "方法执行之前的处理逻辑...");
    }

    public void methodInvokeAfter(JoinPoint joinPoint) {
        String targetClassName = joinPoint.getTarget().getClass().getName();//获取切面执行的目标类
        String methodName = joinPoint.getSignature().getName();
        System.out.println(targetClassName + "." + methodName + "方法执行之后的处理逻辑...");
    }

    public void methodInvokeAfterReturn(JoinPoint joinPoint,Object ret) {
        String targetClassName = joinPoint.getTarget().getClass().getName();//获取切面执行的目标类
        String methodName = joinPoint.getSignature().getName();
        System.out.println(targetClassName + "." + methodName + "方法执行之后返回的结果为:" + ret);
    }

    public void methodInvokeException(JoinPoint joinPoint,Throwable thr) throws Throwable {
        String targetClassName = joinPoint.getTarget().getClass().getName();//获取切面执行的目标类
        String methodName = joinPoint.getSignature().getName();
        System.out.println(targetClassName + "." + methodName + "方法执行中的异常信息为:" + thr.getMessage());
        throw thr;
    }
}

4、测试类 Application

package com.text;
import com.text.entity.Course;
import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        StudentService studentService = context.getBean("studentService", StudentService.class);
        studentService.save(new Student("张三",20,new Course("计算机")));
        System.out.println("======save end========");
        studentService.deleteById("1");
        System.out.println("======delete end ========");
        Student student = studentService.searchById("1");
        System.out.println("======search end ========");
        studentService.updateById("1");
        System.out.println("======update end ========");
    }
}

5、输出结果:

示例2:统计com.text包及子包项下所有类名称以DaoImpl结尾的类的所有方法执行时长情况

此需求如果按照之前的advice至少需要写2个,一个是aop:before,一个是aop:after,方法的总执行时间需要after的当前时间减去before的当前时间,这2个时间如何联通也存在困难,AOP 提供一种自定义的通知执行时机-Around Advice(环绕通知)可以轻松解决此需求

1、配置文件:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<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"
       xmlns="http://www.springframework.org/schema/beans"
       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.text"/>-->
    <bean id="studentDao" class="com.text.dao.impl.StudentDaoImpl"/>
    <!-- 切面类交给IOC容器实例 -->
    <bean id="methodAspect" class="com.text.aspect.MethodAspect"/>
    <!-- aop配置-->
    <aop:config>
        <!-- 切点配置,expression表示com.text所有包及子包项下所有服务层(以DaoImpl结束的类)的所有方法-->
        <aop:pointcut id="pointcut" expression="execution(public * com.text..*DaoImpl.*(..))"/>
        <!-- 切面配置-->
        <aop:aspect ref="methodAspect">
            <!-- 自定义的环绕通知-->
            <aop:around method="countMethodInvokeTime" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>

2、服务类及方法(pointcut)

package com.text.dao.impl;
import com.text.dao.StudentDao;

public class StudentDaoImpl implements StudentDao {

    @Override
    public void getById(String id) throws Exception {
        Thread.sleep(1000);
        System.out.println("查询学生id=" + id + "的信息");
    }
}

3、切面类(aspect) 

package com.text.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

import java.util.Date;

/**
 * 定义方法切面类
 */
public class MethodAspect {
    public void countMethodInvokeTime(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("目标方法执行之前记录初始时间...");
        Date startTime = new Date();
        try {
            proceedingJoinPoint.proceed();//执行目标方法 即:StudentDaoImpl.getById方法
            System.out.println("目标方法执行之后记录结束时间...");
            String methodName = proceedingJoinPoint.getTarget().getClass().getName() + "." +
                    proceedingJoinPoint.getSignature().getName();
            Date endTime = new Date();
            System.out.println(methodName + "方法执行总时长为:" + (endTime.getTime() - startTime.getTime()) + "毫秒");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

4、测试类 Application

package com.text;

import com.text.dao.StudentDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        StudentDao studentDao = context.getBean("studentDao", StudentDao.class);
        studentDao.getById("1");
        System.out.println("======getById end========");
    }
}

5、输出结果:

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

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

相关文章

汽车总线之---- CAN FD总线

CAN FD 最高可支持8M/s的通信速率&#xff0c;从传统CAN到CAN FD的转换是很容易实施和推广的。 CAN FD报文的帧&#xff1a;标准帧&#xff0c;扩展帧 CAN FD 标准帧结构 CAN FD 报文的标准帧与CAN 报文的标准帧的区别 CAN FD 报文的标准帧与CAN FD报文的扩展帧的区别&…

lsof可以查看当前系统中正在被使用的文件,包括动态库

lsof的英文是 list open files lsof打印结果的最后一列是Name&#xff0c;表示正在被使用或打开的文件名或动态库名 lsof直接回车&#xff0c;会显示很多&#xff0c;可以配合more命令查看 一个文件或动态库可能被多个进程打开&#xff0c;lsof会显示多行 lsof | more -1…

uniapp小程序持续获取用户位置信息,后台位置获取

做一个小程序持续获取用户位置信息的功能&#xff0c;即使小程序切换到后台也能继续获取&#xff0c;getLocation这个api只有小程序在前台才能获取位置&#xff0c;所以不用这个 先申请一个腾讯地图key 在uniapp项目配置源码视图里加上这个代码 先获取权限&#xff0c;再开启…

[项目:微服务即时通讯系统客户端(基于C++QT)]三,左侧界面搭建

三&#xff0c;左侧界面搭建 一&#xff0c;导入 先把MainWidget类做成“单例类” 采用的是单例模式&#xff0c;让某一个类&#xff0c;在指定进程中只有唯一的实例 先看一下MainWidget的框架 QWidget//这部分是头文件保护宏&#xff0c;确保该头文件只被包含一次&#x…

240922-chromadb的基本使用

A. 背景介绍 ChromaDB 是一个较新的开源向量数据库&#xff0c;专为高效的嵌入存储和检索而设计。与其他向量数据库相比&#xff0c;ChromaDB 更加专注于轻量化、简单性和与机器学习模型的无缝集成。它的核心目标是帮助开发者轻松管理和使用高维嵌入向量&#xff0c;特别是与生…

【软件工程】数据流图和数据字典

一、数据流图 3.符号 分析结果 二、数据字典 例题 选择题

使用build_chain.sh离线搭建匹配的区块链,并通过命令配置各群组节点的MySQL数据库

【任务】 登陆Linux服务器&#xff0c;以MySQL分布式存储方式安装并部署如图所示的三群组、四机构、 七节点的星形组网拓扑区块链系统。其中&#xff0c;三群组名称分别为group1、group2和group3&#xff0c; 四个机构名称为agencyA、agencyB、agencyC、agencyD。p2p_port、cha…

Python | Leetcode Python题解之第429题N叉树的层序遍历

题目&#xff1a; 题解&#xff1a; class Solution:def levelOrder(self, root: Node) -> List[List[int]]:if not root:return []ans list()q deque([root])while q:cnt len(q)level list()for _ in range(cnt):cur q.popleft()level.append(cur.val)for child in c…

爬虫过程 | 蜘蛛程序爬取数据流程(初学者适用)

蜘蛛程序&#xff08;也称网络爬虫&#xff0c;是搜索引擎的重要组成部分&#xff09; 主要功能&#xff1a;遍历互联网&#xff0c;抓取网站信息并建立索引&#xff0c;便于用户在搜索引擎中检索到最新的网页内容工作原理&#xff1a;从初始网站页面的URL开始&#xff0c;发送…

qt-C++笔记之Q_DECLARE_METATYPE和qRegisterMetaType

qt-C笔记之Q_DECLARE_METATYPE和qRegisterMetaType code review! 文章目录 qt-C笔记之Q_DECLARE_METATYPE和qRegisterMetaType一.Q_DECLARE_METATYPE使用方法应用场景 二.为什么需要注册类型&#xff1f;三.使用 Q_DECLARE_METATYPE 处理自定义类型的简短示例3.1.自定义类型定…

《独孤九剑》游戏源码(客户端+服务端+数据库+游戏全套源码)大小2.38G

《独孤九剑》游戏源码&#xff08;客户端服务端数据库游戏全套源码&#xff09;大小2.38G ​ 下载地址&#xff1a; 通过网盘分享的文件&#xff1a;【源码】《独孤九剑》游戏源码&#xff08;客户端服务端数据库游戏全套源码&#xff09;大小2.38G 链接: https://pan.baidu.co…

生信服务器 | 组蛋白甲基化修饰、DNA亲和纯化测序、优青博导团队指导设计、解读实验结果。

查看原文>>>生信服务器 | 组蛋白甲基化修饰、DNA亲和纯化测序、优青博导团队免费指导设计、解读实验结果、一台服务器解决您所有的分析困扰!

VLDB 2024 圆桌会议回顾:展望物联网与 AI 时代的时序数据库

回顾我们在 VLDB 2024 8 月 26 日至 8 月 30 日&#xff0c;数据库领域的顶级国际会议 VLDB 2024 在广州举行。IoTDB 最新研发成果的三篇论文被本次大会录用&#xff08;详见&#xff1a;IoTDB 在顶级会议 VLDB 2024&#xff1a;四篇最新论文入选&#xff0c;特邀做 TPC 报告与…

6.7泊松噪声

基础概念 在OpenCV联合C中给一张图片添加泊松噪声&#xff08;Poisson Noise&#xff09;可以通过生成随机数并在图像的每个像素上加上这些随机数来实现。泊松噪声是一种统计分布服从泊松分布的噪声&#xff0c;通常用于模拟光子计数等场景。 使用泊松噪声的场景 泊松噪声通…

【记录】Excel|不允许的操作:合并或隐藏单元格出现的问题列表及解决方案

人话说在前&#xff1a;这篇的内容是2022年5月写的&#xff0c;当时碰到了要批量处理数据的情况&#xff0c;但是又不知道数据为啥一直报错报错报错&#xff0c;说不允许我操作&#xff0c;最终发现是因为存在隐藏的列或行&#xff0c;于是就很无语地写了博客&#xff0c;但内容…

Codeforces Round 972 (Div. 2) E2. Subtangle Game (Hard Version)(博弈+双指针 sg函数思想)

题目 思路来源 稲葉廻代码 题解 这个题比easy version的数据范围大了比较多&#xff0c; 不能再直接dp[i][j][k]表示数组a的第i个做开始局面时&#xff0c;位置(j,k)为起点时的获胜情况了 当然你把第一维压到bitset里&#xff0c;然后前缀和优化一下&#xff0c;还是可以通…

中序遍历二叉树全过程图解

文章目录 中序遍历图解总结拓展&#xff1a;回归与回溯 中序遍历图解 首先看下中序遍历的代码&#xff0c;其接受一个根结点root作为参数&#xff0c;判断根节点是否为nil&#xff0c;不为nil则先递归遍历左子树。 func traversal(root *TreeNode,res *[]int) {if root nil …

Tomcat中间件常见漏洞复现

#1.CVE-2017-12615 -----Tomcat put方法任意文件写入漏洞 1.打开靶场 cd vulhub/tomcat/CVE-2017-12615 docker-compose up -d docker ps 2.访问8080端口&#xff0c;来到靶场 3.首页进抓包&#xff0c;Tomcat允许适⽤put⽅法上传任意⽂件类型&#xff0c;但不允许jsp后缀…

redisson 延迟队列实现任务过期监听

一、需求&#xff1a; 任务超过一个小时以后&#xff0c;如果还为待执行状态&#xff0c;则自动转为结束状态。 二、实现: 创建延迟队列的监听任务RedisDelayedQueueListener&#xff0c;消费延迟队列&#xff1b;创建新增延迟队列的类&#xff0c;用于创建延迟队列&#xf…

LeetCode 热题 100 回顾17

干货分享&#xff0c;感谢您的阅读&#xff01;原文见&#xff1a;LeetCode 热题 100 回顾_力code热题100-CSDN博客 一、哈希部分 1.两数之和 &#xff08;简单&#xff09; 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标…