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文件中引入其他依赖。

org.springframework.boot

spring-boot-starter-parent

2.1.4.RELEASE

org.springframework.boot

spring-boot-starter-web

org.aspectj

aspectjweaver

1.8.14

org.projectlombok

lombok

1.16.20

provided

创建需要被切面切中的类

创建切面

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 : ------@Before:class:com.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失效

友情链接