什么是AOP:
AOP是Spring框架的第二大核心(第一大核心是oC),AOP又叫一>Aspect Oriented Programming(面向切面编程),简单来说: AOP是⼀种思想, 是对某⼀类事情的集中处理
Spring AOP核心概念
需要先引入AOP依赖:
在pom.xml⽂件中添加配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1.切点(Pointcut):
Pointcut 的作用就是提供⼀组规则 (使用 AspectJ pointcut expression language 来描述), 告诉程序对
哪些方法来进行功能增强. @Aspect: 标识这是⼀个切面类
2.连接点:
满足切点表达的方法,就是可以被AOP控制的方法:
上面的recordTime方法就是一个连接点
3.通知(Advice):
连接点(方法)里面要做的工作,要实现的业务,比如下面的计时功能
4.切面:
切⾯(Aspect) = 切点(Pointcut) + 通知(Advice)
所以切⾯既包含了通知逻辑的定义, 也包括了连接点的定义
通知类型:
@Around:环绕通知,此注解标注的通知方法在目标方法前,后都被执行:
@Slf4j
@Component
@Aspect
public class AspectDemo1 {
@Pointcut("execution(* com.suli.springaopdemo.Controller.*.*(..))")
public void pt(){}
@Around("pt()")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object result = null;
try {
result = pjp.proceed();//让原始⽅法执⾏
}catch (Throwable e){
log.error("doAfterThrowing..");
}
long end = System.currentTimeMillis();
log.info(pjp.getSignature() + "执行时间: {}ms", end - begin);
return result;
}
}
**
**
@Before:前置通知,此注解标注的通知方法在目标方法前被执行
@Slf4j
@Component
@Aspect
public class AspectDemo1 {
@Pointcut("execution(* com.suli.springaopdemo.Controller.*.*(..))")
public void pt(){}
/*@Around("pt()")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object result = null;
try {
result = pjp.proceed();//让原始⽅法执⾏
}catch (Throwable e){
log.error("doAfterThrowing..");
}
long end = System.currentTimeMillis();
log.info(pjp.getSignature() + "执行时间: {}ms", end - begin);
return result;
}*/
@Before("pt()")
public void doBefore(){
log.info("doBefore...");
}
@After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
@Slf4j
@Component
@Aspect
public class AspectDemo1 {
@Pointcut("execution(* com.suli.springaopdemo.Controller.*.*(..))")
public void pt(){}
/*@Around("pt()")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object result = null;
try {
result = pjp.proceed();//让原始⽅法执⾏
}catch (Throwable e){
log.error("doAfterThrowing..");
}
long end = System.currentTimeMillis();
log.info(pjp.getSignature() + "执行时间: {}ms", end - begin);
return result;
}*/
/* @Before("pt()")
public void doBefore(){
log.info("doBefore...");
}*/
@After("pt()")
public void doAfter(){
log.info("doAfter...");
}
blog.csdnimg.cn/direct/bda1b5ffb0784f6ca6e1f0da2fcfec7d.png)
@AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行)
@AfterReturning("pt()")
public void doAfterReturning(){
log.info("doAfterReturning...");
}
@AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行
@AfterThrowing("pt()")
public void doAfterThrowing(){
log.info("doAfterThrowing...");
}
@RequestMapping("t1")
public Boolean t1(){
log.info("执行t1");
int ret = 10/0;
return true;
}
自定义注解:
1.创建一个注解类:
package com.suli.springaopdemo.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}
2.使用annotation注解自定义切点表达式,对MyAspect 生效:
package com.suli.springaopdemo.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class MyAspectDemo {
// @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
@Around("@annotation(com.suli.springaopdemo.aspect.MyAspect)")
public Object recordTime(ProceedingJoinPoint pjp){
log.info("目标方法执行前...");
//执行目标方法
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable throwable) {
log.error("目标方法执行后...");
}
log.info("目标方法执行后....");
return result;
}
}
Spring AOP 原理:
Spring AOP是基于动态代理来实现AOP的
代理模式:
让我们在调用目标方法的时候, 不再是直接对目标方法进行调用, 而是通过代理类间接调用,但是要做的的事情不是中介完成还是得目标完成,这就是代理
静态代理:
静态代理就是,在程序运行前中介就已经存在了,直接给你一个中介(.class文件已经生成好了),下面来演示:
(https://i-blog.csdnimg.cn/direct/0e1e28c6e1f7469fa214e74d5f58a9b8.png)
结果:
动态代理:
我们不需要自己提前创建好代理对象,而是把创建代理对象推迟到程序运行时,让JVM来帮我们创建,也就是说代理对象是 根据需要动态创建生成
动态代理的两种方式:1.JDK动态代理,2.CGLIB动态代理
1.1.JDK动态代理:
实现 InvocationHandler 接口:
package com.suli.springaopdemo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKInvocation implements InvocationHandler {
/**
* 目标对象, 被代理对象target
*/
private Object target;
public JDKInvocation(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我开始代理...");
Object invoke = method.invoke(target,args);
System.out.println("我结束代理");
return invoke;
}
}
执行:
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
HouseSubject target = new RealHouseSubject();
//JDK动态代理
HouseSubject houseProxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),
new Class[]{HouseSubject.class},new JDKInvocation(target));
houseProxy.saleHouse();
}
2.CGLIB动态代理:
实现MethodInterceptor接口:
nvocationHandler 接⼝是Java动态代理的关键接口之⼀, 它定义了⼀个单一方法 invoke() , ⽤于
处理被代理对象的⽅法调用
package com.suli.springaopdemo.proxy;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGlibMethodInterceptor implements MethodInterceptor {
private Object target;
public CGlibMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("我开始代理");
Object invoke = method.invoke(target,args);
System.out.println("我结束代理");
return invoke;
}
}
public class Main {
public static void main(String[] args) {
HouseSubject target = new RealHouseSubject();
//Cglib 动态代理
HouseSubject houseProxy = (HouseSubject) Enhancer.create(target.getClass(),
new CGlibMethodInterceptor(target));
houseProxy.rentHouse();
}
CGLIB代理和JDK代理的区别:
.JDK只能代理接口,CGLIB既可以代理类又可以代理接口
什么时候用JDK代理,什么时候用CGLIB代理?
Spring对于AOP的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成
⽣成代理对象的逻辑在⽗类 AbstractAutoProxyCreator 中
Spring-Framework和Spring-Boot一些东西基本上是一致的,因为Spring-Boot对Spring-Framework进行了封装,而对于AOP这个是不同的
重点:1.Spring-Framework的proxytargetclass默认值为false,所以Spring-Framework,JDK和CGLIB两个代理都使用
2.而Spring-Boot需要区分版本:对于Spring-Boot 2.xxx版本,proxytargetclass默认值也为false.所以,Spring-Boot 2.xxx版本也是JDK和CGLIB两个代理都使用, 但是默认值为true所以一般也默认使用cglib,而Spring-Boot 3.xxx版本proxytargetclass默认值则为true,所以Spring-Boot 3.xxx版本使用CGLIB来动态代理实现AOP
总结:1.Spring-Framework,JDK和CGLIB两个代理都使用来实现AOP。
2.Spring-Boot需要区分版本:Spring-Boot 2.xxx版本JDK和CGLIB两个代理都使用来实现AOP; 默认一般也默认使用cglib
Spring-Boot 3.xxx版本,只使用CGLIB来动态代理实现AOP