AspectJ使用以及和Spring AOP的区别

AspectJ是什么?

使用Java代码进行面向切面编程(AOP),如果类的继承是纵向复用代码,那AOP就是横向复用代码,横向地在多个没有类继承关系的类之间复用代码。

AspectJ是怎么实现的?

通过在编译时,在编译出来的类的字节码文件中,动态地添加我们想要的功能(有些博客说是有三种方式织入字节码,分别是编译时、编译后、加载前,这三种可以统称为运行前),对类和方法进行增强,这就叫做织入,在Java中,是使用一个叫做aspectjweaver的第三方包来使用AspectJ,weaver就是织入的意思。

AspectJ和SpringAOP之间的关系

相比于SpringAOP使用动态代理来对类进行增强,AspectJ有着更好的性能,因为AspectJ在编译时,就直接修改了目标类的字节码文件,运行时就不用再做修改了。
而SpringAOP是JVM运行时再生成代理类的字节码文件,再通过反射(JDK动态代理使用发射创建代理类,或者调用目标类)或者调用父类的方式对目标类的方法进行调用,有着更多的时间和空间开销。
AspectJ也有着更为丰富的功能,比如SpringAOP如何使用CGLIB,则无法对被标注为final的类或者方法进行继承或者重写,而AspectJ因为是运行前织入字节码,则没有这个限制。

AspectJ和JDK动态代理、CGLib之间的关系

AspectJ是一种可以在Java代码中运用AOP的工具,我们一般是通过第三方包aspectjweaver来使用它。
JDK动态代理是Java自带的使用代理模式的JDK官方包,我们可以在不引入任何第三方包的情况下使用它,但是JDK动态代理一般不是使用AOP的直接方式,CGLIB和JDK动态代理一样,都是使用动态代理模式的一种工具或者包,都是在运行时生成代理类,而SpringAOP就是通过动态代理来实现AOP的。

SpringBoot项目中使用AspectJ

  • 引入三方依赖
    spring-aop:AOP核心功能,例如代理工厂等等。这里不需要引入。

aspectjweaver:简单理解,支持切入点表达式等等。

aspectjrt:简单理解,支持aop相关注解等等。

由于aspectjweaver是包含aspectjrt,所以只需要引入aspectjweaver即可。
在pom.xml文件中引入其他依赖。

<!-- 1. 父工程依赖:父工程设置为springboot,则当前工程就是springboot工程 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
</parent>
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.14</version>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.20</version>
    <scope>provided</scope>
  </dependency>
</dependencies>
  • 创建需要被切面切中的类

  • 创建切面
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
//@EnableAspectJAutoProxy // 不需要添加这个注解,就能运行
public class ControllerAspect {

    /**
     * 此方法只是定义切面,具体切面会怎样对目标类和方法进行增强,由后面的@Before、@Around等注解的方法进行指定。
     */
    @Pointcut("execution(* com.qqcr.train.aspectjweaver.controller..*.*(..))")
    private void testControllerPointcut() {

    }

    /**
     * 对testControllerPointcut()方法上的注解@Pointcut定义的切面进行前置操作。
     *
     * @param joinPoint 通过joinPoint可以获取方法的全限定名称、参数等信息
     */
    @Before("testControllerPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        String method = joinPoint.getSignature().getName();
        String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
        log.info("------@Before:class:{},method:{}", declaringTypeName, method);
    }

    /**
     * 对testControllerPointcut()方法上的注解@Pointcut定义的切面进行环绕操作。
     * 环绕通知可以调用真正的方法,具体的调用是joinPoint.proceed();
     *
     * @param joinPoint 通过joinPoint可以获取方法的全限定名称、参数等信息
     */
    @Around("testControllerPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.nanoTime();
        Object obj = joinPoint.proceed();
        long end = System.nanoTime();
        String method = joinPoint.getSignature().getName();
        log.info("------@Around:方法" + method + "执行时间: " + (end - start) + " ns");
        return obj;
    }
}
  • 创建启动类
@SpringBootApplication
public class AspectApplication {
    public static void main(String[] args) {
        SpringApplication.run(AspectApplication.class, args);
    }
}
  • 测试
    启动测试类,通过页面或者postman访问接口http://localhost:8080/aspect/hello,打印日志如下
    可以看到,只有/hello接口所在的方法被切面命中了,而test()方法和staticTest()方法都没有被切面命中,与其他博客中的效果不一样,其他的博客中,test()和staticTest()也被切面命中了。
2024-04-14 21:30:26.948  INFO 4172 --- [nio-8080-exec-1] c.q.t.a.config.ControllerAspect          : ------@Beforeclasscom.qqcr.train.aspectjweaver.controller.MyController,method:hello
2024-04-14 21:30:26.951  INFO 4172 --- [nio-8080-exec-1] c.q.t.a.controller.MyController          : ------hello() 开始运行---
2024-04-14 21:30:26.951  INFO 4172 --- [nio-8080-exec-1] c.q.t.a.controller.MyController          : test()方法运行了
2024-04-14 21:30:26.951  INFO 4172 --- [nio-8080-exec-1] c.q.t.a.controller.MyController          : staticTest()方法运行了
2024-04-14 21:30:26.951  INFO 4172 --- [nio-8080-exec-1] c.q.t.a.controller.MyController          : ------hello() 结束运行---
2024-04-14 21:30:26.951  INFO 4172 --- [nio-8080-exec-1] c.q.t.a.config.ControllerAspect          : ------@Around:方法hello执行时间: 3799700 ns

参考

源码详解系列(一)–cglib动态代理的使用和分析

Spring AOP就是这么简单啦

Spring AOP与AspectJ的对比及应用

Spring基础 - Spring核心之面向切面编程(AOP)

aspectjweaver和aspectjrt的区别

Spring的IOC、AOP&Spring AOP与AspectJ AOP的区别

springboot项目中引入Aspectj并使用

IDEA启动Springboot但AOP失效

  • 39
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值