Spring AOP与AspectJ的对比及应用
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的对比及应用的更多相关文章
- 比较 Spring AOP 与 AspectJ
本文翻译自博客Comparing Spring AOP and AspectJ(转载:https://juejin.im/post/5a695b3cf265da3e47449471) 介绍 如今有多个 ...
- Spring AOP With AspectJ
一.AOP和拦截器 某些情况下,AOP和拦截器包括Filter能够实现同样的功能,一般都是请求即controller层的操作,这三个执行顺序为Filter>Interceptor>AOP, ...
- Comparing Spring AOP and AspectJ
AOP 概念 在我们开始之前 , 让我们做一个快速.高级别审查的核心术语和概念 : 方面 — —标准 / 特征代码被分散在多个场所中的应用 , 通常不同于实际的业务逻辑 (例如 , 交易管理) .各方 ...
- Spring AOP 和 AspectJ
现如今有许多个可用的 AOP 库,使用这些库需要能够回答以下问题: 是否与现有的或新的应用程序兼容? 在哪里可以使用 AOP ? 如何迅速与应用程序集成? 性能开销是多少? 在本文中,我们将回答这些问 ...
- 比较分析 Spring AOP 和 AspectJ 之间的差别
面向方面的编程(AOP) 是一种编程范式,旨在通过允许横切关注点的分离,提高模块化.AOP提供方面来将跨越对象关注点模块化.虽然现在可以获得许多AOP框架,但在这里我们要区分的只有两个流行的框架:Sp ...
- Spring aop与AspectJ的区别?
根据我看spring官方文档的理解(不出意外是最正确的答案): ①选择spring的AOP还是AspectJ? spring确实有自己的AOP.功能已经基本够用了,除非你的要在接口上动态代理或者方法拦 ...
- spring aop与aspectj
AOP:面向切面编程 简介 AOP解决的问题:将核心业务代码与外围业务(日志记录.权限校验.异常处理.事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一. AOP应用场景: 日志记录.权 ...
- 曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- Spring错误——Spring AOP——org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException
背景:学习切面,测试前置通知功能,xml配置如下 <?xml version="1.0" encoding="UTF-8"?> <beans ...
- spring---aop(10)---Spring AOP中AspectJ
写在前面 在之前的文章中有写到,Spring在配置中,会存在大量的切面配置.然而在很多情况下,SpringAOP 所提供的切面类真的不是很够用,比如想拦截制定的注解方法,我们就必须扩展DefalutP ...
随机推荐
- 嵌入式-C语言基础:数组指针
定义一个数组指针,指向二维数组: int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int (*p)[4];//定义二维指针数组 p=a;//指向二维数组 ...
- 2022春每日一题:Day 18
题目:[JSOI2007]字符加密 很常见的做法,破环为链,然后以2n为总长再后缀排序,然后对于SA[i] < n 的,说明第i小后缀的编号是小于n的,也就是说,以i开头的编号是合法的,那么输出 ...
- zabbix6.0安装
一.简述 zabbix6.0 对相关软件版本要求较高,需要php7.25以上php8.0以下版本支持,若使用mysql数据库,其最低要求为mysql8.0,本此搭建采用的是使用较广的lnmp架构 za ...
- polkit(ploicykit)特权提升漏洞解决方案
一.[概述] polkit 的 pkexec 存在本地权限提升漏洞,已获得普通权限的攻击者可通过此漏洞获取root权限,漏洞利用难度低. pkexec是一个Linux下Polkit里的setuid工具 ...
- Windows之应用安装程序 —— winget
大家都用过Linux中的应用程序安装工具,如yum.apt.rpm等工具进行安装自己想要的一些工具或则软件之类的,当然Linux操作系统还是很强大的有很多类似的命令来安装我们所需要的程序,但是wind ...
- 链接脚本(Linker Scripts)语法和规则解析(自官方手册)
为了便于与英文原文对照学习与理解(部分翻译可能不准确),本文中的每个子章节标题和引用使用的都是官方手册英文原称.命令及命令行选项统一使用斜体书写.高频小节会用蓝色字体标出. 3 Linker Scri ...
- HDC.Cloud Day | 全国首场上海站告捷,聚开发者力量造梦、探梦、筑梦
摘要:11月20日,首个华为云开发者日HDC.Cloud Day在上海成功举行. 本文分享自华为云社区<HDC.Cloud Day | 全国首场上海站告捷,聚开发者力量造梦.探梦.筑梦>, ...
- layui的table数据匹配问题
<script> layui.use('table', function () { var table = layui.table; //第一个实例 table.render({ elem ...
- Mybatis-Plus通用枚举 -基于jackson(Springboot-web内置)
枚举类 使用 @EnumValue注解标识数据库字段 package com.example.enumpackage; import com.baomidou.mybatisplus.annotati ...
- QMetaObject::connectSlotsByName: No matching signal for xxx
问题描述 这个问题是没有与 xxx 这个槽函数匹配的信号,但是我做了 QMetaObject::connectSlotsByName(this);, 自动连接.并且确保了函数名和信号名是没有错误的,还 ...