首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring-AOP详解(AOP概念,原理,动态代理,静态代理)

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

作者头像
用户11305962
发布2025-04-07 08:15:20
发布2025-04-07 08:15:20
1.4K10
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

什么是AOP:

AOP是Spring框架的第二大核心(第一大核心是oC),AOP又叫一>Aspect Oriented Programming(面向切面编程),简单来说: AOP是⼀种思想, 是对某⼀类事情的集中处理

Spring AOP核心概念

需要先引入AOP依赖:

在pom.xml⽂件中添加配置

代码语言:javascript
代码运行次数:0
运行
复制
<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:环绕通知,此注解标注的通知方法在目标方法前,后都被执行:
代码语言:javascript
代码运行次数:0
运行
复制
@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:前置通知,此注解标注的通知方法在目标方法前被执行
代码语言:javascript
代码运行次数:0
运行
复制
@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:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
代码语言:javascript
代码运行次数:0
运行
复制
@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:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行)
代码语言:javascript
代码运行次数:0
运行
复制
 @AfterReturning("pt()")
    public void doAfterReturning(){
        log.info("doAfterReturning...");
    }


@AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行
代码语言:javascript
代码运行次数:0
运行
复制
 @AfterThrowing("pt()")
    public void doAfterThrowing(){
        log.info("doAfterThrowing...");
    }
代码语言:javascript
代码运行次数:0
运行
复制
@RequestMapping("t1")
    public Boolean t1(){
        log.info("执行t1");
        int ret = 10/0;
        return true;
    }


自定义注解:

1.创建一个注解类:

代码语言:javascript
代码运行次数:0
运行
复制
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 生效:

代码语言:javascript
代码运行次数:0
运行
复制
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 接口:

代码语言:javascript
代码运行次数:0
运行
复制
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;
    }
}

执行:

代码语言:javascript
代码运行次数:0
运行
复制
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() , ⽤于 处理被代理对象的⽅法调用

代码语言:javascript
代码运行次数:0
运行
复制
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;
    }
}
代码语言:javascript
代码运行次数:0
运行
复制
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两个代理都使用,而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 Spring-Boot 3.xxx版本,只使用CGLIB来动态代理实现AOP

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是AOP:
  • Spring AOP核心概念
    • 需要先引入AOP依赖:
    • 1.切点(Pointcut):
    • 2.连接点:
    • 3.通知(Advice):
    • 4.切面:
    • 通知类型:
      • @Around:环绕通知,此注解标注的通知方法在目标方法前,后都被执行:
      • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
      • @After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
      • @AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行)
      • @AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行
  • 自定义注解:
  • Spring AOP 原理:
    • 代理模式:
      • 静态代理:
  • 动态代理:
    • 1.1.JDK动态代理:
    • 2.CGLIB动态代理:
  • CGLIB代理和JDK代理的区别:
    • 什么时候用JDK代理,什么时候用CGLIB代理?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档