1 简介

AOP,即面向切面编程是很常用的技术,特别是在Java Web开发中。而最流行的AOP框架分别是Spring AOP和AspectJ。

2 Spring AOP vs AspectJ

Spring AOP是基于Spring IoC实现的,它解决大部分常见的需求,但它并不是一个完整的AOP解决方案。对于非Spring容器管理的对象,它更没有办法了。而AspectJ旨在提供完整的AOP方案,因此也会更复杂。

2.1 织入方式

两者织入方式有极大的不同,这也是它们的本质区别,它们实现代理的方式不同。

AspectJ是在运行前织入的,分为三类:

  • 编译时织入
  • 编译后织入
  • 加载时织入

因此需要AspectJ编译器(ajc)的支持。

而Spring AOP是运行时织入的,主要使用了两种技术:JDK动态代理和CGLIB代理。对于接口使用JDK Proxy,而继承的使用CGLIB。

2.2 Joinpoints

因为织入方式的区别,两者所支持的Joinpoint也是不同的。像final的方法和静态方法,无法通过动态代理来改变,所以Spring AOP无法支持。但AspectJ是直接在运行前织入实际的代码,所以功能会强大很多。

Joinpoint Spring AOP Supported AspectJ Supported
Method Call No Yes
Method Execution Yes Yes
Constructor Call No Yes
Constructor Execution No Yes
Static initializer execution No Yes
Object initialization No Yes
Field reference No Yes
Field assignment No Yes
Handler execution No Yes
Advice execution No Yes

2.3 性能

编译织入会比较运行时织入快很多,Spring AOP是使用代理模式在运行时才创建对应的代理类,效率没有AspectJ高。

3 Spring Boot使用AspectJ

因为AspectJ比较强大,在项目中应用会更多,所以这里只介绍它与Spring Boot的集成。

3.1 引入依赖

引入以下依赖,在Spring Boot基础上加了Lombok和aspectj:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

3.2 被AOP的对象

为了验证AOP的功能,我们添加一个TestController,它有一个处理Get请求的方法,同时会调用private的成员方法和静态方法:

@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
@GetMapping("/hello")
public String hello() {
log.info("------hello() start---");
test();
staticTest();
log.info("------hello() end---");
return "Hello, pkslow.";
} private void test() {
log.info("------test() start---");
log.info("test");
log.info("------test() end---");
} private static void staticTest() {
log.info("------staticTest() start---");
log.info("staticTest");
log.info("------staticTest() end---");
}
}

3.3 配置Aspect

配置切面如下:

@Aspect
@Component
@Slf4j
//@EnableAspectJAutoProxy
public class ControllerAspect { @Pointcut("execution(* com.pkslow.springboot.controller..*.*(..))")
private void testControllerPointcut() { } @Before("testControllerPointcut()")
public void doBefore(JoinPoint joinPoint){
log.info("------pkslow aop doBefore start------");
String method = joinPoint.getSignature().getName();
String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
log.info("Method: {}.{}" ,declaringTypeName, method);
log.info("------pkslow aop doBefore end------");
} @Around("testControllerPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("------pkslow aop doAround start------");
long start = System.nanoTime();
Object obj = joinPoint.proceed();
long end = System.nanoTime();
log.info("Execution Time: " + (end - start) + " ns");
log.info("------pkslow aop doAround end------"); return obj;
}
}

@Pointcut定义哪些类和方法会被捕抓来代理,这里配置的是controller下的所有方法。

@Before@Around则定义了一些处理逻辑。@Before是打印了方法名,而@Around是做了一个计时。

注意:是不需要配置@EnableAspectJAutoProxy的。

3.4 maven插件

因为是需要编译时织入代码,所以需要maven插件的支持:https://github.com/mojohaus/aspectj-maven-plugin

配置好pom.xml文件即可。

然后执行命令打包:

mvn clean package

这时会显示一些织入信息,大致如下:

[INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))

看到以上信息,说明成功织入了代码,具体可以查看生成的class文件。

可以看到有许多代码都不是我们写的,而是织入生成。

3.5 执行及测试

编译成功后,我们就执行代码。如果是通过IDEA来执行,则在运行前不需要再build了,因为已经通过maven build过了包。通过IDEA自带的编译器build,可能无法织入。或者选择ajc作为编译器。具体请参考:IDEA启动Springboot但AOP失效

访问如下:

GET http://localhost:8080/test/hello

则日志如下,成功实现AOP功能:

3.6 一些遇到的错误

遇到错误:

ajc Syntax error, annotations are only available if source level is 1.5 or greater

需要配置插件:

<complianceLevel>${java.version}</complianceLevel>
<source>${java.version}</source>
<target>${java.version}</target>

可能还会遇到无法识别Lombok的错误,配置如下则解决该问题:

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>${java.version}</complianceLevel>
<source>${java.version}</source>
<target>${java.version}</target>
<proc>none</proc>
<showWeaveInfo>true</showWeaveInfo>
<forceAjcCompile>true</forceAjcCompile>
<sources/>
<weaveDirectories>
<weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>

4 总结

AOP场景应用特别多,还是需要掌握的。

代码请看GitHub: https://github.com/LarryDpk/pkslow-samples

Spring AOP与AspectJ的对比及应用的更多相关文章

  1. 比较 Spring AOP 与 AspectJ

    本文翻译自博客Comparing Spring AOP and AspectJ(转载:https://juejin.im/post/5a695b3cf265da3e47449471) 介绍 如今有多个 ...

  2. Spring AOP With AspectJ

    一.AOP和拦截器 某些情况下,AOP和拦截器包括Filter能够实现同样的功能,一般都是请求即controller层的操作,这三个执行顺序为Filter>Interceptor>AOP, ...

  3. Comparing Spring AOP and AspectJ

    AOP 概念 在我们开始之前 , 让我们做一个快速.高级别审查的核心术语和概念 : 方面 — —标准 / 特征代码被分散在多个场所中的应用 , 通常不同于实际的业务逻辑 (例如 , 交易管理) .各方 ...

  4. Spring AOP 和 AspectJ

    现如今有许多个可用的 AOP 库,使用这些库需要能够回答以下问题: 是否与现有的或新的应用程序兼容? 在哪里可以使用 AOP ? 如何迅速与应用程序集成? 性能开销是多少? 在本文中,我们将回答这些问 ...

  5. 比较分析 Spring AOP 和 AspectJ 之间的差别

    面向方面的编程(AOP) 是一种编程范式,旨在通过允许横切关注点的分离,提高模块化.AOP提供方面来将跨越对象关注点模块化.虽然现在可以获得许多AOP框架,但在这里我们要区分的只有两个流行的框架:Sp ...

  6. Spring aop与AspectJ的区别?

    根据我看spring官方文档的理解(不出意外是最正确的答案): ①选择spring的AOP还是AspectJ? spring确实有自己的AOP.功能已经基本够用了,除非你的要在接口上动态代理或者方法拦 ...

  7. spring aop与aspectj

    AOP:面向切面编程 简介 AOP解决的问题:将核心业务代码与外围业务(日志记录.权限校验.异常处理.事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一. AOP应用场景: 日志记录.权 ...

  8. 曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  9. Spring错误——Spring AOP——org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException

    背景:学习切面,测试前置通知功能,xml配置如下 <?xml version="1.0" encoding="UTF-8"?> <beans ...

  10. spring---aop(10)---Spring AOP中AspectJ

    写在前面 在之前的文章中有写到,Spring在配置中,会存在大量的切面配置.然而在很多情况下,SpringAOP 所提供的切面类真的不是很够用,比如想拦截制定的注解方法,我们就必须扩展DefalutP ...

随机推荐

  1. 真正“搞”懂HTTP协议03之时间穿梭

    上一篇我们简单的介绍了一下DoD模型和OSI模型,还着重的讲解了TCP的三次握手和四次挥手,让我们在空间层面,稍稍宏观的了解了HTTP所依赖的底层模型,那么这一篇,我们来追溯一下HTTP的历史,看一看 ...

  2. javax.script.ScriptException: Cannot find engine named: 'nashorn', ensure you set language field in JSR223 Test Element: JSR223 预处理程序

    jmeter运行脚本报错,跟java版本有关,做个记录. 1. 问题记录: 执行登录接口测试,登录失败.点击jmeter右上角[黄色!],查看错误日志.显示如下: 2022-09-23 10:29:5 ...

  3. 【操作说明】全能型H.265播放器如何使用?

    本播放器集成了公司业务的接口,包含了实播,回放,云台控制和回放速度控制,截图和全屏功能可以根据type直接初始化接口地址如果是第三方业务对接,也可以单独配置接口地址 正确使用H.265播放器需要按以下 ...

  4. Redis系列10:HyperLogLog实现海量数据基数统计

    Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...

  5. C#一个16进制数用二进制数表示是几位?

    1个字节是8位,二进制8位:xxxxxxxx 范围从00000000-11111111,表示0到255.一位16进制数(用二进制表示是xxxx) 最多只表示到15(即对应16进制的F),要表示到255 ...

  6. Centos 7.6 安装部署 openGauss 3.1.0 企业版一主两备集群

    一.安装环境设置 1.1 硬件环境 名称 最低配置 建议配置 测试配置 服务器数量 3 略 略 硬盘 * 至少1GB用于安装openGauss的应用程序.* 每个主机需大约300MB用于元数据存储.* ...

  7. ubuntu等debian系linux系统添加人脸识别howdy

    准备 proxychains proxychains是用来给终端走代理的软件,因为下载howdy的时候有一部分的文件的服务器在国外,且被墙了. 安装proxychains sudo apt insta ...

  8. 1 c++编程基础

    重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦! 生命就像一朵花,要拼尽全力绽放!死磕自个儿,身心愉悦! 1 c++揭开面纱 1.1 编程 ...

  9. 记一次hook mac地址实现伪装硬件码

    1. 前言 好久没写文章了,工作比较忙,不过我还是对技术比较热爱,即使它不能给我带来利益,保持初心. 工作期间遇到一个问题,连接vpn的软件是校验机器硬件码,不是公司电脑不让使用vpn软件,上下班已经 ...

  10. 如何发布一个 TypeScript 编写的 npm 包

    前言 在这篇文章中,我们将使用TypeScript和Jest从头开始构建和发布一个NPM包. 我们将初始化一个项目,设置TypeScript,用Jest编写测试,并将其发布到NPM. 项目 我们的库称 ...