级别: 中级 Helen Hawkins (hawkinsh@uk.ibm.com), 软件工程师, IBM Sian January (sjanuary@uk.ibm.com), 软件工程师, IBM
2006 年 8 月 24 日 面向方面的编程(AOP)是一个功能强大并且发展迅速的技术,它促使横切关注点完全模型化,例如错误处理,日志,监控,调试支持,确认等等。模型驱动开发的方面(Aspects for MDD)第一次在IBM® Rational® Software Architect(RSA)的插件中一起带来了AOP和MDD。这篇文章介绍了Aspects for MDD插件,并且向你展示了如何使用它在MDD应用程序中追踪与首次失败数据捕获(First Failure Data Capture,FFDC)。我们还将探究方面的概念,并且研究它们是如何工作的。
介绍
Aspects for MDD是一个框架,它使得建模和开发人员可以直接在IBM® Rational®Software Architect(下文称为 RSA)中应用AspectJ代码,而不再需要学习任何关于方面的技术知识。
 |
追踪和日志有何区别?
日志是在事件发生时为其建立一个记录。追踪是一类特殊的日志,它按照方法入口和出口产生的顺序记录它们。 |
|
这篇文章介绍了Aspects for MDD的com.ibm.aspectj.lib库中的两个方面,追踪与首次失败数据捕获。追踪使用的是Java1.4或者更高版本的Java™ Developer Kit (JDK™)中的日志工具。不论是最初的开发人员,或者是服务工程师,再或者是最终用户,追踪通常都是用在问题诊断中。追踪的结果会输出到一个文本文件或者终端窗口上,其中会列出程序中运行的入口和出口方法。通过阅读追踪信息的文件,可以缩小运行程序代码的出错范围。我们版本的追踪是可配置的,并且可以把追踪结果输出到终端、文本文件,或者两者。
首次错误数据捕获(FFDC)也是用来做问题诊断的。当你的应用程序发生一个错误或者抛出一个异常时,FFDC可以确保这个错误或者异常被正确的处理,并且任何和这个错误相关的信息都将会被保存下来。在一个应用程序中使用FFDC可以减少我们诊断和修复一个问题的时间。除此之外,使用一个方面实现FFDC可以确保没有错误或者异常未被发现,如果手动诊断的话这个问题常常发生。
阅读这篇文章和开始使用Aspects for MDD是不需要AOP相关知识的。当然,一些基本的知识能够帮助你更好的阅读这篇文章的表面之下一节。虽然使用Software Architect的经验不是必须的,但是它可以帮助你更好的理解这篇文章。
在阅读这篇文章之后,你应该对追踪和FFDC适用的环境有个很好的理解。你需要安装 Aspects for MDD,然后应用追踪和FFDC到你的应用程序。你应该对方面是如何工作的有一些理解。
安装
在进行这篇文章之前,你应该已经安装好了Rational Software Architect v6.0.1,并且还需要做以下准备工作:
- 安装AspectJ Development Tools (AJDT) for Eclipse 3.0
- 安装Aspects for MDD Framework
- 建立一个样例模型
介绍如何安装AJDT for Eclipse 3.0的文章可以在AJDT主页上找到,下面是主页的链接(查看参考资源)。查看下载Aspects for MDD Framework for Rational Architect的参考资源。
你将要用到的样例模型是一个简单的HelloWorld程序,和所有的HelloWorld程序一样,这个程序是把"Hello World"输出到屏幕。由于这篇文章将告诉你如何应用FFDC方面后,我们可以使用一个方法,它的作用是抛出RuntimeException异常并且在屏幕上打印"Goodbye World"。
建立这个简单的样例模型:
- 建立一个新的AspectJ Project,叫做aj_project(File > New > Other > AspectJ > AspectJ Project)
- 在这个新的项目中,建立一个新的UML模型。右键单击aj_project项目并选择New > UML Model。
- 将名字改为SimpleModel其他属性保持默认,然后点击Finish。
- 在这个模型中,通过右键点击模型并选择Add UML > Class,来建立一个新的HelloWorld类。
- 在UML类中,通过右键点击HelloWorld图表然后选择New UML > operation来建立4个操作,它们是main,sayHello,sayGoodbye和throwingMethod。
- 在Properties视图中改变刚建立的操作的属性,确保main是静态的,sayHello和sayGoodbye返回字符串。
- 通过右键点击刚在模型中建立的类,并选择Transform > UML to Java来把UML转变成Java程序。
完成这些操作后,打开编辑器中的HelloWorld类,把列表1中的代码输入进去。
列表1:HelloWorld类
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Starting");
HelloWorld main = new HelloWorld();
System.out.println( main.sayHello() );
System.out.println( main.sayGoodbye() );
System.out.println( "Finishing" );
}
public String sayHello() {
return "Hello World";
}
public String sayGoodbye() {
try {
throwingMethod();
} catch (Exception e) {
}
return "Goodbye World";
}
public void throwingMethod() {
throw new RuntimeException();
}
}
|
在进行后面的工作前,请先按照下列步骤运行这段程序:
- 选择Run > Run
- 在左边的菜单中,选择Java Application,然后点击New
- 在右边的Main标签上,选择HelloWorld
- 选择OK。
结果被输出到终端上,如列表2所示:
列表2:HelloWorld应用程序的输出结果
Starting
Hello World
Goodbye World
Finishing
|
现在你已经做好了应用追踪和FFDC方面的准备。
使用追踪方面
在这个章节中,你将会学到如何把追踪应用到你的程序中。首先你将会把它应用到安装章节中开发的简单的模型中。稍后这篇文章将会讨论在应用程序中应用追踪的不同的可能性。最后,我们将会在表面之下讨论追踪方面以及它是如何工作的。
把追踪应用到样例模型中
使用在安装章节讨论的样例测试模型,下面你将会把java.util.logging应用到sayHello()方法中。请跟着下面的步骤操作:
- 在图表中,右键单击
sayHello()。
- 从中选择Aspect Lib。
- 在Aspect Specification对话框中,从AspectJ Library Aspects种类中选择JDK14Tracing。
- 选择OK。
你会发现在操作旁边有一个<<jdk14tracing>>,如图1所示:
图1:sayHello模型元素中的jdk14tracing
- 现在,通过右键点击图表中的Transform > Run Transformation > UML to Java,将UML转换成Java程序。
如果你在查看Package Explorer(如果没有显示这项,请选择Window > Show View > Package Explorer),你会发现一个叫做HelloWorldJDK14Tracing.aj的文件被建立了,如图2所示:
图2:HelloWorldJDK14Tracing.aj文件
稍后你将会更加详细的查看这个文件。
为了运行HelloWorld这个程序,java.util.logging需要logging.properties文件的建立(关于java.util.logging的更多资料请查看参考资源)。请参考以下步骤建立logging.properties配置文件:
- 在包浏览器中选择项目。
- 选择File > New > File。
- 输入logging.properties作为名称。
- 选择Finish。
- 打开文件,粘贴列表3种的内容到这个文件中。
- 保存文件。
列表3:logging.properties文件
############################################################
# Logging Configuration File
############################################################
# Global properties
handlers= java.util.logging.ConsoleHandler
.level= INFO
# Handler specific properties.
java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter =
java.util.logging.SimpleFormatter
|
正如先前所提到的,我们可以把追踪结果输出到一个文本文件或者在终端显示。下一个章节如何在你的应用程序中应用追踪,将会告诉你如何通过改变logging.properties文件来达到此目的。
首先运行样例程序。如下步骤所示:
- 通过选择Run > Run打开你在安装章节最后的设置的运行配置。
- 找到Arguments标签,在VM Arguments文本框中输入
- Djava.util.logging.config.file=logging.properties。
- 选择OK。
列表4中的输出结果被显示到了终端:
列表4:应用追踪方面后的应用程序输出
Starting
? initLogger() logger=HelloWorld
? initLogger() traceEnabled=true
21-Oct-2005 16:28:57 HelloWorld sayHello
FINER: ENTRY
Hello World
Goodbye World
Finishing
21-Oct-2005 16:28:57 HelloWorld sayHello
FINER: RETURN Hello World
|
如何在你的应用程序中应用追踪
在介绍部分已经提到,追踪主要是用来做问题诊断。当你这么考虑问题时,追踪什么就会有很多不同的选择。首先,如果应用程序很小,你可能想要追踪所有的东西。这样可以帮助你在代码的未预料领域发现问题。但是,随着应用程序的扩大,这种完整的追踪方式的有效性降低了,因为它使得很多有用信息变得模糊。在大的应用程序中,你或许只想追踪有可能会发现问题的领域,或者除此之外你还想追踪你的API类。这就意味着如果一个问题发生在了一个客户端程序,那么你必须去确定谁的代码发生了错误。如果这个错误发生在你的代码中,那么查看API的哪个部分正在被使用,并且被怎样使用就是很有帮助的了。
在决定把追踪应用到你的应用程序之后,你或许想要配置一些你的追踪的细节信息。这是通过配置logging.properties文件完成的。
原始的logging.properties文件如之前的列表3所示。如果想要把追踪结果显示在终端同时输出到一个文本文件,那么就需要把配置文件
handlers= java.util.logging.ConsoleHandler
改成
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
。如果只是把结果输出到一个文本文件中,那么就把它改成
handlers= java.util.logging.FileHandler
。稍后你需要为文件管理添加一些配置设置:
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
更新后的logging.properties文件,如列表5所示:
列表5:更新后的logging.properties文件
############################################################
# Logging Configuration File
############################################################
# Global properties
handlers = java.util.logging.FileHandler,
java.util.logging.ConsoleHandler
.level = INFO
# Handler specific properties.
java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter =
java.util.logging.SimpleFormatter
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter =
java.util.logging.XMLFormatter
|
你或许还想为终端,文件或者个人的类配置日志级别。你选择的日志级别决定了在追踪中包括哪个事件。可用的级别有:
- SEVERE
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
SEVERE级别将会给你最少的信息,FINEST级别是最多的信息。除此之外,你还可以指定OFF来中断追踪或者选择ALL来包括所有事件。使用下列信息来指定级别:
-
Overall:
.level= ...
-
The console:
java.util.logging.ConsoleHandler.level = ...
-
The file:
java.util.logging.FileHandler.level = ...
-
An individual class (for example, HelloWorld):
HelloWorld.level = ...
注意:在你编写阶段,所有的输出都是默认的FINER级别。
想要了解更多的关于Java日志和如何配置日志的信息,请查看参考资源章节。
表面之下
 |
AOP术语
-
Join Point: Join Point是一个程序运行时发生的事件,例如执行一个方法或者处理一个异常。
-
Pointcut: 一个Pointcut描述了一系列的Join Points。
-
Advice: Advice是和Pointcut联系在一起的。运行在Join Point上的通过Pointcut进行匹配的额外逻辑,例如追踪一个方法的异常。
-
Aspect: Aspect是一个包含Pointcut和Advice的编程结构。
想要了解更多关于AOP的信息,请访问参考资源章节。
|
|
Com.ibm.aspectj.lib方面库提供了一个包含抽象追踪方面的MDD方面,JDK14Tracing。
这个方面以及它的根源,追踪,包含所有的追踪执行逻辑,但是却没有包含应该追踪什么的信息。一个子方面需要提供这种信息,也就是pointcut的形式。如果你已经在部分UML模型中应用了一个方面,那么一个AspectJ方面将会作为UML转换成Java代码过程的一部分,建立在一个.aj文件中。在先前的例子中建立这个方面的过程如列表6所示:这是抽象的JDK14Tracing方面的一个具体的类型。如列表7所示:
请注意,这里我们为了使例子更加简明,所以省掉了导入语句,包的声明,版权声明,以及一些比较长的代码列表的部分内容。完整的代码在com.ibm.aspectj.lib库文献的aspectjLibsrc.zip文件中。
列表6:HelloWorldJDK14Tracing.aj
public aspect HelloWorldJDK14Tracing extends
org.aspectj.lib.tracing.JDK14Tracing {
protected pointcut tracingScope () :
execution( * HelloWorld.sayHello() )
|| staticinitialization(HelloWorld);
} |
列表7:JDK14Tracing.aj
public abstract aspect JDK14Tracing extends Tracing {
/*
* Sub-aspects implement to determine what to trace
*/
protected abstract pointcut tracingScope ();
/*
* Use if() pointcut to efficiently determine
* when to trace
*/
protected pointcut shouldTrace () :
if(tracingEnabled) && tracingScope();
private static boolean tracingEnabled = false;
/*
* Ensure logger is initialized for each class we are tracing
*/
before(): staticinitialization(!Tracing+) && tracingScope() {
initLogger(thisJoinPointStaticPart);
}
public final static Level ENABLED = Level.FINER;
public final static Level DISABLED = Level.OFF;
protected String getLoggerName (JoinPoint.StaticPart sjp) {
return sjp.getSignature().getDeclaringTypeName();
}
protected void initLogger (JoinPoint.StaticPart sjp) {
Logger logger = Logger.getLogger(getLoggerName(sjp));
System.err.println("? initLogger() logger=" +
logger.getName());
if (!tracingEnabled) {
tracingEnabled = isTracingEnabled(logger);
}
System.err.println("? initLogger() traceEnabled=" +
tracingEnabled);
}
/*
* From the Javadoc for Level: "By default logging calls for
* entering, returning, or throwing an exception are traced
* at this level."
*/
private static boolean isTracingEnabled (Logger logger) {
return logger.isLoggable(ENABLED);
}
protected void enter (JoinPoint jp) {
Logger logger = Logger.getLogger(getLoggerName(
jp.getStaticPart()));
if (isTracingEnabled(logger)) {
CodeSignature signature =
(CodeSignature)jp.getSignature();
logger.entering(signature.getDeclaringTypeName(),
signature.getName(),jp.getArgs());
}
}
protected void enter (JoinPoint jp, Object obj) {
Logger logger = Logger.getLogger(getLoggerName(
jp.getStaticPart()));
if (isTracingEnabled(logger)) {
CodeSignature signature =
(CodeSignature)jp.getSignature();
logger.entering(signature.getDeclaringTypeName(),
signature.getName(),jp.getArgs());
}
}
protected void exit (JoinPoint.StaticPart sjp) {
Logger logger = Logger.getLogger(getLoggerName(sjp));
if (isTracingEnabled(logger)) {
CodeSignature signature =
(CodeSignature)sjp.getSignature();
logger.exiting(signature.getDeclaringTypeName(),
signature.getName());
}
}
protected void exit (JoinPoint.StaticPart sjp, Object ret) {
Logger logger = Logger.getLogger(getLoggerName(sjp));
if (isTracingEnabled(logger)) {
CodeSignature signature =
(CodeSignature)sjp.getSignature();
logger.exiting(signature.getDeclaringTypeName(),
signature.getName(),ret);
}
}
protected void exception (JoinPoint.StaticPart sjp,
Exception ex) {
Logger logger = Logger.getLogger(getLoggerName(sjp));
if (isTracingEnabled(logger)) {
Signature signature = sjp.getSignature();
logger.exiting(signature.getDeclaringTypeName(),
signature.getName(),ex);
}
}
}
|
JDK14Tracing继承另一个追踪方面,如列表8所示:
Listing 8. Tracing.aj
public abstract aspect Tracing {
/*
* Sub-aspects implement to determine what and when to trace
*/
protected abstract pointcut shouldTrace ();
private pointcut staticContext () : !this(Object);
private pointcut nonStaticContext (Object obj) : this(obj);
private pointcut voidMethod () : execution(void *(..));
private pointcut objectMethod () : execution(* Object.*(..));
/*
* Ensure we don't trace ourselves or any other sub-aspects
*/
private pointcut excluded () :
objectMethod()
|| within(Tracing+);
private pointcut tracedMethod () :
execution(public * *(..))
&& !excluded();
private pointcut tracedConstructor (Object obj) :
execution(new(..))
&& this(obj)
&& !excluded()
;
before (Object obj) : tracedMethod() && nonStaticContext(obj)
&& shouldTrace() {
enter(thisJoinPoint,obj);
}
before () : tracedMethod() && staticContext() && shouldTrace() {
enter(thisJoinPoint);
}
after() returning() : tracedMethod()&& voidMethod()
&& shouldTrace() {
exit(thisJoinPointStaticPart);
}
after() returning(Object ret) : tracedMethod() && !voidMethod()
&& shouldTrace() {
exit(thisJoinPointStaticPart,ret);
}
after() throwing(Exception ex) : tracedMethod()
&& shouldTrace() {
exception(thisJoinPointStaticPart,ex);
}
before () : tracedConstructor(Object) && shouldTrace() {
enter(thisJoinPoint);
}
after (Object obj) : tracedConstructor(obj) && shouldTrace() {
exit(thisJoinPointStaticPart,obj);
}
/*
* Template methods implemented by infrastructure-specific
* sub-aspects
* e.g. Log4j to log data
*/
protected abstract void enter (JoinPoint jp, Object obj);
protected abstract void enter (JoinPoint jp);
protected abstract void exit (JoinPoint.StaticPart sjp);
protected abstract void exit (JoinPoint.StaticPart sjp,
Object ret);
protected abstract void exception (JoinPoint.StaticPart sjp,
Exception ex);
...
} |
列表8中的追踪方面是一个通用的追踪方面,它提供了一个追踪的机制——Pointcuts和Advice的形式——但是并不包含任何特殊的追踪或者日志框架。它被(在列表7中)JDK14Tracing继承,它是通过使用java.util.logging框架的追踪来实现入口与出口模型方法的。这意味着,在将来其它的追踪实现也可以在工作台中使用(例如,基于Log4j的追踪),并且可以复用追踪的上级方面。
列表8的第6行中显示:追踪方面包含一个抽象的Pointcut,我们在列表9中再次显示这几行内容:
列表9:Tracing.aj中的一个Pointcut
/*
* Sub-aspects implement to determine what and when to trace
*/
protected abstract pointcut shouldTrace ();
|
这在列表10中的HelloWorldJDK14Tracing方面已经构建成了具体形式。具体的实现描述了HelloWorld类中sayHello()方法的追踪的执行范围。staticinitalization pointcut是JDK141Tracing的实现工件。
列表10:HelloWorldJDK14Tracing.aj
public aspect HelloWorldJDK14Tracing extends
org.aspectj.lib.tracing.JDK14Tracing {
protected pointcut tracingScope () :
execution( * HelloWorld.sayHello() )
|| staticinitialization(HelloWorld);
}
|
追踪方面包含了方法和构造器的 before 和 after Advice,这意味着入口和出口都被追踪了。列表11是这些条Advice的一个例子。Advice代表一个入口或者出口模型方法(或者异常模型方法)。这些方法在JDK14Tracing中执行,如列表12所示:
列表11:Tracing.aj中的Before advice
before (Object obj) : tracedMethod() && nonStaticContext(obj)
&& shouldTrace() {
enter(thisJoinPoint,obj);
}
|
列表12:JDK14Tracing.aj中的入口方法
protected void enter (JoinPoint jp) {
Logger logger = Logger.getLogger(getLoggerName(
jp.getStaticPart()));
if (isTracingEnabled(logger)) {
CodeSignature signature =
(CodeSignature)jp.getSignature();
logger.entering(signature.getDeclaringTypeName(),
signature.getName(),jp.getArgs());
}
}
|
追踪方面除了一个通过我们产生的方面实现的抽象Pointcut外,还包含很多具体的Pointcut。如列表12所示:
列表13:Tracing.aj中的Pointcuts
private pointcut staticContext () : !this(Object);
private pointcut nonStaticContext (Object obj) : this(obj);
private pointcut voidMethod () : execution(void *(..));
private pointcut objectMethod () : execution(* Object.*(..));
|
第一个Pointcut用来定义静态方法的范围,第二个用来定义非静态方法的范围。第三个用来确定一个空返回类型。第四个Pointcut用来确定从java.lang.Object继承的方法的异常(例如,toString()和hashcode()),为了防止递归的产生,这在追踪中是被禁止的。这些Pointcut过去经常被Advice用在不同的联合方式中,为了保证所有的方法都被正确的追踪。
使用FFDC方面
在这个章节中,你将会学些如何把FFDC应用到你的应用程序。首先,你要把它应用到之前建立的样例应用程序中,查看它是如何工作的。之后我们会讨论把FFDC应用到你的应用程序的可能性,最后,我们会在方面覆盖之下部分讨论它们是如何工作的。
在样例应用程序中应用FFDC方面
使用在安装章节建立的样例测试模型,把FFDC应用到sayGoodbye()方法中。这和使用追踪方面章节中在sayHello()方法中应用日志的方法类似。按照下列步骤将FFDC应用的模型应用程序:
- 在图表中,右键点击
sayGoodbye()
- 从菜单中选择Aspect Lib
- 在Aspect Specification对话框中,从AspectJ Library Aspects中选择JDK14FFDC。
- 选择OK。
你会发现在操作旁边出现一个<<jdk14ffdc>>如图3所示:
图3:把jdk14ffdc应用到sayGoodbye模型元素
下一步,在图表中选择运行UML到Java代码转换类,右键点击它,找到Transform > Run Transformation > UML to Java。
如果你使用的是Package Explorer,你会发现一个叫做HelloWorldJDK14FFDC.aj的文件被建立,如图4所示:
图4:新建立的HelloWorldJDK14FFDC.aj文件
稍后你会看到这个文件的详细信息。
现在我们可以运行应用程序来查看FFDC的应用情况。请注意在使用追踪方面章节中我们也是把它应用到了sayHello()方法中,因此当你运行应用程序时,会看到输出的追踪信息。由于最后运行的应用程序是HelloWorld,所以你只需点击工具栏上的Run按钮即可返回(或者找到Run > Run History > HelloWorld)。
和之前一样,输出结果被显示在终端上。在列表14中你会看到FFDC方面的输出结果。
列表14:FFDC和追踪方面的输出
Starting
? initLogger() logger=HelloWorld
? initLogger() traceEnabled=true
Hello World
Goodbye World
Finishing
21-Oct-2005 13:17:31 HelloWorld sayHello
FINER: ENTRY
21-Oct-2005 13:17:31 HelloWorld sayHello
FINER: RETURN Hello World
21-Oct-2005 13:17:31 org.aspectj.lib.logging.JDK14FFDC processNonStaticFFDC
WARNING: object=HelloWorld@400e699e, source=HelloWorld.sayGoodbye, line=25
java.lang.RuntimeException
at HelloWorld.throwingMethod(HelloWorld.java:31)
at HelloWorld.sayGoodbye(HelloWorld.java:24)
at HelloWorld.main(HelloWorld.java:7)
|
如何在你的应用程序中应用FFDC
和在使用追踪方面章节讨论的追踪方面不同,你通常希望在整个应用程序中使用FFDC。它提供了一个兼容的错误处理机制,同时会确保在整个程序设计阶段不会丢失任何错误信息。使用 Aspects for MDD 框架,你可以使用同样的方法在类中应用追踪方面。
表面之下
如果你已经在部分UML模型中应用了方面,那么在.aj文件中会产生一个AspectJ方面,它是作为UML转换成Java代码过程的一个部分。在范例过程中建立方面,如列表15所示:
列表15:HelloWorldJDK14FFDC.aj
public aspect HelloWorldJDK14FFDC extends
org.aspectj.lib.logging.JDK14FFDC {
protected pointcut ffdcScope () :
withincode( * HelloWorld.sayGoodbye() );
}
|
和追踪方面相比,这次产生的方面继承了一个叫JDK14FFDC的方面。列表16中显示了这个方面的部分内容:
列表16:JDK14FFDC方面
public abstract aspect JDK14FFDC extends FFDC {
/**
* Method for consumption of raw FFDC in a
* static context
*/
protected void processStaticFFDC(
Throwable th,
JoinPoint.StaticPart tjp,
JoinPoint.StaticPart ejp) {
Logger logger = Logger.getLogger(getLoggerName(ejp));
logger.log(Level.WARNING,"source=" + getSourceId(ejp)
+ ", line=" + getProbeId(tjp),th);
}
/**
* Method for consumption of raw FFDC in a
* non-static context
*/
protected void processNonStaticFFDC(
Throwable th,
Object obj,
JoinPoint.StaticPart tjp,
JoinPoint.StaticPart ejp) {
Logger logger = Logger.getLogger(getLoggerName(ejp));
logger.log(Level.WARNING,"object=" + obj
+ ", source="
+ getSourceId(ejp) + ", line="
+ getProbeId(tjp),th);
}
...
}
|
FFDC方面和追踪方面类似,你会发现JDK14FFDC同时还继承了另外一个方面,也就是FFDC。列表17中显示了这个方面的一部分:
列表17:FFDC方面
public abstract aspect FFDC {
/**
* Scope of FFDC policy e.g. packages, classes, methods
* is declared by sub-aspect.
*/
protected abstract pointcut ffdcScope();
private pointcut staticContext() : !this(Object);
private pointcut nonStaticContext(Object obj) : this(obj);
private pointcut caughtThrowable(Throwable th) :
handler(Throwable+) && args(th);
/**
* Exclude FFDC aspects from exception reporting to avoid
* unwanted recursion
*/
private pointcut excluded() : within(FFDC+);
/**
* Advice for catch blocks in static context
*/
before (Throwable th) : caughtThrowable(th)
&& ffdcScope() && !excluded() && staticContext() {
processStaticFFDC(th,
thisJoinPointStaticPart,
thisEnclosingJoinPointStaticPart);
}
/**
* Advice for catch blocks in non-static context. Extract
* the object that caught the exception
*/
before (Throwable th, Object obj) : caughtThrowable(th)
&& ffdcScope() && !excluded() && nonStaticContext(obj) {
processNonStaticFFDC(th,
obj,
thisJoinPointStaticPart,
thisEnclosingJoinPointStaticPart);
}
/**
* Method for consumption of raw FFDC in a
* static context
*/
protected abstract void processStaticFFDC(
Throwable th,
JoinPoint.StaticPart tjp,
JoinPoint.StaticPart ejp);
/**
* Method for consumption of raw FFDC in a
* non-static context
*/
protected abstract void processNonStaticFFDC(
Throwable th,
Object obj,
JoinPoint.StaticPart tjp,
JoinPoint.StaticPart ejp);
...
}
|
请看FFDC方面,你会发现这里有两个抽象的方法processStaticFFDC和processNonStaticFFDC。这两个方法在列表16的JDK14FFDC方法中显示了,它们各自记录了信息。
再次查看FFDC方面,你会发现有一个叫做ffdcScope的Pointcut,如列表18所示:
列表18:抽象的Pointcut ffdcScop()
/**
* Scope of FFDC policy e.g. packages, classes, methods is declared by
* sub-aspect.
*/
protected abstract pointcut ffdcScope();
|
请查看列表19中显示的HelloWorldJDKFFDC方面:
列表19:刚产生的HelloWorldJDK14FFDC方面
public aspect HelloWorldJDK14FFDC extends
org.aspectj.lib.logging.JDK14FFDC {
protected pointcut ffdcScope () :
withincode( * HelloWorld.sayGoodbye() );
}
|
你会看到这里提供了一个ffdcScope Pointcut的具体实现方式。更具体的说,FFDC方面的范围应该在HelloWorld类的sayGoodbye()方法中,在这里sayGoodbye()不带任何参数(因此用()),并且可以返回任意类型的值。
现在你已经了解了FFDC方面所涉及的范围。但是你还是不知道它做了什么。这在FFDC方面的Advice中有详细信息,如列表20所示:
列表20:FFDC方面中的Advice
/**
* Advice for catch blocks in static context
*/
before (Throwable th) : caughtThrowable(th)
&& ffdcScope() && !excluded() && staticContext() {
processStaticFFDC(th,
thisJoinPointStaticPart,
thisEnclosingJoinPointStaticPart);
}
/**
* Advice for catch blocks in non-static context. Extract
* the object that caught the exception
*/
before (Throwable th, Object obj) : caughtThrowable(th)
&& ffdcScope() && !excluded() && nonStaticContext(obj) {
processNonStaticFFDC(th,
obj,
thisJoinPointStaticPart,
thisEnclosingJoinPointStaticPart);
}
|
这里的Advice是before Advice,这意味着它将在Join point实现之前执行,这正好符合Pointcut的条件。请注意在静态内容和非静态内容中各有一个捕捉块。在静态内容中,Pointcut表达式是由很多命名的Pointcut组成的:
-
caughtThrowable
-
ffdcScope
-
excluded
-
staticContext
与非静态内容相比,staticContext被nonStaticContext替换了。这些Pointcut在FFDC方面中已经定义,如列表21所示:
列表21:FFDC方面中定义的Pointcut
protected abstract pointcut ffdcScope();
private pointcut staticContext() : !this(Object);
private pointcut nonStaticContext(Object obj) : this(obj);
private pointcut caughtThrowable(Throwable th) :
handler(Throwable+) && args(th);
private pointcut excluded() : within(FFDC+);
|
通过先前的讨论,你已经了解了ffdcScope Pointcut是什么。依次查看剩余的四个Pointcut,下面是它们的功能:
-
staticContext(): 它和未邦定的Join point匹配
-
nonStaticContext(Object obj):他是staticContext()的一个补充,它允许你得到一个正在运行的对象的参考说明。
-
caughtThrowable(Throwable th):它和处理Throwable类型的异常相匹配。通过使用args Pointcut指示器,你可以找到异常实例,还可以在你的报告中使用它。
-
excluded(): 它和FFDC方面和其子方面中的所有Join points匹配。例如象防止递归发生的Pointcut。
列表21中的Advice通过内容(静态或者非静态)组合这些Pointcut,然后调用一个执行方法处理信息,最终将其打印到屏幕上。
结论
这篇文章介绍了Aspects for MDD 框架,以及com.ibm.aspectj.lib是如何帮助我们在MDD应用程序中使用追踪和FFDC的。这篇文章一步一步地详细描述了各种方面,并且讨论了这些方面是如何应用到应用程序的。
参考资料 学习
获得产品和技术
-
AJDT主页:下载最新版本,查看版本的更新内容或者参与团体的讨论中。
-
AspectJ主页:浏览文档,获得最新的消息,下载或者加入AspectJ团体的讨论中。
- 为Rational Software Architect下载 Aspects for MDD 插件,插件可在RAS资料库中找到,在"设计方面"种类下。你可以直接从Rational Software Architect工具访问RAS资料库。想要了解更多关于如何做的信息,可以访问Pattern Solutions 页面,然后查看"Installing RAS asset"下的内容。
讨论
作者简介  | |  | Dr. Helen Hawkins 是一个IBM面向方面软件开发小组的一个开发人员,他在Hursley,England从事AJDT和CME Eclipse项目的工作。在加入这个小组之前,他的主要工作是测试(特别是压力测试)系统。 |
 | |  | Sian January是在位于Hursley,England的IBM Java 技术中心的一名软件工程师。她在2003年加入IBM后,已经从事了三年AJDT Eclipse.org项目的研究。她已经在多个会议上讨论了AspectJ的话题,其中包括2005年在南特的Eclipse Day。 |
对本文的评价
|