概念
AspectJ
- Aspect 切面(由多个切点组成,多个点组成面)
启用@AspectJ支持后,Spring 会自动检测出在应用程序上下文中定义的任何 Bean,如下使用@Aspect 定义的一个切面示例。
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
- Pointcut 切点(被切入的方法)
切入点决定了连接兴趣点,从而使我们能够控制建议的运行时间。Spring AOP 仅支持 Spring bean 的方法执行连接点,因此您可以将切入点视为与Spring bean 上的方法执行相匹配。切入点声明由两部分组成:一个由名称和任何参数组成的签名,以及一个切入点表达式,该表达式准确确定我们感兴趣的方法执行。在 AOP 的@AspectJ注释样式中,切入点签名由常规方法定义提供,切入点表达式通过使用注释进行指示(用作切入点签名的方法必须具有返回类型)。@Pointcut``void
一个示例可能有助于明确切入点签名和切入点表达式之间的区别。下面的示例定义了一个名为 的切入点,该切入点与任何名为 的方法来执行相匹配:anyOldTransfer``transfer
支持的切入点指示符
弹簧 AOP 支持以下 AspectJ 切入点指示符 (PCD), 用于切入点表达式:
execution
:用于匹配方法执行连接点。这是使用弹簧 AOP 时要使用的主要切入点指示符。within
:限制匹配以连接特定类型中的点(使用Spring AOP时执行在匹配类型中声明的方法)。this
:限制与连接点的匹配(使用弹簧 AOP 时的方法的执行),其中 Bean 引用(弹簧 AOP 代理)是给定类型的实例。target
:限制与连接点的匹配(使用Spring AOP时的方法执行),其中目标对象(正在代理的应用程序对象)是给定类型的实例。args
:限制匹配以连接点(使用Spring AOP时执行方法),其中参数是给定类型的实例。@target
:限制与连接点(使用Spring AOP时执行方法)的匹配,其中执行对象的类具有给定类型的注释。@args
:限制与连接点的匹配(使用Spring AOP时的方法执行),其中传递的实际参数的运行时类型具有给定类型的注释。@within
:限制在具有给定注释的类型中连接点的匹配(使用Spring AOP时,执行具有给定注释的类型中声明的方法)。@annotation
:将匹配限制为连接点的主题(在 Spring AOP 中运行的方法)具有给定注释的连接点。
-
Advice 通知(切入的时机,被切入的业务逻辑)
-
Before 方法执行之前
-
After方法执行之后
- AfterThrowing 方法执行之后异常处理
- AfterReturning 方法执行之后,返回结果
-
Around 环绕通知,方法执行前和执行之后
-
JoinPoint 连接点,用于获取方法的参数(配合Advice 里的具体通知使用)
任何建议方法都可以将 类型的参数声明为其第一个参数。请注意,在建议周围需要声明类型的第一个参数,它是 的子类org.aspectj.lang.JoinPoint
ProceedingJoinPoint
JoinPoint。getArgs()
:返回方法参数。getThis()
: 返回代理对象。getTarget()
: 返回目标对象。getSignature()
: 返回所建议方法的说明。toString()
: 打印所建议方法的有用说明。
关于具体的切面,切点,建议,以及链接点请参考下面官方文档和实战部分代码结合理解
核心技术 (spring.io)
AOP动态代理
spring AOP 默认对 AOP 代理使用标准 JDK 动态代理。这使得任何接口(或一组接口)都可以被代理。
这对于代理类,不是必须实现被代理类接口。缺省情况下,如果业务对象没有可以实现的接口,则使用 CGLIB。由于编程到接口而不是类是很好的做法,因此业务类通常实现一个或多个业务接口。在那些(希望是罕见的)情况下,可以强制使用CGLIB,在这些情况下,您需要建议未在接口上声明的方法,或者需要将代理对象作为具体类型传递给方法。
Aop 动态代理工厂类图
Aop 动态代理对象创建过程
IOC部分
这部分只是展示一个正常bean的创建过程(如果启用了切点代理,这部分是一样的)
initalizeBean 方法和applyBeanPostProcessorsAfterInitialization方法是创建aop动态代理的重要方法,下面aop部分主要以后置处理器的方法进行详细剖析。
AOP部分
applyBeanPostProcessorsAfterInitialization(获取所有后置处理器)
annotationAwareAspectJAutoProxyCreator(切面代理后置处理器)
实战
AOP动态动态代理对象
目录结构
配置类
切面类
package com.kang.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @Description 切面测试
* @ClassName Aoptest
* @Author 康世行
* @Date 10:06 2022/7/29
* @Version 1.0
**/
@Aspect
@Component
public class Aoptest {
@Pointcut("execution(* *(..))")
private void beforeTest(){}
@Before("beforeTest()")
public void testBefore(JoinPoint account){
Object[] args = account.getArgs();
for (Object arg : args) {
System.out.println("方法执行之前参数->"+arg);
}
}
@AfterReturning("beforeTest()")
public void testAfter(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println("方法执行之后参数->"+arg);
}
}
// @Around("beforeTest()")
// public Object testAroundTest(ProceedingJoinPoint joinPoint){
// Object result=null;
// try {
// Object[] args = joinPoint.getArgs();
// for (Object arg : args) {
// System.out.println("方法执行之前参数:->"+arg);
// }
// result= joinPoint.proceed();
//
// } catch (Throwable e) {
// throw new RuntimeException(e);
// }
// System.out.println("方法执行完毕");
// return result;
// }
}
注解容器上下文配置类(用于扫包和开启aop代理注解)
package com.kang.aop.config;
import com.kang.aop.Aoptest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
/**
* @Description 开启切面配置
* @ClassName AppConfig
* @Author 康世行
* @Date 10:05 2022/7/29
* @Version 1.0
**/
@Configuration
@EnableAspectJAutoProxy
@ComponentScan({"com.kang.aop"})
public class AppConfig {
}
JDK
接口
package com.kang.aop.aoptest;
/**
* @Description 发送消息接口
* @ClassName SendMessage
* @Author 康世行
* @Date 9:39 2022/7/30
* @Version 1.0
**/
public interface SendMessage {
/**
* @author 康世行
* @description: 发送消息
* @date 2022/7/30 9:40
* @param msg 消息体
* @return void
* @Version1.0
**/
void sendMessage(String msg);
}
实现类
package com.kang.aop.aoptest;
import org.springframework.stereotype.Service;
/**
* @Description 发送消息实现类
* @ClassName SendMessageImpl
* @Author 康世行
* @Date 9:40 2022/7/30
* @Version 1.0
**/
@Service("sendMessageImpl")
public class SendMessageImpl implements SendMessage {
@Override
public void sendMessage(String msg) {
System.out.println("发送的消息是->"+msg);
}
}
package com.kang.aop.aoptest;
import org.springframework.stereotype.Service;
/**
* @Description TODO
* @ClassName temp
* @Author 康世行
* @Date 9:46 2022/7/30
* @Version 1.0
**/
@Service("temp")
public class temp implements SendMessage{
@Override
public void sendMessage(String msg) {
System.out.println(msg);
}
}
结果
CGLIB
实现类
package com.kang.aop.aoptest;
import org.springframework.stereotype.Service;
/**
* @Description TODO
* @ClassName userSavle
* @Author 康世行
* @Date 10:12 2022/7/29
* @Version 1.0
**/
@Service
public class UserImpl {
public void save(String msg){
System.out.println("保存用户信息:"+msg);
}
public String print(String msg){
String result= "userImpl"+msg;
return result;
}
}
结果
完整分析-流程图
https://www.processon.com/view/link/63257d13f346fb3377e81de7
ing msg){
System.out.println(“保存用户信息:”+msg);
}
public String print(String msg){
String result= “userImpl”+msg;
return result;
}
}
**结果**
[外链图片转存中...(img-Ywji2EMS-1685082295412)]
# 完整分析-流程图
**https://www.processon.com/view/link/63257d13f346fb3377e81de7**