一.什么是AOP
二.AOP的典型应用场景
三.关于AOP的核心知识点
切点表达式说明
定义相关通知
四.AOP的实现原理
Spring AOP 是构建在 动态代理 基础上,因此 Spring 对 AOP 的支持局限于方法级别的拦截 。
Spring AOP 支持 JDK Proxy 和 CGLIB 方式实现动态代理。默认情况下,实现了接口的类(被代理类),使用 AOP 会基于 JDK 生成代理类,没有实现接口的类,会基于 CGLIB 生成代理类。
织入( Weaving ):代理的生成时机
织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。
在目标对象的生命周期里有多个点可以进行织入:
编译期: 切面在目标类编译时被织入。这种方式需要特殊的编译器。 AspectJ 的织入编译器就是以这种
方式织入切面的。
类加载器: 切面在目标类加载到 JVM 时被织入。这种方式需要特殊的类加载器( ClassLoader ) , 它可以 在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的加载时织入( load-timeweaving. LTW )
就支持以这种方式织入切面。
运行期: 切面在应用运行的某一时刻被织入。一般情况下,在织入切面时, AOP 容器会为目标对象动态创建一个代理对象。SpringAOP 就是以这种方式织入切面的。
五.AOP的实现方式
JDK和GCLIB实现的代理类如下:
JDK
import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态代理:使用JDK提供的api(InvocationHandler、Proxy实现),此种方式实现,要求被代理类必须实现接口
public class PayServiceJDKInvocationHandler implements InvocationHandler {
//目标对象即就是被代理对象
private Object target;
public PayServiceJDKInvocationHandler(Object target) {
this.target = target;
}
//proxy代理对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
//1.安全检查
System.out.println("安全检查");
//2.记录日志
System.out.println("记录日志");
//3.时间统计开始
System.out.println("记录开始时间");
//通过反射调用被代理类的方法
Object retVal = method.invoke(target, args);
//4.时间统计结束
System.out.println("记录结束时间");
return retVal;
}
public static void main(String[] args) {
PayService target = new AliPayService();
//方法调用处理器
InvocationHandler handler =
new PayServiceJDKInvocationHandler(target);
//创建一个代理类:通过被代理类、被代理实现的接口、方法调用处理器来创建
PayService proxy = (PayService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class[]{PayService.class},
handler
);
proxy.pay();
}
}
GCLIB
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;
import java.lang.reflect.Method;
public class PayServiceCGLIBInterceptor implements MethodInterceptor {
//被代理对象
private Object target;
public PayServiceCGLIBInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy
methodProxy) throws Throwable {
//1.安全检查
System.out.println("安全检查");
//2.记录日志
System.out.println("记录日志");
//3.时间统计开始
System.out.println("记录开始时间");
//通过cglib的代理方法调用
Object retVal = methodProxy.invoke(target, args);
//4.时间统计结束
System.out.println("记录结束时间");
return retVal;
}
public static void main(String[] args) {
PayService target = new AliPayService();
PayService proxy = (PayService) Enhancer.create(target.getClass(), new
PayServiceCGLIBInterceptor(target));
proxy.pay();
}
}
六.AOP的使用
SpringAOP在使用时主要有以下两种使用方式:
①使用aspectj风格的注解进行开发:
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定义通知时机和通知逻辑
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author tongchen
* @create 2023-05-08 11:14
*/
@Aspect
//将配置加载到容器中
@Component
public class AopConfig {
//定义切点
//包后面一定要有一个空格
@Pointcut("execution(* com.ljl..service.*Service.*(..))")
public void pointCut(){
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint){
//pointcut匹配的方法
try {
Long start=System.currentTimeMillis();
Object proceed = joinPoint.proceed();
Long end=System.currentTimeMillis();
System.out.println("方法运行的时间:"+(end-start));
} catch (Throwable e) {
throw new RuntimeException(e);
}
return null;
}
}
定义切点方法
import org.springframework.stereotype.Service;
import java.util.ArrayList;
/**
* @author tongchen
* @create 2023-05-08 11:13
*/
@Service
public class AspectService {
public Object timeTest(){
//模拟获取数据库中的数据
return new ArrayList<Integer>();
}
}
②定义一个类,实现MethodInteceptor