spring4第7-8课-AOP的5种通知类型+切点定义详解+执行顺序

继续学习,方便自己复查记录

①AOP简介:

面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP)。

Spring框架中的一个重要内容。。

通过预编译方式和运行期间动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP是OOP(面向对象编程)的延续。

作用: 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高 程序的可重用性,同时提高了开发的效率。

使用场景:日志记录,性能统计,安全控制,事务处理,异常处理等。

②AOP实例学习:几种通知类型↓↓↓↓

1,前置通知;

2,后置通知;

3,环绕通知;

4,返回通知;

5,异常通知;

②-1首先看下不用AOP前的代码写法,

(作为测试,业务目的很简单:在程序的开始和结束打印一点日志)

代码如下:涉及4个文件

复制代码
代码1:配置类 bean.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl"></bean>
	
</beans>


代码2:StudentService类
package com.java1234.service;

public interface StudentService {

	public void addStudent(String name);
}

代码3:StudentService实现类
package com.java1234.service.impl;

import com.java1234.service.StudentService;

public class StudentServiceImpl implements StudentService{

	@Override
	public void addStudent(String name) {
		 System.out.println("开始添加学生"+name);
		System.out.println("添加学生"+name);
		 System.out.println("完成学生"+name+"的添加");
	}

}


代码4:测试类
package com.java1234.test;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.java1234.service.StudentService;


public class T {

	private ApplicationContext ac;

	@Before
	public void setUp() throws Exception {
		ac=new ClassPathXmlApplicationContext("beans.xml");
	}

	@Test
	public void test1() {
		// 多态特性:父类的引用可以指向具体的实现。
		// 按照bean.xml的定义,本来 ac.getBean("studentService") 得到的是StudentServiceImpl,被安全的向下转型为StudentService类型
		StudentService studentService=(StudentService)ac.getBean("studentService");//

		studentService.addStudent("张三");
	}
	

}

②-2使用AOP类的变化:

a.增加切面通知类

b.引入相关jar包:aopalliance.jar,aspectjweaver-1.6.6.jar,spring-aspects-4.0.6.RELEASE.jar(版本号可能有差异)

c.在bean.xml中增加

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation 后面增加http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

加完上面这个就可以在bean.xml中使用aop功能了

++插入知识点1:bean.xml的命名空间的含义。可以跳过++

xsi:schemaLocation 的通用结构为: 命名空间URI1 XSD路径1 命名空间URI2 XSD路径2 ...

例子:(都是两个两个一起的)

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

在给定的 xsi:schemaLocation 属性中,每一对字符串分别由命名空间 URI 和 实际 XSD 文件路径组成。具体解析如下:

命名空间 URI

http://www.springframework.org/schema/beans

这是 Spring 框架中 beans 模块的命名空间 URI,用于标识 XML 中相关元素的所属规范。

http://www.springframework.org/schema/aop

这是 Spring AOP 模块的命名空间 URI,用于标识 AOP 相关配置的命名空间。

实际 XSD 文件路径

http://www.springframework.org/schema/beans/spring-beans.xsd

这是 beans 模块对应的 XML Schema 定义文件(XSD)的实际网络路径。

http://www.springframework.org/schema/aop/spring-aop.xsd

这是 AOP 模块对应的 XSD 文件路径,定义了 AOP 配置的校验规则。

插入知识点2:执行表达式execution的理解,可以跳过

执行表达式 execution(* com.java1234.service.*.*(..)) 的含义

该表达式是 Spring AOP 中的切点(Pointcut)表达式,用于匹配特定方法的执行。以下是逐部分解析:

组成部分解析
  1. execution

    表示匹配方法执行的连接点(Join Point),是 AOP 中最常用的切点指示符。

  2. *(第一个星号)

    匹配任意返回类型(如 voidStringint 等)。

  3. com.java1234.service.*

    指定包路径和类名:

    • com.java1234.service 是目标包名。
    • .* 匹配该包下的任意类(但不包括子包中的类)。
  4. *(第三个星号)

    匹配类中的任意方法名。

  5. (..)

    匹配任意参数列表(无论是否有参数,或参数类型是什么)。

完整含义

该表达式匹配:
com.java1234.service 包下任意类中,所有返回类型、所有方法名、所有参数列表的方法。

常见用法示例
复制代码
@Aspect
@Component
public class ServiceAspect {
    @Before("execution(* com.java1234.service.*.*(..))")
    public void logBeforeServiceMethod(JoinPoint joinPoint) {
        System.out.println("Executing: " + joinPoint.getSignature());
    }
}
扩展说明
  • 若需匹配子包中的类,可使用 ..:(2个点)
    execution(* com.java1234.service..*.*(..))
  • 若需限定方法名,可替换 * 为具体名称(如 get* 匹配以 get 开头的方法)。
相关语法对比
复制代码
// 匹配特定返回类型(String)的方法
execution(String com.java1234.service.*.*(..))

// 匹配特定类(UserService)中的方法
execution(* com.java1234.service.UserService.*(..))

// 匹配特定参数类型(需一个Long参数)的方法
execution(* com.java1234.service.*.*(Long))

d.在bean.xml中增加aop切面的定义 ,完整的bean.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: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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 引入第三行 xmlns:aop="http://www.springframework.org/schema/aop" 和
    引入第7.8行 http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
         就可以使用AOP功能配置了 -->
   <!-- 切面类是studentServiceAspect(这里有想添加的方法功能) -->
   <bean id="studentServiceAspect" class="com.java1234.advice.StudentServiceAspect"></bean>

   <bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl"></bean>
   
   <aop:config>
       <!--aop:aspect 配置切面,关联的切面类是studentServiceAspect(这里有想添加的方法功能) -->
      <aop:aspect id="studentServiceAspectConfig" ref="studentServiceAspect">
         <!-- aop:pointcut 定义1个切点,spring的切点是定义到方法级的。execution是表达式,用于匹配特定方法的执行-->
         <!-- execution(* com.java1234.service.*.*(..)) 用于匹配特定方法的执行   -->
         <aop:pointcut expression="execution(* com.java1234.service.*.*(..))" id="businessService"/>
         <!-- 开始定义各种通知 aop:before  aop:after  aop:around aop:after-returning aop:after-returning aop:after-throwing-->
         <!-- 定义1,前置通知;方法执行前触发. pointcut-ref="businessService" 表示引用的切点是businessService-->
         <aop:before method="doBefore" pointcut-ref="businessService" />
         <!-- 定义2,后置通知;方法执行后触发(无论是否抛出异常)。 -->
         <!--aop:after method="doAfter" pointcut-ref="businessService"/-->
         <!-- 定义3,环绕通知;包围目标方法,需手动调用 proceed()。 -->
         <aop:around method="doAround" pointcut-ref="businessService" />
         <!-- 定义4,返回通知; 方法正常返回后触发。-->
         <aop:after-returning method="doAfterReturning" pointcut-ref="businessService"/>
         <!-- 定义5,异常通知   方法抛出异常时触发。-->
         <aop:after-throwing method="doAfterThrowing" pointcut-ref="businessService" throwing="ex"/>
      </aop:aspect>
   </aop:config>
</beans>

其他的代码1,业务类:没有了冗余的日志打印代码,用aop实现了

复制代码
package com.java1234.service.impl;

import com.java1234.service.StudentService;

public class StudentServiceImpl implements StudentService{

   @Override
   public void addStudent(String name) {
      // System.out.println("开始添加学生"+name);
 System.out.println("业务逻辑开始::::添加学生"+name);
      // System.out.println(1/0);
      // System.out.println("完成学生"+name+"的添加");
   }

}

代码2: 接口类(匹配上expression="execution(* com.java1234.service.*.*(..))"这里的路径)

复制代码
package com.java1234.service;

public interface StudentService {

   public void addStudent(String name);
}

代码3:切面类(5种通知的具体内容)

复制代码
package com.java1234.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class StudentServiceAspect {

   public void doBefore(JoinPoint jp){
      System.out.println("doBefore:类名:"+jp.getTarget().getClass().getName());
      System.out.println("doBefore:方法名:"+jp.getSignature().getName());
      System.out.println("doBefore:开始添加学生:"+jp.getArgs()[0]);
   }

   public void doAfter(JoinPoint jp){
      System.out.println("doAfter:类名:"+jp.getTarget().getClass().getName());
      System.out.println("doAfter:方法名:"+jp.getSignature().getName());
      System.out.println("doAfter:学生添加完成:"+jp.getArgs()[0]);
   }

   public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ // 参数和其他通知不一样 ProceedingJoinPoint
      System.out.println("doAround:添加学生前");
      System.out.println("doAround:手动调用 proceed方法");
      Object retVal=pjp.proceed();  // 需手动调用 proceed()。
      //System.out.println(retVal);
      System.out.println("doAround:添加学生后");
      return retVal;
   }

   public void doAfterReturning(JoinPoint jp){
      System.out.println("doAfterReturning:返回通知");
   }

   public void doAfterThrowing(JoinPoint jp,Throwable ex){
      System.out.println("doAfterThrowing:异常通知");
      System.out.println("doAfterThrowing:异常信息:"+ex.getMessage());
   }
}

代码4:测试类,调用的main类

复制代码
package com.java1234.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.java1234.service.StudentService;


public class T {

   public static void main(String[] args) {
      ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
      StudentService studentService=(StudentService)ac.getBean("studentService");
      studentService.addStudent("张三");
      
   }
   
}

最后:关于5种通知:

1,前置通知;

2,后置通知;

3,环绕通知;

4,返回通知;

5,异常通知;

ps1: 第4种返回通知;第5种异常通知; 不会同时存在,只能同时返回1种

ps2. 如果同时存在时的执行顺序:

在业务处理无异常时的执行顺序:

1前置通知--》3环绕通知的前处理--》业务逻辑--》3环绕通知的后处理--》2后置通知--》4返回通知

在业务处理有异常时的执行顺序:

1前置通知--》3环绕通知的前处理--》业务逻辑--》5异常通知

相关推荐
做题不NG5 分钟前
大模型应用开发-LangChain4j
java
今天背单词了吗9808 分钟前
算法学习笔记:7.Dijkstra 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·数据结构·笔记·算法
CHENWENFEIc1 小时前
SpringBoot论坛系统安全测试实战报告
spring boot·后端·程序人生·spring·系统安全·安全测试
高兴达1 小时前
RPC--Netty客户端实现
java·spring·rpc
重庆小透明1 小时前
力扣刷题记录【1】146.LRU缓存
java·后端·学习·算法·leetcode·缓存
lang201509281 小时前
Reactor操作符的共享与复用
java
TTc_2 小时前
@Transactional事务注解的批量回滚机制
java·事务
wei_shuo2 小时前
飞算 JavaAI 开发助手:深度学习驱动下的 Java 全链路智能开发新范式
java·开发语言·飞算javaai
欧阳秦穆3 小时前
apoc-5.24.0-extended.jar 和 apoc-4.4.0.36-all.jar 啥区别
java·jar
岁忧3 小时前
(LeetCode 面试经典 150 题 ) 58. 最后一个单词的长度 (字符串)
java·c++·算法·leetcode·面试·go