- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在 Java 中使用 AspectJ 来记录对某些方法的调用。我在网上查过,但找不到答案:
当两个 @Around
通知匹配一个方法时会发生什么?
具体来说,我使用了两个@Around 建议,如下所示:
@Around("condition1() && condition2() && condition3()")
public Object around(ProceedingJoinPoint point) {
return around(point, null);
}
@Around("condition1() && condition2() && condition3() && args(request)")
public Object around(ProceedingJoinPoint point, Object request) {
...
result = (Result) point.proceed();
...
}
如果这两个建议都匹配,这是否会导致 point.proceed()
被调用两次(实际方法被调用两次)?
最佳答案
您的方法存在很大问题,因为您手动从另一个建议中调用一个建议。这不是应用 AOP 的方式。请让 AspectJ 根据它们各自的切入点来决定执行哪些建议。您将一个建议委托(delegate)给另一个建议的方式,您甚至可以调用一个本身不匹配的建议。没有 Spring 的普通 AspectJ 中的示例(但在 Spring AOP 中的工作方式相同):
Java 驱动应用程序:
package de.scrum_master.app;
public class Application {
private static void doSomething() {
System.out.println("Doing something");
}
public static void main(String[] args) {
doSomething();
}
}
方面:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyBogusAspect {
@Around("execution(* doSomething(..))")
public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println("matching advice called on joinpoint " + thisJoinPoint);
return nonMatchingAdvice(thisJoinPoint);
}
@Around("execution(* doSomethingElse(..))")
public Object nonMatchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println("non-matching advice called on joinpoint " + thisJoinPoint);
return thisJoinPoint.proceed();
}
}
控制台日志:
matching advice called on joinpoint execution(void de.scrum_master.app.Application.doSomething())
non-matching advice called on joinpoint execution(void de.scrum_master.app.Application.doSomething())
Doing something
你能看出你的方法有多不健康吗?一个不匹配的通知被匹配的通知调用。这会产生一些非常出乎意料的行为 IMO。 请不要这样做!!!
现在关于您最初关于多个匹配建议的问题,您应该这样做:
修改方面:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyBetterAspect {
@Around("execution(* doSomething(..))")
public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println(">>> matching advice on " + thisJoinPoint);
Object result = thisJoinPoint.proceed();
System.out.println("<<< matching advice on " + thisJoinPoint);
return result;
}
@Around("execution(* doSomething(..))")
public Object anotherMatchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println(">>> another matching advice on " + thisJoinPoint);
Object result = thisJoinPoint.proceed();
System.out.println("<<< another matching advice on " + thisJoinPoint);
return result;
}
}
新的控制台日志:
>>> matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> another matching advice on execution(void de.scrum_master.app.Application.doSomething())
Doing something
<<< another matching advice on execution(void de.scrum_master.app.Application.doSomething())
<<< matching advice on execution(void de.scrum_master.app.Application.doSomething())
如您所见,AspectJ 或 Spring AOP 在连接点周围包裹了多个匹配建议,例如洋葱皮,只有最里面的 proceed()
调用实际连接点,而外层调用内部连接点,确保每个连接点只执行一次。没有必要比 AOP 框架更聪明,可能会造成损害(参见我的第一个示例)。
还有一件事:如果多个切面有匹配的切入点,您可以通过 AspectJ 中的 @DeclarePrecedence
影响它们的执行顺序,但在单个切面中,您对执行顺序没有影响,或者至少没有影响你不应该依赖它。在 Spring AOP 中,您可以使用 @Order
注释来确定方面的优先级,但是对于来自同一方面的多个通知,顺序也未定义,另见 Spring manual .
2016 年 2 月 28 日,欧洲中部时间 18:30 更新,经过评论中的一些讨论:
好的,我们将驱动程序类扩展一点,以便我们可以进行更多测试:
package de.scrum_master.app;
public class Application {
private static void doSomething() {
System.out.println("Doing something");
}
private static String doSomethingElse(String text) {
System.out.println("Doing something else");
return text;
}
private static int doAnotherThing(int i, int j, int k) {
System.out.println("Doing another thing");
return (i + j) * k;
}
public static void main(String[] args) {
doSomething();
doSomethingElse("foo");
doAnotherThing(11, 22, 33);
}
}
现在,在 AspectJ 中绑定(bind)第一个参数就像 args(request, ..)
一样简单,它适用于一个或多个参数。唯一的异常(exception)是零参数,在这种情况下切入点不会触发。所以要么我最终得到类似于你所做的事情:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BoundFirstParameterAspect {
@Pointcut("execution(* do*(..))")
public static void myPointcut() {}
@Around("myPointcut()")
public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
return anotherMatchingAdvice(thisJoinPoint, null);
}
@Around("myPointcut() && args(request, ..)")
public Object anotherMatchingAdvice(ProceedingJoinPoint thisJoinPoint, Object request) {
System.out.println(">>> another matching advice on " + thisJoinPoint);
Object result = thisJoinPoint.proceed();
System.out.println("<<< another matching advice on " + thisJoinPoint);
return result;
}
}
这使得相同的建议触发两次,从而导致开销,即使原始方法只调用一次,但您可以在日志中看到开销:
>>> another matching advice on execution(void de.scrum_master.app.Application.doSomething())
Doing something
<<< another matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
Doing something else
<<< another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
<<< another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
>>> another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
Doing another thing
<<< another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
<<< another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
您可以很容易地识别出如何为每个连接点触发双重建议。
或者,您可以在运行时绑定(bind)参数,这不是很优雅并且会产生一点运行时损失,但效果很好:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BoundFirstParameterAspect {
@Pointcut("execution(* do*(..))")
public static void myPointcut() {}
@Around("myPointcut()")
public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
System.out.println(">>> matching advice on " + thisJoinPoint);
Object[] args = thisJoinPoint.getArgs();
Object request = args.length > 0 ? args[0] : null;
System.out.println("First parameter = " + request);
Object result = thisJoinPoint.proceed();
System.out.println("<<< matching advice on " + thisJoinPoint);
return result;
}
}
这避免了双重建议执行以及代码重复,并产生以下控制台输出:
>>> matching advice on execution(void de.scrum_master.app.Application.doSomething())
First parameter = null
Doing something
<<< matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
First parameter = foo
Doing something else
<<< matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
First parameter = 11
Doing another thing
<<< matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
最后但并非最不重要的一点是,您可以有两个略有不同的切入点 - 一个带有空 args()
一个带有 args(request, ..)
- 两者都有正如我在其中一条评论中所说,它可以将参数处理、日志记录和异常处理委托(delegate)给辅助方法以避免重复:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BoundFirstParameterAspect {
@Pointcut("execution(* do*(..))")
public static void myPointcut() {}
@Around("myPointcut() && args()")
public Object myAdvice(ProceedingJoinPoint thisJoinPoint) {
return myAdviceHelper(thisJoinPoint, null);
}
@Around("myPointcut() && args(request, ..)")
public Object myAdviceWithParams(ProceedingJoinPoint thisJoinPoint, Object request) {
return myAdviceHelper(thisJoinPoint, request);
}
private Object myAdviceHelper(ProceedingJoinPoint thisJoinPoint, Object request) {
System.out.println(">>> matching advice on " + thisJoinPoint);
System.out.println("First parameter = " + request);
Object result = thisJoinPoint.proceed();
System.out.println("<<< matching advice on " + thisJoinPoint);
return result;
}
}
控制台日志应该和之前的完全一样。
更新 2:
好吧,我刚刚意识到空的 args()
技巧也适用于您的原始想法并避免双重执行以及辅助方法:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class BoundFirstParameterAspect {
@Pointcut("execution(* do*(..))")
public static void myPointcut() {}
@Around("myPointcut() && args()")
public Object myAdvice(ProceedingJoinPoint thisJoinPoint) {
return myAdviceWithParams(thisJoinPoint, null);
}
@Around("myPointcut() && args(request, ..)")
public Object myAdviceWithParams(ProceedingJoinPoint thisJoinPoint, Object request) {
System.out.println(">>> matching advice on " + thisJoinPoint);
System.out.println("First parameter = " + request);
Object result = thisJoinPoint.proceed();
System.out.println("<<< matching advice on " + thisJoinPoint);
return result;
}
}
这是可以接受的,也很优雅,因为它不会为每个连接点生成两次字节码。这两个切入点是互斥的,所以这是一件好事。我推荐这个解决方案。
关于java - AspectJ 处理多个匹配建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35599765/
我是一个相对较新的程序员; CS 学士学位,大学毕业大约 2 年,主要使用 C# 中的 .NET。我对 SQL 交互/脚本编写相当流利,并且对 ASP.NET 做了一些工作(主要是维护现有站点)。 我
我计划开发一个简单的解决方案,使我能够即时执行非常基本的视频流分析。我以前从未做过类似的事情,因此这是一个非常笼统和开放的问题。主要重点是检查流是否正常运行,例如 - 卡住帧、黑屏以及音频是否存在。同
我正在考虑重组一个大型 Maven 项目...... 我们当前结构的基本概述: build [MVN plugins, third party dependency management]:5.1
我需要有关附加查询的建议。该查询执行了一个多小时,并根据解释计划进行了全表扫描。我对查询调优还很陌生,希望得到一些建议。 首先,为什么我要进行全表扫描,即使我使用的所有列都在其上创建了索引。 其次,有
我正在做一个项目,我需要在 4 个模型之间创建三个多对多关系。这是它的过程: 常见问题类别可以有许多常见问题子类别,反之亦然。 常见问题组可以有许多常见问题的子类别,反之亦然。 常见问题可以有许多常见
对于代码大小比语音质量更重要的 PIC 和/或 ARM 嵌入式系统,是否有任何易于使用的免费或廉价的语音合成库?现在似乎 1 meg 的封装被认为是“紧凑的”,但很多微 Controller 都比它小
我们正在使用 Solr 建议器功能进行 businessName 查找。当用户输入查询以及匹配的名称时,我们希望 solr 发送来自个人资料的其他属性,如 id、地址、城市、州、国家等字段。 我尝试使
我正在构建一个用户界面。我的计划将包括 4 个主要部分: 1) 顶部菜单 - TMainMenu。一个窗口的顶部 2) 主菜单 - TTreeView。一个窗口的左边。 TreeView的每一项=对应
我的公司需要一个任务管理系统来处理从“为X购买一台计算机”到“将一个人转移到另一个国家”这样简单的场景。简单的场景是由一个人处理的单个任务,而更大的任务可以分解为在工作流程中委派给多个人的多个子任务。
MarkLogic 服务器的林大小与实际内存的建议比率是多少?例如,我目前有一个 190GB 的数据库,并且该数据库随着时间的推移而不断增长。由于数据库会不断增长,我最终需要对该数据库进行集群。因此,
去年我收到了一个礼物,它是一个索尼 CMT700Ni 音频站,支持 wifi。它还具有类似于广播的功能,称为“PartyStreaming”。我目前正在挖掘内部,探索它,所以也许我可以结束拥有自己的“
有没有我可以阅读的研究论文/书籍可以告诉我针对手头的问题哪种特征选择算法最有效。 我试图简单地将 Twitter 消息识别为 pos/neg(首先)。我从基于频率的特征选择开始(从 NLTK 书开始)
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
我正在浏览 stackoverflow 以查找有关使用 jUnit 进行测试的常见建议,但仍然有几个问题。我知道,如果要测试的方法很复杂,最好的方法是将其分成小的单独部分并测试每个部分。但问题是 -
我有一个方法如下 public List> categorize(List customClass){ List> returnValue = new ArrayList<>();
我的问题是,当按照下面的程序合并时,在最佳实践场景中,“将分支折叠回主干”程序的最后一步是正确的方法吗? 我已经使用 svn 很多年了。在我的个人项目中,我总是毫不犹豫地在主干上愉快地进行修改,并且在
我读过 UINavigationController当您想从 n 个屏幕跳转到第一个屏幕时,这是最佳选择。这样做需要以下代码: NSMutableArray *array=[[NSMutableArr
我有一个文件输入类。它在构造函数中有一个字符串参数来加载提供的文件名。但是,如果文件不存在,它就会退出。如果文件不存在,我希望它输出一条消息 - 但不确定如何...... 这是类(class): pu
我希望创建一个“您访问过的国家/地区” map - 就像您可能在 Facebook、TravelAdvisor 和诸如此类的网站上看到的那样。 我尝试过不同的闪光灯套件,但它们并不像我希望的那样先进。
我需要一些关于如何处理我想用 Perl 编写的脚本的建议。基本上我有一个看起来像这样的文件: id: 1 Relationship: "" name: shelby pet: 1
我是一名优秀的程序员,十分优秀!