阅读本文前,建议先阅读Spring之基于注解的AOP

12.1、环境搭建

创建名为spring_aop_xml的新module,过程参考9.1节

12.1.1、配置打包方式和依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>org.rain</groupId>
<artifactId>spring_aop_xml</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging> <dependencies>
<!-- Spring-IOC的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency> <!-- spring-AOP的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency> <!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies> </project>

12.1.2、创建Calculator接口及实现类

package org.rain.spring.aop.xml;

/**
* @author liaojy
* @date 2023/8/18 - 8:09
*/
public interface Calculator { int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j); }

package org.rain.spring.aop.xml;

import org.springframework.stereotype.Component;

/**
* @author liaojy
* @date 2023/8/18 - 8:11
*/
// @Component注解:保证这个目标类能够放入IOC容器
@Component
public class CalculatorImpl implements Calculator {
public int add(int i, int j) { int result = i + j;
System.out.println("方法内部 result = " + result);
return result; } public int sub(int i, int j) { int result = i - j;
System.out.println("方法内部 result = " + result);
return result; } public int mul(int i, int j) { int result = i * j;
System.out.println("方法内部 result = " + result);
return result; } public int div(int i, int j) { int result = i / j;
System.out.println("方法内部 result = " + result);
return result; }
}

12.1.3、创建切面类LoggerAspect

package org.rain.spring.aop.xml;

import org.springframework.stereotype.Component;

/**
* @author liaojy
* @date 2023/8/18 - 8:15
*/
// @Component注解:保证这个目标类能够放入IOC容器
@Component
public class LoggerAspect {
}

12.1.4、创建spring配置文件

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--
对指定的package进行扫描,将使用组件注解的类的对象(本示例是目标对象和切面对象),交给spring的ioc容器来管理
-->
<context:component-scan base-package="org.rain.spring.aop.xml"></context:component-scan> </beans>

12.2、前置通知的使用

12.2.1、基本示例

12.2.1.1、定义前置通知的功能

    public void beforeMethod(){
System.out.println("LoggerAspect,前置通知");
}

12.2.1.2、配置前置通知到切面

    <aop:config>
<!--
aop:aspect标签:将ioc容器中的bean对象设置为切面,效果等同于@Aspect注解
ref属性:引用IOC容器中(要设置为切面的)bean的id
-->
<aop:aspect ref="loggerAspect">
<!--
aop:before标签:将方法设置为该切面的前置通知(方法),效果等同于@Before注解
method属性:指定(要设置为前置通知的)方法的名称
pointcut属性:设置该前置通知的切入点表达式
-->
<aop:before method="beforeMethod" pointcut="execution(* org.rain.spring.aop.xml.CalculatorImpl.*(..))"></aop:before>
</aop:aspect>
</aop:config>

12.2.1.3、测试使用效果

由控制台日志可知,切面类的前置通知(方法),通过切入点表达式,作用到了目标方法的连接点上

package org.rain.spring.aop.test;

import org.junit.Test;
import org.rain.spring.aop.xml.Calculator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @author liaojy
* @date 2023/8/18 - 19:07
*/
public class AOPTest { @Test
public void testAOPByXML(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aopByxml.xml");
// 注意:这里不能直接获取目标对象来使用;因为使用了AOP之后,IOC容器中就只有对应目标对象的代理对象;
// 如果强行获取目标对象,则报错:NoSuchBeanDefinitionException
//Calculator calculator = ioc.getBean(CalculatorImpl.class); // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
Calculator calculator = ioc.getBean(Calculator.class); // 只能通过代理对象来访问目标对象中的方法
calculator.div(1,1); }
}

12.2.2、高级示例

12.2.2.1、改进前置通知的功能

该示例中(前置)通知方法引入了连接点参数,通过连接点参数,可以动态获取(切入点表达式)对应的目标方法的名称和参数列表

    // joinPoint参数:可以获取(通过切入点表达式定位出的)连接点的相关信息
public void beforeMethod(JoinPoint joinPoint){
// 获取连接点所对应目标方法的名称
String methodName = joinPoint.getSignature().getName();
// 获取连接点所对应目标方法的参数列表
Object[] args = joinPoint.getArgs();
System.out.println("LoggerAspect-->前置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
}

12.2.2.2、测试使用效果

12.3、切入点表达式的复用

切入点表达的详细语法,请参考11.3.2节

12.3.1、配置公共的切入点表达式

        <!--
aop:pointcut标签:用于声明公共的切入点表达式,效果等同于@Pointcut注解
id属性:设置该公共切入点表达式的唯一标识
expression属性:设置切入点表达式
-->
<aop:pointcut id="pointcutOne" expression="execution(* org.rain.spring.aop.xml.CalculatorImpl.*(..))"/>

12.3.2、使用公共的切入点表达式

<!--
aop:before标签:将方法设置为该切面的前置通知(方法),效果等同于@Before注解
method属性:指定(要设置为前置通知的)方法的名称
pointcut属性:设置该前置通知的切入点表达式
pointcut-ref属性:通过id引用对应的公共切入点表达式
-->
<!--<aop:before method="beforeMethod" pointcut="execution(* org.rain.spring.aop.xml.CalculatorImpl.*(..))"></aop:before>-->
<aop:before method="beforeMethod" pointcut-ref="pointcutOne"></aop:before>

12.4、其他通知的使用

12.4.1、后置通知

12.4.1.1、定义后置通知的功能

    public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("LoggerAspect-->后置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
}

12.4.1.2、配置后置通知到切面

            <!--
aop:after标签:将方法设置为该切面的后置通知(方法),效果等同于@After注解
-->
<aop:after method="afterMethod" pointcut-ref="pointcutOne"></aop:after>

12.4.1.3、测试使用效果

由控制台日志可知,后置通知在目标对象方法的finally子句中执行(一般用于释放资源)

    @Test
public void testAOPByXML(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aopByxml.xml"); // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
Calculator calculator = ioc.getBean(Calculator.class); // 只能通过代理对象来访问目标对象中的方法
calculator.div(1,0); }

12.4.2、返回通知

12.4.2.1、定义返回通知的功能

    public void afterReturningMethod(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("LoggerAspect-->返回通知,方法名:"+methodName+",结果:"+ result);
}

12.4.2.2、配置返回通知到切面

<!--
aop:after-returning标签:将方法设置为该切面的返回通知(方法),效果等同于@AfterReturning注解
returning属性:指定返回通知(方法)中的某个参数(名),用于接收目标对象方法的返回值
-->
<aop:after-returning method="afterReturningMethod" pointcut-ref="pointcutOne" returning="result"></aop:after-returning>

12.4.2.3、测试使用效果

返回通知在目标对象方法的返回值之后执行

    @Test
public void testAOPByXML(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aopByxml.xml"); // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
Calculator calculator = ioc.getBean(Calculator.class); // 只能通过代理对象来访问目标对象中的方法
calculator.div(1,1); }

12.4.3、异常通知

12.4.3.1、定义异常通知的功能

    public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("LoggerAspect-->异常通知,方法名:"+methodName+",异常:"+ ex);
}

12.4.3.2、配置异常通知到切面

<!--
aop:after-throwing标签:将方法设置为该切面的异常通知(方法),效果等同于@AfterThrowing注解
throwing属性:指定异常通知(方法)中的某个参数(名),用于接收目标对象方法出现的异常
-->
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcutOne" throwing="ex" ></aop:after-throwing>

12.4.3.3、测试使用效果

由控制台日志可知,异常通知在目标对象方法的catch子句中执行

    @Test
public void testAOPByXML(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aopByxml.xml"); // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
Calculator calculator = ioc.getBean(Calculator.class); // 只能通过代理对象来访问目标对象中的方法
calculator.div(1,0); }

12.5、环绕通知

12.5.1、定义环绕通知的功能

环绕通知和动态代理的形式,非常相似

    /**
* 环绕通知(方法)使用的参数是ProceedingJoinPoint类型
* 环绕通知(方法)的返回值,必须和目标对象方法的返回值一致
*/
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
String methodName = proceedingJoinPoint.getSignature().getName();
Object[] args = proceedingJoinPoint.getArgs();
Object result = null;
try {
System.out.println("LoggerAspect-->环绕前置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
// 表示目标对象方法的执行
result = proceedingJoinPoint.proceed();
System.out.println("LoggerAspect-->环绕返回通知,方法名:"+methodName+",结果:"+ result);
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("LoggerAspect-->环绕异常通知,方法名:"+methodName+",异常:"+ throwable);
}finally {
System.out.println("LoggerAspect-->环绕后置通知,方法名:"+methodName+",参数:"+ Arrays.toString(args));
}
return result;
}

12.5.2、配置环绕通知到切面

            <!--
aop:around标签:将方法设置为该切面的环绕通知(方法),效果等同于@Around注解
-->
<aop:around method="aroundMethod" pointcut-ref="pointcutOne"></aop:around>

12.5.3、测试使用效果

注意:因为环绕通知包括了其他四种通知,所以一般要么配置其他四种通知,要么只配置环绕通知;

本示例为了展示效果才同时配置

    @Test
public void testAOPByXML(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aopByxml.xml"); // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
Calculator calculator = ioc.getBean(Calculator.class); // 只能通过代理对象来访问目标对象中的方法
calculator.div(1,1); }

12.6、切面的优先级

12.6.1、创建其他切面类ValidateAspect

package org.rain.spring.aop.xml;

import org.springframework.stereotype.Component;

/**
* @author liaojy
* @date 2023/8/20 - 23:59
*/
// @Component注解:保证这个目标类能够放入IOC容器
@Component
public class ValidateAspect {
}

12.6.2、定义前置通知的功能

    public void beforeMethod(){
System.out.println("ValidateAspect-->前置通知");
}

12.6.3、配置前置通知到切面

        <aop:aspect ref="validateAspect">
<aop:before method="beforeMethod" pointcut-ref="pointcutOne"></aop:before>
</aop:aspect>

12.6.4、测试使用效果

由控制台日志可知,ValidateAspect切面的前置通知方法生效了,但执行顺序在LoggerAspect切面的前置通知方法的后面

    @Test
public void testAOPByXML(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-aopByxml.xml"); // 虽然不知道代理对象的类名,但可以通过代理对象和目标对象共同实现的接口类型来从ioc容器中获取代理对象
Calculator calculator = ioc.getBean(Calculator.class); // 只能通过代理对象来访问目标对象中的方法
calculator.div(1,1); }

12.6.5、调整切面的优先级

        <!--
aop:aspect标签的order属性:用于设置切面的优先级,value属性值越小,优先级越高,默认值为Integer的最大值
-->
<aop:aspect ref="validateAspect" order="2023">
<aop:before method="beforeMethod" pointcut-ref="pointcutOne"></aop:before>
</aop:aspect>

12.6.6、测试调整后的效果

由控制台日志可知,ValidateAspect切面的前置通知方法的执行顺序,在LoggerAspect切面的前置通知方法的前面

这是因为ValidateAspect切面的Order属性值已设为2023,要小于LoggerAspect切面所使用的默认值(Integer的最大值2147483647)

12、Spring之基于xml的AOP的更多相关文章

  1. Spring中基于xml的AOP

    1.Aop 全程是Aspect Oriented Programming 即面向切面编程,通过预编译方式和运行期动态代理实现程序功能的同一维护的一种技术.Aop是oop的延续,是软件开发中的 一个热点 ...

  2. spring的基于xml的AOP配置案例和切入点表达式的一些写法

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...

  3. 基于XML的AOP配置

    创建spring的配置文件并导入约束 此处要导入aop的约束 <?xml version="1.0" encoding="UTF-8"?> < ...

  4. Spring 框架的概述以及Spring中基于XML的IOC配置

    Spring 框架的概述以及Spring中基于XML的IOC配置 一.简介 Spring的两大核心:IOC(DI)与AOP,IOC是反转控制,DI依赖注入 特点:轻量级.依赖注入.面向切面编程.容器. ...

  5. Spring学习--基于 XML 的配置声明切面

    正常情况下 , 基于注解的生命要优先于基于 XML 的声明. 通过 AspectJ 注解 , 切面可以与 AspectJ 兼容 , 而基于 XML 的配置则是 Spring 专有的.由于 Aspect ...

  6. 【Spring】基于@Aspect的AOP配置

    Spring AOP面向切面编程,可以用来配置事务.做日志.权限验证.在用户请求时做一些处理等等.用@Aspect做一个切面,就可以直接实现. ·   本例演示一个基于@Aspect的小demo 1. ...

  7. Spring AspectJ基于注解的AOP实现

    对于AOP这种编程思想,很多框架都进行了实现.Spring就是其中之一,可以完成面向切面编程.然而,AspectJ也实现了AOP的功能,且实现方式更为简捷,使用更加方便,而且还支持注解式开发.所以,S ...

  8. 基于 XML 的 AOP 配置(1)

    本文连接:https://www.cnblogs.com/qzhc/p/11969734.html 接下来我将用一个很简单的实例 1. 环境搭建 1.1. 第一步:准备必要的代码 业务层代码: Acc ...

  9. Spring框架——基于XML/注解开发

    IoC的实现方式有两种:XML配置文件.基于注解. MVC开发模式: Controller层 Service层 Repository层 Controller层调用Service,Service调用Re ...

  10. 阶段3 2.Spring_10.Spring中事务控制_1 基于XML的AOP实现事务控制

    新建项目 首先把依赖复制进来 aop必须引入.aspectjweaver 复制src下的所有内容 复制到我们的新项目里面 factory文件夹删掉 删除后测试类必然就报错 配置文件 beanFacto ...

随机推荐

  1. 2021-08-02:按公因数计算最大组件大小。给定一个由不同正整数的组成的非空数组 A,考虑下面的图:有 A.length 个节点,按从 A[0] 到 A[A.length - 1] 标记;只有当

    2021-08-02:按公因数计算最大组件大小.给定一个由不同正整数的组成的非空数组 A,考虑下面的图:有 A.length 个节点,按从 A[0] 到 A[A.length - 1] 标记:只有当 ...

  2. Can't uninstall 'Pillow'. No files were found to uninstall.

    Can't uninstall 'Pillow'. No files were found to uninstall. Pillow卸载不掉的解决办法 1.进入python所在路径,进入scripts ...

  3. .NET 通过源码深究依赖注入原理

    依赖注入 (DI) 是.NET中一个非常重要的软件设计模式,它可以帮助我们更好地管理和组织组件,提高代码的可读性,扩展性和可测试性.在日常工作中,我们一定遇见过这些问题或者疑惑. Singleton服 ...

  4. nginx发布vue 项目

    在本次使用nginx发布vue项目遇到 配置location 始终404 和 在项目子目录点击浏览器刷新出现404问题 使用nginx发布vue项目,为了方便测试就下载了一个nginx 放置自己目录下 ...

  5. react中useRef的应用

    何为useRef useRef是随着react函数式组件发展而来的,是react众多官方hook中的一个,调用useRef可以返回一个伴随这组件整个声明周期不发生改变的对象,这个对象常见的用途有两个: ...

  6. 【python基础】基本数据类型-字符串类型

    1.初识字符串 字符串就是一系列字符.在python中,用引号括起来文本内容的都是字符串. 其语法格式为:'文本内容'或者"文本内容" 我们发现其中的引号可以是单引号,也可以是双引 ...

  7. 基于渗透的python

    Python for Pentesters 还记得开始学习编程的C,虽然淡忘,但思想仍在. 子域名枚举 request库 import pyfiglet import requests import ...

  8. 通过redis学网络(1)-用go基于epoll实现最简单网络通信框架

    本系列主要是为了对redis的网络模型进行学习,我会用golang实现一个reactor网络模型,并实现对redis协议的解析. 系列源码已经上传github https://github.com/H ...

  9. 2023-06-11:redis中,如何在100个亿URL中快速判断某URL是否存在?

    2023-06-11:redis中,如何在100个亿URL中快速判断某URL是否存在? 答案2023-06-11: 传统数据结构的不足 当然有人会想,我直接将网页URL存入数据库进行查找不就好了,或者 ...

  10. Elasticsearch 6.x 配置search-guard 插件

    前言   es之前版本一直无用户验证功能,不过官方有提供一x-pack,但是问题是付费.在es的6.3.2版本中,已经集成了x-pack,虽然es团队已经对x-pack开源,但是在该版本中如果需要使用 ...