我的情况如下:我有一个loggingapect,其中有几个切入点与我的主应用程序中的特定方法执行相匹配。相应的建议机构基本上看起来都很相似,导致大量代码重复:
void around() : download() {
String message = "Downloading, verifying (MD5) and unpacking";
SimpleLogger.verbose(message, IndentMode.INDENT_AFTER);
proceed();
SimpleLogger.verbose(message + " - done", IndentMode.DEDENT_BEFORE);
}
不过也有一些变化。有时是切入点
void around(BasicFilter filter) : fixFaultyLinkTargets() && this(filter) {
String message = "TOC file: checking for faulty link targets";
SimpleLogger.verbose(message, IndentMode.INDENT_AFTER);
proceed(filter);
SimpleLogger.dedent();
}
经常发生的事情是我手动告诉记录器
我的想法是,我想用一个切入点编写一个元方面(或称其为帮助方面),该切入点拦截LoggingAspect
中的进行()
调用,以便相应地自动调整缩进级别。但是似乎没有切入点匹配进行()
。我尝试过调用(某些方法InMyMainApp)
,甚至一个切入点匹配日志方面的所有内容,但是切入点匹配任何我不需要的东西,但从来没有继续。
如果有人知道我该如何做到这一点,我将非常感谢您的提示或代码片段。
实现这一点的间接方法可能不是拦截通知本身,而是通过创建以下额外切入点来拦截这些通知所建议的方法调用(或执行):
// ATTENTION: each new pointcut must also be added here
pointcut catchAll() : download() || fixFaultyLinkTargets() || ...;
void around() : catchAll() {
SimpleLogger.indent();
proceed();
SimpleLogger.dedent();
}
不过,我更喜欢另一种方式,而不必每次在日志方面更改某些内容时都记得更新额外的catchAll()
切入点。
请注意:我将在这里回答我自己的问题,在loddar2012建议的解决方案中添加更多信息和参数化的附加功能。因为他的回答把我引向了正确的方向,所以我将接受这个答案,尽管这个答案确实解决了我从原始问题中的所有需求,例如(引用我自己的话):
不过也有一些变化。有时是切入点
我们在这里处理的基本问题是Ramnivas Laddad在其著作AspectJ in Action中所称的worker对象模式。他的(和loddar2012的)想法是,用平淡的散文
如果需要异步执行调用,一个优雅的解决方案是创建匿名可运行类的实例。不过,我们将使用我们自己的抽象基类LogHelper,因为我们想要在我们的tea中添加更多的糖,特别是将日志消息和一些影响日志输出的其他参数传递给每个worker的选项。这就是我所做的(示例代码中未显示包名和导入):
抽象工作者基类:
abstract class LogHelper {
// Object state needed for logging
String message;
boolean logDone;
boolean indent;
LogType type;
// Main constructor
LogHelper(String message, boolean logDone, boolean indent, LogType type) {
this.message = message;
this.logDone = logDone;
this.indent = indent;
this.type = type;
}
// Convenience constructors for frequent use cases
LogHelper(String message, boolean logDone) {
this(message, logDone, true, LogType.VERBOSE);
}
LogHelper(String message) {
this(message, true);
}
// Worker method to be overridden by each anonymous subclass
abstract void log();
}
记录捕获工作对象执行的建议:
aspect LoggingAspect
{
void around(LogHelper logHelper) :
execution(* LogHelper.log()) && this(logHelper)
{
try {
SimpleLogger.log(logHelper.type, logHelper.message);
if (logHelper.indent)
SimpleLogger.indent();
proceed(logHelper);
} finally {
if (logHelper.indent)
SimpleLogger.dedent();
if (logHelper.logDone)
SimpleLogger.log(logHelper.type, logHelper.message + " - done");
}
}
// (...)
}
如您所见,日志记录建议在调用containe(logHelper)之前做了一些事情(即执行worker对象的log()
方法),然后使用存储在worker对象中的状态信息做了一些事情,例如
因为在我的用例中,所有记录的方法都返回void,所以不需要实现返回值传递,但如果必要的话,这很容易实现。然后,通知的返回值将只是对象,我们将把继续()的结果传递给调用方,这没什么大不了的。
一些建议捕获要记录的连接点并利用参数化的工作对象来完成工作:
aspect LoggingAspect
{
// (...)
pointcut processBook() : execution(* OpenbookCleaner.downloadAndCleanBook(Book));
pointcut download() : execution(* Downloader.download());
pointcut cleanBook() : execution(* OpenbookCleaner.cleanBook(Book));
pointcut cleanChapter() : execution(* OpenbookCleaner.cleanChapter(Book, File));
pointcut initialiseTitle() : execution(* *Filter.initialiseTitle(boolean));
void around(final Book book) : processBook() && args(book) {
new LogHelper("Book: " + book.unpackDirectory) {
void log() { proceed(book); } }.log();
}
void around() : download() {
new LogHelper("Downloading, verifying (MD5) and unpacking") {
void log() { proceed(); } }.log();
}
void around() : cleanBook() {
new LogHelper("Filtering") {
void log() { proceed(); } }.log();
}
void around(final File origFile) : cleanChapter() && args(*, origFile) {
new LogHelper("Chapter: " + origFile.getName()) {
void log() { proceed(origFile); } }.log();
}
void around() : initialiseTitle() {
new LogHelper("Initialising page title", false) {
void log() { proceed(); } }.log();
}
}
这些示例展示了如何
建议将procedue()封装在匿名类中。编写一个方面来处理这个执行(但不要忘记procedure()的潜在异常)。
我的建议:
// AspectProceedCaller.java
public abstract class AspectProceedCaller {
public abstract Object doProceed();
};
// aspect ProceedCallerAspect.aj
aspect ProceedCallerAspect {
pointcut execProceedCaller() : execution( * AspectProceedCaller+.doProceed() );
Object around() : execProceedCaller() {
try {
SimpleLogger.indent();
return proceed();
}
finally {
SimpleLogger.dedent();
}
}
};
// Your application aspect
aspect AnyAspect {
pointcut anyPointcut() : ...;
Object around() : anyPointcut() {
AspectProceedCaller apc=new AspectProceedCaller() {
public Object doProceed() {
return proceed();
}
};
// DO Stuff before ....
Object retval = apc.doProceed();
// ... and after calling proceed.
return retval;
}
};
问候马尔科
有多篇关于如何在Spring AOP中拦截内部方法调用的帖子。但是找不到任何与使用AspectJ排除内部方法相关的帖子。我们希望使用AspectJ编译时编织来实现它promise的运行时性能改进。 如果另一个类的方法调用了下面类TestService中的任何公共方法,则应该拦截该调用。但是,不应该截取从method1()到method2()的内部调用。我们只希望拦截器对每个对象只拦截一次。 一个示
使用bytepal拦截类Foo,有两种方法A、B 在A方法中,B方法被调用。如果我们在intercetpor类栏中同时删除A和B到C方法,它们在第一行调用 会发生什么?这些方法的执行顺序是什么?
我正在尝试使用来做同样的事情,现在我使用哪个注释来捕获相关的对象,切入点表达式应该是什么?我尝试了,但这是一个void方法,如何捕获该方法的参数?我是AOP的初学者,所以如果这个问题太琐碎,请原谅。 解决方案:使用spring AOP获取方法参数?
我试图让aspectj拦截带注释的方法: 我删除了!为了简洁起见,在(InterceptMeAspect)内,但它并没有拦截太多。如果我删除注释约束(在(@InterceptMe*)内),它可以工作,但会拦截所有内容,这会造成一个大问题。 输出字节码似乎有完整的注释,所以我希望注释标准匹配。我正在或试图进行编译时编织。这很重要,因为我有另一个方面确实使用上面相同的方法工作。我怀疑该方面正在搞乱这个
我正在使用AeyJ拦截一个名为的方法。为此,我使用了我自己指定的(标记)注释。这就是类的样子: 截取截取注释的方面: 然而,我的方面是基于带注释的参数进行拦截。但我希望方面能够拦截参数t包含的特定值的方法请求。 例如,如果t==“t1”,则必须截取该方法,否则不能截取。 我想知道是否可以在AeyJ(与Spring AOP结合使用)中做到这一点。