Spring Boot 中使用CGLIB动态代理实现AOP编程实战与原理解读

引言

在Java开发中,面向切面编程(AOP)是一种重要的设计思想,它允许开发者将通用的横切关注点如日志记录、事务管理、权限验证等从业务逻辑中分离出来,实现模块化和可复用性。Spring框架提供了强大的AOP支持,其中就包括使用CGLIB动态代理机制来实现代理功能。

CGLIB动态代理产生的背景

JDK自身提供的动态代理机制基于接口实现,其java.lang.reflect.Proxy类能够为我们动态地创建一个实现了指定接口的新类,这个新类在运行时可以代理对原接口方法的调用,从而在调用前后插入额外的操作。然而,这种机制有一个显著的限制,那就是它只适用于实现了接口的类。
面对大量未实现接口的业务类,如果我们仍希望对其进行代理增强,那么就需要一种能够在运行时动态创建子类的解决方案。这就催生了CGLIB动态代理库的广泛应用。CGLIB通过生成被代理类的子类并在子类中重写非final方法来实现动态代理,突破了JDK动态代理仅限于接口的局限,使得代理功能可以应用于几乎所有的Java类。

为何使用CGLIB动态代理

  1. 无接口依赖:CGLIB无需目标类实现任何接口,只要目标类没有声明为final,就可以为其实现代理功能,适用范围更广。
  2. 高效性:CGLIB底层基于ASM字节码生成框架,直接对字节码进行操作,因此在某些场景下的性能表现优于反射代理,特别是在频繁调用代理方法的情况下。
  3. 深度代理:由于CGLIB是通过生成子类的方式实现代理,它可以代理目标类中的所有方法,包括私有方法(虽然在AOP实践中一般不涉及私有方法的增强),并且可以方便地处理final方法以外的成员变量。

Spring Boot中使用CGLIB动态代理示例

package com.example.demo.annotation;

import java.lang.annotation.*;

@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
}
package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {

    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    /**
     * @Pointcut注解用于定义一个切点表达式,即定义在哪些连接点(方法执行、异常抛出等)上应用通知(如前置通知、后置通知、环绕通知等)
     * @annotation(com.example.demo.annotation.Log):匹配所有标记了com.example.demo.annotation.Log注解的方法
     * @within(com.example.demo.annotation.Log):匹配所有声明了com.example.demo.annotation.Log注解的类型(类或接口)。这意味着如果一个类或接口被com.example.demo.annotation.Log注解修饰,那么这个类或接口下的所有公共方法都将作为切点。
     * 通过使用逻辑运算符||,上述两个条件合并为一个切点表达式,表示同时满足上述任意一种情况的方法都会成为切点
     */
    @Pointcut("@annotation(com.example.demo.annotation.Log)"+"|| @within(com.example.demo.annotation.Log)")
    public void dsPointCut() {
    }

    /**
     * @Before:前置通知,在目标方法执行前执行。
     * @param joinPoint
     */
    @Before("dsPointCut()")
    public void beforeMethod(JoinPoint joinPoint) {
        log.info("Pre-notification:Start executing the method");
        log.info("joinPoint.getSignature().getName():{}",joinPoint.getSignature().getName());
        log.info("joinPoint.getArgs():{}",joinPoint.getArgs());
        log.info("joinPoint.getTarget():{}",joinPoint.getTarget());
        log.info("joinPoint.getThis():{}",joinPoint.getThis());
        log.info("joinPoint.getSignature():{}",joinPoint.getSignature());
    }

    /**
     * @After:后置通知(最终通知),无论方法是否抛出异常,都会在方法执行后调用
     * @param joinPoint
     */
    @After("dsPointCut()")
    public void afterMethod(JoinPoint joinPoint) {
        log.info("Post notification: After the method is executed");
    }

    /**
     * @AfterReturning:返回通知,仅在方法正常执行完毕并返回时调用。
     * @param joinPoint
     * @param result
     */
    @AfterReturning(pointcut = "dsPointCut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        log.info("Return notification: The method returns normally, with the result: {}",result);
    }

    /**
     * @AfterThrowing:异常通知,仅在方法抛出异常后调用。
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "dsPointCut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e) {
        log.info("Exception notification: Method execution throws an exception: {}",e.getMessage());
    }

    /**
     * @Around:环绕通知,最为强大的通知类型,能够完全控制何时执行目标方法以及如何处理目标方法的返回值。
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("dsPointCut()")
    public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
        log.info("Around notification: Before the method is executed");
        Object result = pjp.proceed();
        log.info("Around notification: After the method is executed");
        return result;
    }
}
package com.example.demo.controller;

import com.example.demo.annotation.Log;
import com.example.demo.utils.result.ResultEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/log/aspect/test")
public class LogAspectTestController {

    @Log
    @GetMapping("/test")
    public ResultEntity<String> test() {
        return ResultEntity.success("test");
    }

    @Log
    @GetMapping("/testError")
    public ResultEntity<String> testError() {
        int count = 1/0;
        return ResultEntity.success("testError");
    }
}

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值