Spring-AOP详解(AOP概念,原理,动态代理,静态代理)

什么是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

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

robin_suli

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值