前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >用心整理 | Spring AOP 干货文章,图文并茂,附带 AOP 示例 ~

用心整理 | Spring AOP 干货文章,图文并茂,附带 AOP 示例 ~

作者头像
create17
发布于 2019-11-13 05:55:44
发布于 2019-11-13 05:55:44
1.1K00
代码可运行
举报
运行总次数:0
代码可运行

Spring AOP 是 Java 面试的必考点,我们需要了解 AOP 的基本概念及原理。那么 Spring AOP 到底是啥,为什么面试官这么喜欢问它呢?本文先介绍 AOP 的基本概念,然后根据 AOP 原理,实现一个接口返回统一格式的小示例,方便大家理解 Spring AOP 到底如何用!

一、为什么要使用 AOP ?

在实际的开发过程中,我们的应用程序会被分为很多层。通常来讲一个 Java 的 Web 程序会拥有以下几个层次:

  • Web 层:主要是暴露一些 Restful API 供前端调用。
  • 业务层:主要是处理具体的业务逻辑。
  • 数据持久层:主要负责数据库的相关操作(增删改查)。

虽然看起来每一层都做着全然不同的事情,但是实际上总会有一些类似的代码,比如日志打印和异常处理等。如果我们选择在每一层都独立编写这部分代码,那么久而久之代码将变的很难维护。所以我们提供了另外的一种解决方案:AOP。这样可以保证这些通用的代码被聚合在一起维护,而且我们可以灵活的选择何处需要使用这些代码。

二、什么是 AOP ?

AOP(Aspect Oriented Programming,面向切面编程),可以说是 OOP(Object Oriented Programing,面向对象编程)的补充和完善。OOP 引入封装、继承和多态性等概念来建立一种对象层次结构,用来模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP 允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能,日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如权限管理、异常处理等也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在 OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而 AOP 技术则恰恰相反,它利用一种称为 “横切” 的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为 “Aspect” ,即切面。所谓“切面”,简单地说,就是将权限、事务、日志、异常等与业务逻辑相对独立的功能抽取封装,便于减少系统的重复代码,降低模块间的耦合度,增加代码的可维护性。AOP 代表的是一个横向的关系,如果说 “对象” 是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向切面编程,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息,然后又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

切面理解:用刀将西瓜分成两瓣,切开的切口就是切面;炒菜、锅与炉子共同来完成炒菜,锅与炉子就是切面。Web 层级设计中,Controller 层、Service 层、Dao 层,每一层之间也是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。

推荐网上的一篇通俗易懂的 AOP 理解:

https://blog.csdn.net/qukaiwei/article/details/50367761

三、AOP 使用场景

  • 权限控制
  • 日志存储
  • 统一异常处理
  • 缓存处理
  • 事务处理
  • ……

四、AOP 专业术语

AOP有很多专业术语,初看这么多术语,可能一下子不大理解,多读几遍,相信很快就会搞懂。

1、Advice(通知)
  • 前置通知(Before advice):在目标方法调用前执行通知
  • 环绕通知(Around advice):在目标方法调用前后均可执行自定义逻辑
  • 返回通知(After returning advice):在目标方法执行成功后,调用通知
  • 异常通知(After throwing advice):在目标方法抛出异常后,执行通知
  • 后置通知(After advice):在目标方法完成(不管是抛出异常还是执行成功)后执行通知
2、JoinPoint(连接点)

就是 Spring 允许你放通知(Advice)的地方,很多,基本每个方法的前、后(两者都有也行)或抛出异常时都可以是连接点,Spring 只支持方法连接点,和方法有关的前前后后都是连接点。

Tips:可以使用连接点获取执行的类名、方法名和参数名等。

3、Pointcut(切入点)

是在连接点的基础上来定义切入点。比如在一个类中,有 15 个方法,那么就会有几十个连接点,但只想让其中几个方法的前后或抛出异常时干点什么,那么就用切入点来定义这几个方法,让切入点来筛选连接点。

4、Aspect(切面)

是通知(Advice)和切入点(Pointcut)的结合,通知(Advice)说明了干什么和什么时候(通过@Before、@Around、@After、@AfterReturning、@AfterThrowing来定义执行时间点)干,切入点(Pointcut)说明了在哪(指定方法)干,这就是一个完整的切面定义。

5、AOP 代理

AOP Proxy:AOP 框架创建的对象,代理就是目标对象的加强。AOP 巧妙的例用动态代理优雅的解决了 OOP 力所不及的问题。Spring 中的 AOP 代理可以是 jdk 动态代理,也可以是 cglib 动态代理。前者基于接口,后者基于子类。

五、AOP 示例:实现 Spring 接口返回统一(正常/异常)格式

读完上面这么多抽象概念,如果不来一个 AOP 具体示例,吸收效果或者理解深度可能不是那么好。所以,请接着往下看:

1、定义返回格式
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import lombok.Data;

@Data
public class Result<T> {

    // code 状态值:0 代表成功,其他数值代表失败
    private Integer code;

    // msg 返回信息。如果code为0,msg则为success;如果code为1,msg则为error
    private String msg;

    // data 返回结果集,使用泛型兼容不同的类型
    private T data;

}
2、定义一些已知异常
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
public enum ExceptionEnum {

    UNKNOW_ERROR(-1, "未知错误"),
    NULL_EXCEPTION(-2, "空指针异常:NullPointerException"),
    INVALID_EXCEPTION(1146, "无效的数据访问资源使用异常:InvalidDataAccessResourceUsageException");

    public Integer code;

    public String msg;

}
3、异常类捕获并返回
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//@ControllerAdvice
@Component
@Slf4j
public class ExceptionHandle {

    //    @ExceptionHandler(value = Exception.class)
//    @ResponseBody
    public Result exceptionGet(Throwable t) {
        log.error("异常信息:", t);
        if (t instanceof InvalidDataAccessResourceUsageException) {
            return ResultUtil.error(ExceptionEnum.INVALID_EXCEPTION);
        } else if (t instanceof NullPointerException) {
            return ResultUtil.error(ExceptionEnum.NULL_EXCEPTION);
        }
        return ResultUtil.error(ExceptionEnum.UNKNOW_ERROR);
    }

}

制作一个结果返回工具类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ResultUtil {

    /**
     * @return com.study.spring.entity.Result
     * @description 接口调用成功返回的数据格式
     * @param: object
     */
    public static Result success(Object object) {
        Result result = new Result();
        result.setCode(0);
        result.setMsg("success");
        result.setData(object);
        return result;
    }

    /**
     * @return com.study.spring.entity.Result
     * @description 接口调用失败返回的数据格式
     * @param: code
     * @param: msg
     */
    public static Result error(Integer code, String msg) {
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(null);
        return result;
    }

    /**
     * 返回异常信息,在已知的范围内
     *
     * @param exceptionEnum
     * @return
     */
    public static Result error(ExceptionEnum exceptionEnum) {
        Result result = new Result();
        result.setCode(exceptionEnum.code);
        result.setMsg(exceptionEnum.msg);
        result.setData(null);
        return result;
    }

}
4、pom 依赖

必须要添加 spring aop 等相关依赖:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!-- web 依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- aop 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 用于日志切面中,以 json 格式打印出入参 -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>
<!-- lombok 简化代码-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
5、自定义注解
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface HandleResult {

    String desc() default "create17";

}

上述代码内有些概念需要解释说明:

  • @Retention:定义注解的保留策略
    • @Retention(RetentionPolicy.SOURCE) :注解保留在源码中,当 Java 文件编译成 class 字节码文件的时候,注解被遗弃。
    • @Retention(RetentionPolicy.CLASS :默认的保留策略,注解会保留在 class 字节码文件中,但运行( jvm 加载 class 字节码文件)时会被遗弃。
    • @Retention(RetentionPolicy.RUNTIME) :注解保留在 class 字节码文件中,在运行时也可以通过反射获取到。
  • @Target:定义注解的作用目标,可多个,用逗号分隔。
    • @Target(ElementType.TYPE) :作用于接口、类、枚举、注解
    • @Target(ElementType.FIELD) :作用于字段、枚举的常量
    • @Target(ElementType.METHOD) :作用于方法,不包含构造方法
    • @Target(ElementType.PARAMETER) :作用于方法的参数
    • @Target(ElementType.CONSTRUCTOR) :作用于构造方法
    • @Target(ElementType.LOCAL_VARIABLE) :作用于本地变量
    • @Target(ElementType.ANNOTATION_TYPE) :作用于注解
    • @Target(ElementType.PACKAGE) :作用于包
  • @Document:说明该注解将被包含在javadoc中。
  • @Inherited:说明子类可以继承父类中的该注解。
  • @interface:声明自定义注解。
  • desc():定义一个属性,默认为 create17。具体使用为:@HandleResult(desc = "描述内容…")

到这里,一个完整的自定义注解就定义完成了。

6、切面实现
1)首先我们定义一个切面类 HandleResultAspect
  • 使用 @Aspect 注解来定义切面,将当前类标识为一个切面供容器管理,必不可少。
  • 使用 @Component 注解来定义组件,将当前类标识为一个组件供容器管理,也必不可少。
  • 使用 @Slf4j 注解来打印日志;
  • 使用 @Order(i) 注解来表示切面的顺序,后文会详细讲。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Aspect
@Component
@Slf4j
@Order(100)
public class HandleResultAspect {
    ...
}        
2)接下来,我们定义一个切点。

使用 @Pointcut 来定义一个切点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Pointcut("@annotation(com.study.spring.annotation.HandleResult)")
// @Pointcut("execution(* com.study.spring.controller..*.*(..))")
public void HandleResult() {
}

对于 execution 表达式,官网对 execution 表达式的介绍为:

execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)

除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。这个解释可能有点难理解,下面我们通过一个具体的例子来了解一下。在 HandleResultAspect 中我们定义了一个切点,其 execution 表达式为:* com.study.spring.controller..*.*(..)),下表为该表达式比较通俗的解析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
表达式的主体

上述的 execution 表达式是把 com.study.spring.controller 下所有的方法当作一个切点。@Pointcut 除了可以使用 execution 表达式之外,还可用 @annotation 来指定注解切入,比如可指定上面创建的自定义注解 @HandleResult ,@HandleResult 在哪里被使用,哪里就是一个切点。

3)说一下 Advice(通知)有关的切面注解
  • @Before:修饰的方法会在进入切点之前执行。在这个部分,我们需要打印一个开始执行的日志,比如:类型、方法名、参数名等。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Before(value = "HandleResult() && @annotation(t)", argNames = "joinPoint,t")
public void doBefore(JoinPoint joinPoint, HandleResult t) throws Exception {
    // 类名
    String className = joinPoint.getTarget().getClass().getName();
    // 方法名
    String methodName = joinPoint.getSignature().getName();
    // 参数名
    Object[] args = joinPoint.getArgs();
    StringBuilder sb = new StringBuilder();
    if (args != null && args.length > 0) {
        for (Object arg : args) {
            sb.append(arg).append(", ");
        }
    }
    log.info("接口 {} 开始被调用, 类名: {}, 方法名: {}, 参数名为: {} .",
             t.desc(), className, methodName, sb.toString());
}
  • @Around:修饰的方法会环绕整个切点,可以在切入点前后织入代码,并可以自由地控制何时执行切点。通俗点讲就是:在进入切点前执行一部分逻辑,然后进入切点执行业务逻辑(ProceedingJoinPoint.proceed() 方法可用来接收业务逻辑的返回信息),最后出切点执行另一部分逻辑。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Around("HandleResult()")
public Result doAround(ProceedingJoinPoint point) {
    long startTime = System.currentTimeMillis();
    log.info("---HandleResultAspect--Around的前半部分----------------------------");
    Object result;
    try {
        // 执行切点。point.proceed 为方法返回值
        result = point.proceed();
        // 打印出参
        log.info("接口原输出内容: {}", new Gson().toJson(result));
        // 执行耗时
        log.info("执行耗时:{} ms", System.currentTimeMillis() - startTime);
        return ResultUtil.success(result);
    } catch (Throwable throwable) {
        return exceptionHandle.exceptionGet(throwable);
    }
}
  • @After:修饰的方法和 @Before 相对应,无论程序执行正常还是异常,均执行该方法。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@After("HandleResult()")
public void doAfter() {
    log.info("doAfter...");
}
  • @AfterReturning:在切点正常执行后,执行该方法,一般用于对返回值做些加工处理的场景。

returning 可接收接口最终地返回信息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@AfterReturning(pointcut = "@annotation(t)", returning = "res")
public void afterReturn(HandleResult t, Object res) {
    log.info("接口 {} 被调用已结束, 最终返回结果为: {} .",
             t.desc(), new Gson().toJson(res));
}
  • @AfterThrowing:在切点抛出异常后,执行该方法。

throwing 可用来获取异常信息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@AfterThrowing(throwing = "throwable", pointcut = "HandleResult()")
public void afterThrowing(Throwable throwable) {
    log.info("After throwing...", throwable);
}

关于这些通知的执行顺序如下图所示:

以下为切面实现的全部代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Aspect
@Component
@Slf4j
@Order(100)
public class HandleResultAspect {

    @Autowired
    private ExceptionHandle exceptionHandle;

    /**
     * @return void
     * @description 定义切点
     */
    @Pointcut("@annotation(com.study.spring.annotation.HandleResult)")
//    @Pointcut("execution(* com.study.spring.controller..*.*(..))")
    public void HandleResult() {
    }

    /**
     * @return void
     * @description 打印接口名、类名、方法名及参数名
     * @param: joinPoint
     * @param: t
     */
    @Before(value = "@annotation(t)", argNames = "joinPoint,t")
    public void doBefore(JoinPoint joinPoint, HandleResult t) throws Exception {
        // 类名
        String className = joinPoint.getTarget().getClass().getName();
        // 方法名
        String methodName = joinPoint.getSignature().getName();
        // 参数名
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        if (args != null && args.length > 0) {
            for (Object arg : args) {
                sb.append(arg).append(", ");
            }
        }
        log.info("接口 {} 开始被调用, 类名: {}, 方法名: {}, 参数名为: {} .",
                t.desc(), className, methodName, sb.toString());
    }

    /**
     * @return java.lang.Object
     * @description 定义@Around环绕,用于何时执行切点
     * @param: proceedingJoinPoint
     */
    @Around("HandleResult()")
    public Result doAround(ProceedingJoinPoint point) {
        long startTime = System.currentTimeMillis();
        log.info("---HandleResultAspect--Around的前半部分----------------------------");
        Object result;
        try {
            // 执行切点。point.proceed 为方法返回值
            result = point.proceed();
            // 打印出参
            log.info("接口原输出内容: {}", new Gson().toJson(result));
            // 执行耗时
            log.info("执行耗时:{} ms", System.currentTimeMillis() - startTime);
            return ResultUtil.success(result);
        } catch (Throwable throwable) {
            return exceptionHandle.exceptionGet(throwable);
        }
    }

    /**
     * @return void
     * @description 程序无论正常还是异常,均执行的方法
     * @param:
     */
    @After("HandleResult()")
    public void doAfter() {
        log.info("doAfter...");
    }

    /**
     * @return void
     * @description 当程序运行正常,所执行的方法
     * 以json格式打印接口执行结果
     * @param: t
     * @param: res
     */
    @AfterReturning(pointcut = "@annotation(t)", returning = "res")
    public void afterReturn(HandleResult t, Object res) {
        log.info("接口 {} 被调用已结束, 接口最终返回结果为: {} .",
                t.desc(), new Gson().toJson(res));
    }

    /**
     * @return void
     * @description 当程序运行异常,所执行的方法
     * 可用来打印异常
     * @param: throwable
     */
    @AfterThrowing(throwing = "throwable", pointcut = "HandleResult()")
    public void afterThrowing(Throwable throwable) {
        log.info("After throwing...", throwable);
    }

}

六、多切面的执行顺序

在生产中,我们的项目可能不止一个切面,那么在多切面的情况下,如何指定切面的优先级呢?

我们可以使用 @Order(i) 注解来定义切面的优先级,i 值越小,优先级越高。

比如我们再创建一个切面,代码示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Aspect
@Component
@Order(50)
@Slf4j
public class TestAspect2 {

    @Pointcut("@annotation(com.study.spring.annotation.HandleResult)")
    public void aa(){

    }

    @Before("aa()")
    public void bb(JoinPoint joinPoint){
        log.info("我是 TestAspect2 的 Before 方法...");
    }

    @Around("aa()")
    public Object cc(ProceedingJoinPoint point){
        log.info("我是 TestAspect2 的 Around 方法的前半部分...");
        Object result = null;
        try {
            result = point.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        log.info("我是 TestAspect2 的 Around 方法的后半部分...");
        return result;
    }

    @After("aa()")
    public void doAfter() {
        log.info("我是 TestAspect2 的 After 方法...");
    }

    @AfterReturning("aa()")
    public void afterReturn() {
        log.info("我是 TestAspect2 的 AfterReturning 方法...");
    }

    @AfterThrowing("aa()")
    public void afterThrowing() {
        log.info("我是 TestAspect2 的 AfterThrowing 方法...");
    }
}

切面 TestAspect2 为 @Order(50),之前的切面 HandleResultAspect 为 Order(100)。测试接口返回的日志如下图所示:

总结一下规律就是:

  • 在执行切点之前,@Order 从小到大被执行,也就是说 Order 越小的优先级越高;
  • 在执行切点之后,@Order 从大到小被执行,也就是说 Order 越大的优先级越高;

也就是:先进后出的原则。为了方便我们理解,我画了一个图,如下图所示:

七、如何设置在特定环境下使用AOP

一般在项目开发中,都会设置三个环境:开发、测试、生产。那么如果我只想在 开发 和 测试 环境下使用某切面该怎么办呢?我们只需要在指定的切面类上方加上注解 @Profile 就可以了,如下所示:

这样就指定了 HandleResultAspect 该切面只能在 dev(开发)环境、test(测试)环境下生效,prod(生产)环境不生效。当然,你需要创建相应的 application-${dev/test/prod}.yml 文件,最后在 application.yml 文件内指定 spring.profiles.active 属性为 dev 或 test 才可以生效。

八、总结

本文篇幅较长,但总算对 Spring AOP 有了一个简单的了解。从 AOP 的起源到概念、使用场景,然后深入了解其专业术语,利用 AOP 思想实现了示例,方便我们自己理解。读完这篇文章,相信大家对 Spring AOP 有了更深的认识了!

本文所涉及的代码已上传至 github :

https://github.com/841809077/spring-boot-study/tree/master/src/main/java/com/study/spring/annotation

本文参考链接:

  • https://www.exception.site/springboot/spring-boot-aop-web-request
  • https://www.ibm.com/developerworks/cn/java/j-spring-boot-aop-web-log-processing-and-distributed-locking/index.html
  • https://blog.csdn.net/w05980598/article/details/79070388
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大数据实战演练 微信公众号,前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring Boot如何利用AOP巧妙记录操作日志?
SpringAOP的相关的知识点包括源码解析,我已经在之前的文章中详细说明,如果对AOP的概念还不是特别清晰的话。
烂猪皮
2021/01/14
1.1K0
Spring Boot如何利用AOP巧妙记录操作日志?
Spring Aop该如何使用
AOP(Aspect OrientedProgramming),即面向切面编程。本文介绍了AOP的相关概念和术语,用业务场景演示了Spring Aop的使用方式。希望本文对你轻松使用Spring Aop有所帮助。
每周聚焦
2024/11/13
1470
Spring Aop该如何使用
Spring学习总结(三)——Spring实现AOP的多种方式
AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的横向多模块统一控制的一种技术。AOP是OOP的补充,是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP可以分为静态织入与动态织入,静态织入即在编译前将需织入内容写入目标模块中,这样成本非常高。动态织入则不需要改变目标模块。Spring框架实现了AOP,使用注解配置完成AOP比使用XML配置要更加方便与直观。上一篇随笔中已经详细讲了代理模式。
张果
2022/05/09
4630
Spring学习总结(三)——Spring实现AOP的多种方式
【Spring Boot】Spring AOP 快速上手指南:开启面向切面编程新旅程
AOP是Spring框架的第⼆⼤核⼼(第⼀⼤核⼼是IoC),说明AOP也是非常重要的;
用户11288949
2025/01/17
2050
【Spring Boot】Spring AOP 快速上手指南:开启面向切面编程新旅程
Spring-AOP详解(AOP概念,原理,动态代理,静态代理)
blog.csdnimg.cn/direct/bda1b5ffb0784f6ca6e1f0da2fcfec7d.png)
用户11305962
2025/04/07
4650
Spring-AOP详解(AOP概念,原理,动态代理,静态代理)
Spring AOP技术原理及利用自定义注解验证数据正确性
1. 切面(Aspect):一个切面由多个通知(Advice)组成,代表了交叉业务逻辑的模块。通知包含了要在目标方法执行前后执行的代码。
用户7353950
2024/04/30
3040
Spring AOP技术原理及利用自定义注解验证数据正确性
【Spring进阶】基于注解的面向切面编程(AOP)详解
面向切面编程(AOP)是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高代码的模块化和可维护性。在Java中,AOP通常通过使用框架如Spring来实现。
王也518
2024/04/22
1.3K0
Spring系列六:AOP概念和使用
AOP:面向切面编程。简单说,就是把一些业务逻辑中的相同的代码抽取到一个独立的模块中,让业务逻辑更加清爽。
叶秋学长
2022/07/27
2920
Spring系列六:AOP概念和使用
深入理解AOP思想
概念 : AOP为Aspect Oriented Programming的缩写, 意思为切面编程。
用户11097514
2024/05/30
1080
深入理解AOP思想
【Spring 篇】注解之舞:Spring AOP的优雅表演
欢迎来到Spring的代码舞台,在这里,我们将沉浸在一场注解之舞的盛宴中。今天我们将探讨如何使用注解方式实现Spring AOP,一种优雅而富有表现力的编程技术。
繁依Fanyi
2024/01/11
3820
【Spring 篇】注解之舞:Spring AOP的优雅表演
Java架构笔记——Spring AOP的实现方式
AOP常用的实现方式有两种,一种是采用声明的方式来实现(基于XML),一种是采用注解的方式来实现(基于AspectJ)。
慕容千语
2019/06/11
6010
面试:Spring Boot项目怎么用AOP
将通用的逻辑用AOP技术实现可以极大的简化程序的编写,例如验签、鉴权等。Spring的声明式事务也是通过AOP技术实现的。 Spring的AOP技术主要有4个核心概念:
用户5546570
2019/06/06
6420
【Spring学习】AOP实现日志记录
AOP,面向切面编程。通过预编译方式和运行时动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
pbinlog
2022/03/21
6880
【Spring学习】AOP实现日志记录
给SpringBoot接口加全局日志(AOP)
一想到,每个接口都需要加一个开始时间和一个结束时间,我就感觉特别烦,就是不想撸代码。
猿码优创
2019/07/27
2K0
一文搞懂Spring-AOP原理
文章目录 1. 简介 2. 添加依赖 3. 通知 4. 连接点 5. 切点 6. 切面 7. 实现 8. 注解的实现 9. 切入点表达式 10. 切面执行顺序(Order) 10.1. 注意点 11. Aspect实例化模型 12. 获取参数(args) 13. PointCut 14. PointCuts 15. ClassFilter 16. ClassFilters 17. MethodMatcher 18. MethodMatchers 19. Advice 20. Advisor 20.1.
爱撒谎的男孩
2019/12/31
1.1K0
Spring-AOP思想
加入Jar包 com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aop-4.3.18.RELEASE.jar spring-aspects-4.3.18.RELEASE.jar
Java开发者之家
2021/06/17
2730
Spring官网阅读(十八)AOP的核心概念
在前面的文章中我们已经对IOC做过详细的介绍了,本文主要介绍AOP,关于其中的源码部分将在专门的源码专题介绍,本文主要涉及的是AOP的基本概念以及如何使用,本文主要涉及到官网中的第5、6两大章 ”
程序员DMZ
2020/07/07
7980
SpringBoot 利用 AOP 记录日志
Aspect Oriented Programming 面向切面编程。解耦是程序员编码开发过程中一直追求的。AOP也是为了解耦所诞生。
JAVA日知录
2020/05/25
1.9K0
【Spring AOP】@Aspect结合案例详解(一): @Pointcut使用@annotation + 五种通知Advice注解
在微服务流行的当下,在使用SpringCloud/Springboot框架开发中,AOP使用的非常广泛,尤其是@Aspect注解方式当属最流行的,不止功能强大,性能也很优秀,还很舒心!所以本系列就结合案例详细介绍@Aspect方式的切面的各种用法,力求覆盖日常开发中的各种场景。本文带来的案例是:打印Log,主要介绍@Pointcut切点表达式的@annotation方式,以及 五种通知Advice注解:@Before、@After、@AfterRunning、@AfterThrowing、@Around。
天罡gg
2023/01/08
7.2K0
【Spring AOP】@Aspect结合案例详解(一): @Pointcut使用@annotation + 五种通知Advice注解
Spring-AOP
AOP引介 AOP(Aspect Oriented Programing)面向切面编程采用横向抽取机制,以取代传统的纵向继承体系的重复性代码(如性能监控/事务管理/安全检查/缓存实现等). 横向抽取代码复用: 基于代理技术,在不修改原来代码的前提下,对原有方法进行增强. Spring AOP 历史 1.2开始, Spring开始支持AOP技术(Spring AOP) Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码. 2.0之后, 为了简化A
Java帮帮
2018/03/16
1.2K1
相关推荐
Spring Boot如何利用AOP巧妙记录操作日志?
更多 >
LV.2
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验