我有一个情况如下:我有一个LoggingAspect
几个切入点匹配我的主要应用特定的方法执行。 相应的咨询机构基本上看起来都差不多,造成大量重复的代码:
void around() : download() {
String message = "Downloading, verifying (MD5) and unpacking";
SimpleLogger.verbose(message, IndentMode.INDENT_AFTER);
proceed();
SimpleLogger.verbose(message + " - done", IndentMode.DEDENT_BEFORE);
}
有一些变化,虽然。 有时切入点&建议有一个arg
或this
参数也被输出到日志。 有时,“完成”,如果它只是不换其他很多呼叫的电话未成年人,像这样不打印消息:
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();
}
恒定的事情是,我手动告诉记录器
- 以增加被印刷在第一消息之后缩进层次,即,直接前
proceed()
被调用,并且 - 降低缩进级别被印刷的最后消息之前(如果有的话被印刷),即后直接
proceed()
返回。
我的想法是,我还想写一元方面(或者称之为帮手方面)与拦截切入点proceed()
的调用LoggingAspect
,从而自动调整相应的缩进级别。 但是,似乎没有切入点的匹配proceed()
我曾尝试call(SomeMethodInMyMainApp)
甚至一个切入点匹配的记录方面的一切,但切入点匹配什么我并不需要,但从来没有在继续。
如果有人知道我怎么能做到这一点,我将不胜感激的提示或代码段。
这样做的一种间接的方式可能是拦截不建议本身,而是这些意见建议的方法调用(或执行)通过创建一个额外的切入点是这样的:
// ATTENTION: each new pointcut must also be added here
pointcut catchAll() : download() || fixFaultyLinkTargets() || ...;
void around() : catchAll() {
SimpleLogger.indent();
proceed();
SimpleLogger.dedent();
}
我宁愿另一种方式,虽然,有没有我记得更新额外catchAll()
切入点,每次我更改日志方面的东西。
建议包装在一个匿名类中进行()。 而写这本ADRESS执行方面(但不要忘了继续的潜在的异常())。
我的建议:
// 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;
}
};
最好的问候马尔科
请注意:我要在这里回答我的问题,增加了更多的信息和参数化的由loddar2012建议的解决方案的附加功能。 因为他的回答使我对正确的方向,我会接受它,即使在这里这个答案真的解决了从原来的问题我所有的需求,如(引用自己):
有一些变化,虽然。 有时切入点&建议具有Arg或该参数也被输出到日志。 有时,如果它只是不换其他很多呼叫的通话轻微不打印“完成”的消息
我们在这里处理的基本的东西是什么拉姆尼瓦斯·拉达称在他的书中工人对象模式 在行动的AspectJ 。 他(和loddar2012的)想法是,以纯散文
- 包装一个呼叫到一个匿名类的实例(工作者对象),其中
- 基类或实现接口提供意图做的工作的方法,
- 工人对象提供了一个具体实现的辅助方法的具体要求
proceed()
在其中, - 工人方法能够创建对象后立即被调用(我们将在这里做的)或更高版本,甚至在自己的线程,
- 工人对象可通过周围或添加到调度队列(其中没有我们需要在这里)。
如果你需要执行你的优雅的解决方案proceed()
调用异步将创建匿名的情况下Runnable
的类。 我们将用我们自己的抽象基类LogHelper
,不过,因为我们希望在我们的茶叶一些更多的糖,特别是选择通过日志消息,并影响日志输出到每个工人的一些其他参数。 所以这是我做了什么(包名和进口的代码示例未显示):
摘要工人的基类:
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");
}
}
// (...)
}
正如你所看到的,记录咨询打电话之前做一些事情proceed(logHelper)
即执行中的工人对象的log()
方法)之后,有些东西,使用存储在工人对象内部的状态信息,如
- 要记录的消息,
- 日志级别(这里所说的“类型”),
- 标志规定,如果缩进水平应在继续前提出,
- 标记,用于规定当“完成”消息应工人执行之后被打印。
因为在我的使用情况下,所有登录方法返回void
,没有必要实施返回值的传球,但如果需要的话,这将是容易实现。 该建议的返回值会就做Object
,我们将通过的结果proceed()
回到我们的调用,没什么大不了的。
有些意见捕捉joinpoints将被记录并利用parametrised工人对象来完成工作:
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();
}
}
这些例子告诉你如何能
- 实例化一个匿名
LogHelper
作为与一种或多种构造参数的工人对象,设置其状态 - 实现
log()
方法,任选地使用经由结合连接点状态this()
或args()
- 调用/运行辅助对象(通话将被记录意见的切入点拦截和真实记录企业在那里进行)。