1.AOP基本介绍
AOP 的全称 (aspect oriented programming) ,面向切面编程。
1.和传统的面向对象不同。 面向切面编程是根据自我的需求,将切面类的方法切入到其他的类的方法中。(这么说抽象吧!来张图来解释。)
如图
传统的面向对象方法是 对象.方法()然后被调用
面向切面编程是什么呢?
首先满足 @1 类的这个方法是通过动态代理实现的比如有个A.say()方法。
然后有一个切面类B,这个类也有许多方法比如study(),但是不同的是,这个类中的方法study(),可以插入到A.say()的方法前后进行。 当A.say()被调用的时候,切面类B的study()方法****就会被自动调用。
这就和传统的直接调用B.study()方法不同,切面类的方法是直接插入到另外一个动态代理类的方法的前后的。
AOP的核心概念
面向切面编程(AOP)是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高代码的模块化和可维护性。在Java中,AOP通常通过使用框架如Spring来实现。
介绍AOP的关键术语
切面(Aspect):切面是封装横切关注点的模块。它包含了一组通知(Advice)和切入点(Pointcut)。
连接点(Joinpoint):在程序执行过程中的特定点,如方法的调用或执行,异常的抛出等。
切点(Pointcut):切点是定义在哪些连接点上应用通知的规则。
通知(Advice):通知是在切点上执行的代码,它定义了在连接点上执行的逻辑,如前置、后置、环绕等。
解释AOP的几种通知类型
前置(Before):在方法执行前执行的通知。
后置(After):在方法执行后执行的通知,无论方法是否成功执行。
环绕(Around):在方法调用前后都可以执行的通知,可以控制方法的调用过程。
异常(Throws):在方法抛出异常后执行的通知。
最终(AfterReturning):在方法正常返回后执行的通知。
2.AOP的快速入门
在xml配置下:
XML
<context:annotation-config/>
<context:component-scan base-package="aspectj"/>
<!--启用基于注解方式的AOP功能-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
【Spring进阶系列丨第十篇】基于注解的面向切面编程(AOP)详解_spring 切面 注解-CSDN博客
3.AOP的切入表达式
语法细节
注意第一个*是包含了任意的权限修饰符和返回值。
方法说明
注意细节
1. 切入表达式也可以指向类的方法 , 这时切入表达式会对该类 / 对象生效
2. 切入表达式也可以指向接口的方法 , 这时切入表达式会对实现了接口的类 / 对象生效
3. 切入表达式也可以对没有实现接口的类,进行切入 (因为Spring的CGLIB)。
4.JoinPoint连接点
作用:通过 JoinPoint 可以获取到调用方法的签名 signature
方法:
// 获取目标方法名
joinPoint.getSignature().getName();
// 获取目标方法所属类的简单类名
joinPoint.getSignature().getDeclaringType().getSimpleName();// 获取目标方法所属类的类名
joinPoint.getSignature().getDeclaringTypeName();// 获取目标方法声明类型(public、private、protected)
joinPoint.getSignature().getModifiers();// 获取传入目标方法的参数,返回一个数组
Object[] args = joinPoint.getArgs();// 获取被代理的对象
joinPoint.getTarget();// 获取代理对象自己
joinPoint.getThis();
在如下代码中要将beforeMethod()方法切入到aspectj包下的SmartDog类的getSum()下
java
@Before(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
public void beforeMethod(JoinPoint joinPoint){
joinPoint.getSignature().getName(); // 获取目标方法名
joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名
joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)
Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
joinPoint.getTarget(); // 获取被代理的对象
joinPoint.getThis(); // 获取代理对象自己
}
5.返回,异常,环绕通知返回结果
java
//返回通知
@AfterReturning(value = "execution(public float aspectj.SmartDog.getSum(float, float))", returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
}
//异常通知
@AfterThrowing(value = "execution(public float aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
}
6.切入点表达式重用
在这些切入方法里面,如果是作用于同一个方法,就会让切入表达式感到重复和冗余,于是为了统一管理切入点表达式,可以使用切入点表达式重用技术
java
package aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @version 1.0
* 这是一个切面类
*/
@Aspect
@Component("smartAnimalAspect")
public class SmartAnimalAspect {
// 切入表达式重用技术
@Pointcut(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
public void mypointcut()
{
}
//给SmartDog配置前置,返回,异常,最终通知
//解读
//1.@Before表示我们目标对象执行前要执行的哪个方法
//2.value = "execution(public float aspectj.SmartDog.getSum(float, float))"
// 是指定切入到哪个类的哪个方法 形式是:访问修饰符 返回类型 全类名.方法名(形参列表) execution是执行的意思
// JoinPoint joinPoint 在底层执行的时候,由Aspectj切面框架,会给该切入方法传入jointPoint对象
// 通过该对象可以获得相关的信息。
//前置通知
@Before(value = "mypointcut()")
public void showBeginLog(JoinPoint joinPoint) {
//通过连接点对象joinPoint 可以获取方法签名
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-" + signature.getName() + "-参数 "
+ Arrays.asList(joinPoint.getArgs()));
}
//返回通知
@AfterReturning(value = "mypointcut()",returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
}
//异常通知
@AfterThrowing(value = "mypointcut()",throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
}
//最终通知
@After(value = "mypointcut()")
public void showFinallyEndLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());
}
}
7.切面优先级问题
● 切面优先级问题 :
如果同一个方法,有多个切面在同一个切入点切入,那么执行的优先级如何控制 .
● 基本语法
@order(value=n) 来控制 n 值越小,优先级越高 .
举例
切面类1(优先执行)
java
package aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @version 1.0
* 这是一个切面类
*/
@Order(value = 1)
@Aspect
@Component("smartAnimalAspect")
public class SmartAnimalAspect {
// 切入表达式重用技术
@Pointcut(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
public void mypointcut()
{
}
//前置通知
// @Before(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
@Before(value = "mypointcut()")
public void showBeginLog(JoinPoint joinPoint) {
//通过连接点对象joinPoint 可以获取方法签名
Signature signature = joinPoint.getSignature();
System.out.println("1前置通知" + signature.getName() + "-参数 "
+ Arrays.asList(joinPoint.getArgs()));
}
//返回通知
// @AfterReturning(value = "execution(public float aspectj.SmartDog.getSum(float, float))", returning = "res")
@AfterReturning(value = "mypointcut()",returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
Signature signature = joinPoint.getSignature();
System.out.println("1返回通知" + signature.getName() + " 返回的结果是=" + res);
}
//异常通知
// @AfterThrowing(value = "execution(public float aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
@AfterThrowing(value = "mypointcut()",throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
Signature signature = joinPoint.getSignature();
System.out.println("1异常通知" + signature.getName() + " 异常信息=" + throwable);
}
//最终通知
// @After(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
@After(value = "mypointcut()")
public void showFinallyEndLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("1最终通知" + signature.getName());
}
}
切面类2(没有切面类1的优先级高)
java
package aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @version 1.0
* 这是一个切面类
*/
@Order(value = 2)
@Aspect
@Component("smartAnimalAspect2")
public class SmartAnimalAspect2 {
// 切入表达式重用技术
@Pointcut(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
public void mypointcut()
{
}
//前置通知
// @Before(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
@Before(value = "mypointcut()")
public void showBeginLog(JoinPoint joinPoint) {
//通过连接点对象joinPoint 可以获取方法签名
Signature signature = joinPoint.getSignature();
System.out.println("2前置通知");
}
//返回通知
// @AfterReturning(value = "execution(public float aspectj.SmartDog.getSum(float, float))", returning = "res")
@AfterReturning(value = "mypointcut()",returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
Signature signature = joinPoint.getSignature();
System.out.println( "2返回通知 返回的结果是=" + res);
}
//异常通知
// @AfterThrowing(value = "execution(public float aspectj.SmartDog.getSum(float, float))", throwing = "throwable")
@AfterThrowing(value = "mypointcut()",throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
Signature signature = joinPoint.getSignature();
System.out.println("2异常通知" + throwable);
}
//最终通知
// @After(value = "execution(public float aspectj.SmartDog.getSum(float, float))")
@After(value = "mypointcut()")
public void showFinallyEndLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("2最终通知" + signature.getName());
}
}
结果:
注意
不能理解成:优先级高的每个消息通知都先执行,这个和方法调用机制(和 Filter 过滤器
链式调用类似)
8.基于XML的AOP
定义SmartAnimalable接口
java
package xml;
/**
* @author ygd
*/
public interface SmartAnimalable {
float getSum(float i, float j);
float getSub(float i, float j);
}
定义SmartDog类
java
package xml;
/**
* @author ygd
*/
public class SmartDog implements SmartAnimalable {
@Override
public float getSum(float i, float j) {
float result = i + j;
System.out.println("getSum() 方法内部打印 result= " + result);
return result;
}
@Override
public float getSub(float i, float j) {
float result = i - j;
System.out.println("getSub() 方法内部打印 result= " + result);
return result;
}
}
定义SmartAnimalAspect切面类
java
package xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import java.util.Arrays;
/**
* @version 1.0
* 这是一个基于XML的切面类
*/
public class SmartAnimalAspect {
public void showBeginLog(JoinPoint joinPoint) {
//通过连接点对象joinPoint 可以获取方法签名
Signature signature = joinPoint.getSignature();
System.out.println("1前置通知" + signature.getName() + "-参数 "
+ Arrays.asList(joinPoint.getArgs()));
}
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
Signature signature = joinPoint.getSignature();
System.out.println("1返回通知" + signature.getName() + " 返回的结果是=" + res);
}
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
Signature signature = joinPoint.getSignature();
System.out.println("1异常通知" + signature.getName() + " 异常信息=" + throwable);
}
public void showFinallyEndLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
System.out.println("1最终通知" + signature.getName());
}
}
定义beans01.xml
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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置一个切面类对象-bean-->
<bean class="xml.SmartAnimalAspect" id="smartAnimalAspect"/>
<!--配置SmartDog对象-bean-->
<bean class="xml.SmartDog" id="dog"/>
<!--配置切面类,一定要引入xmlns:aop命名空间-->
<aop:config>
<!-- 注意顺序,先配置切入点,在配置切面对象-->
<!--配置切入点-->
<aop:pointcut id="mypointcut" expression="execution(public float xml.SmartDog.getSum(float, float))"/>
<!--这里指定切面对象-->
<aop:aspect ref="smartAnimalAspect" order="10">
<!--配置前置通知-->
<aop:before method="showBeginLog" pointcut-ref="mypointcut"/>
<!--配置返回通知-->
<aop:after-returning method="showSuccessEndLog" pointcut-ref="mypointcut" returning="res"/>
<!--配置异常通知-->
<aop:after-throwing method="showExceptionLog" pointcut-ref="mypointcut" throwing="throwable"/>
<!--配置最终通知-->
<aop:after method="showFinallyEndLog" pointcut-ref="mypointcut"/>
</aop:aspect>
</aop:config>
</beans>
注意点:要 先配置切面点,在配置切面类
xml的语法,跟上面所写的基于注解的aop道理都是一样的。