|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
java也能做一些底层语言开发做的事情(难度很高,不是java顶尖高手是做不来的),编程摘自久久学院
关于一个可以会见源代码的履历丰厚的Java开辟职员来讲,任何程序都能够被看做是博物馆里通明的模子。相似线程转储(dump)、办法挪用跟踪、断点、切面(profiling)统计表等工具可让我们懂得程序今朝正在实行甚么操纵、方才做了甚么操纵、将来将做甚么操纵。可是在产物情况中情形就没有那末分明了,这些工具通常为不克不及够利用的,或最多只能由受过练习的开辟者利用。撑持团队和终极用户也必要晓得在某个时候使用程序正在实行甚么操纵。
为了弥补这个空白,我们已创造了一些复杂的替换品,比方日记文件(典范情形下用于服务器处置)和形态条(用于GUI使用程序)。可是,因为这些工具只能捕获和呈报可用信息的一个很小的子集,而且一般必需把这些信息用简单了解的体例体现出来,以是程序员趋势于把它们明白地编写到使用程序中。而这些代码会环绕纠缠着使用程序的营业逻辑,当开辟者试图调试或懂得中心功效的时分,他们必需"环绕这些代码事情",并且还要记得功效产生改动后更新这些代码。我们但愿完成的真正功效是把形态呈报会合在某个地位,把单个形态动静作为元数据(metadata)来办理。
在本文中我将思索利用嵌进GUI使用程序中的形态条组件的情况。我将先容多种完成这类形态呈报的分歧办法,从传统的硬编码习气入手下手。随后我会先容Java1.5的大批新特征,包含注解(annotation)和运转时字节码重构(instrumentation)。
形态办理器(StatusManager)
我的次要方针是创建一个能够嵌进GUI使用程序的JStatusBarSwing组件。显现了一个复杂的Jframe中形态条的款式。
.我们静态天生的形态条
因为我不但愿间接在营业逻辑中援用任何GUI组件,我将创建一个StatusManager(形态办理器)来充任形态更新的出口点。实践的关照会被托付给StatusState对象,因而今后能够扩大它以撑持多个并发的线程。显现了这类布置。
.StatusManager和JstatusBar
如今我必需编写代码挪用StatusManager的办法来呈报使用程序的历程。典范情形下,这些办法挪用都分离地贯串于try-finally代码块中,一般每一个办法一个挪用。
publicvoidconnectToDB(Stringurl){
StatusManager.push("Connectingtodatabase");
try{
...
}finally{
StatusManager.pop();
}
}
这些代码完成了我们所必要功效,可是在代码库中数十次、乃至于数百次地复制这些代码以后,它看起来就有些凌乱了。别的,假如我们但愿用一些别的的体例会见这些动静该怎样办呢?在本文的前面部分中,我将界说一个用户友爱的非常处置程序,它共享了不异的动静。成绩是我把形态动静埋没在办法的完成当中了,而没有把动静放在动静所属的接口中。
面向属性编程
我真正想完成的操纵是把对StatusManager的援用都放到代码表面的某个中央,并复杂地用我们的动静标志这个办法。接着我可使用代码天生(code-generation)或运转时检查(introspection)来实行真实的事情。XDoclet项目把这类办法归结为面向属性编程(Attribute-OrientedProgramming),它还供应了一个框架组件,能够把自界说的相似Javadoc的标志转换到源代码当中。
可是,JSR-175包括了如许的内容,Java1.5为了包括实在代码中的这些属性供应了一种布局化水平更高的格局。这些属性被称为"注解(annotations)",我们可使用它们为类、办法、字段或变量界说供应元数据。它们必需被显式声明,并供应一组能够包括恣意常量值(包含原语、字符串、列举和类)的称号-值对(name-valuepair)。
注解(Annotations)
为了处置形态动静,我但愿界说一个包括字符串值的新注解。注解的界说十分相似接口的界说,可是它用@interface关头字取代了interface,而且只撑持办法(只管它们的功效更像字段):
public@interfaceStatus{
Stringvalue();
}
与接口相似,我把@interface放进一个叫做Status.java的文件中,并把它导进就任何必要援用它的文件中。
对我们的字段来讲,value多是个奇异的称号。相似message的称号大概更合适;可是,value关于Java来讲具有特别的意义。它同意我们利用@Status("...")取代@Status(value="...")来界说注解,这分明加倍简便。
我如今可使用上面的代码界说本人的办法:
@Status("Connectingtodatabase")
publicvoidconnectToDB(Stringurl){
...
}
请注重,我们在编译这段代码的时分必需利用-source1.5选项。假如你利用Ant而不是间接利用javac命令行创建使用程序,那末你必要利用Ant1.6.1以上版本。
作为类、办法、字段和变量的增补,注解也能够用于为别的的注解供应元数据。出格地,Java引进了大批注解,你可使用这些注解来定制你本人的注解的事情体例。我们用上面的代码从头界说本人的注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public@interfaceStatus{
Stringvalue();
}
@Target注解界说了@Status注解能够援用甚么内容。幻想情形下,我但愿标志年夜块的代码,可是它的选项只要办法、字段、类、当地变量、参数和别的注解。我只对代码感乐趣,因而我选择了METHOD(办法)。
@Retention注解同意我们指定Java甚么时分能够自立地丢弃动静。它多是SOURCE(在编译时丢弃)、CLASS(在类载进时丢弃)或RUNTIME(不丢弃)。我们先选择SOURCE,可是在本文后部我们会更新它。
重构源代码
如今我的动静都被编码放进元数据中了,我必需编写一些代码来关照形态监听程序。假定在某个时分,我持续把connectToDB办法保留源代码控件中,可是却没有对StatusManager的任何援用。可是,在编译这个类之前,我但愿到场一些需要的挪用。也就是说,我但愿主动地拔出try-finally语句和push/pop挪用。
XDoclet框架组件是一种Java源代码天生引擎,它利用了相似上述的注解,可是把它们存储在Java源代码的正文(comment)中。XDoclet天生全部Java类、设置文件或别的创建的部分的时分十分完善,可是它不撑持对已有Java类的修正,而这限定了重构的无效性。作为取代,我可使用剖析工具(比方JavaCC或ANTLR,它供应了剖析Java源代码的语法基本),可是这必要消费大批精神。
看起来没有甚么能够用于Java代码的源代码重构的很好的工具。这类工具大概有市场,可是你在本文的前面部分能够看到,字节码重构多是一种更壮大的手艺。重构字节码
不是重构源代码然后编译它,而是编译原始的源代码,然后重构它所发生的字节码。如许的操纵大概比源代码重构更简单,也大概加倍庞大,而这依附于必要的正确转换。字节码重构的次要长处是代码能够在运转时被修正,不必要利用编译器。
只管Java的字节码格局绝对复杂,我仍是但愿利用一个Java类库来实行字节码的剖析和天生(这能够把我们与将来Java类文件格局的改动断绝开来)。我选择了利用Jakarta的ByteCodeEngineeringLibrary(字节码引擎类库,BCEL),可是我还能够选用CGLIB、ASM或SERP。
因为我将利用多种分歧的体例重构字节码,我将从声明重构的通用接口入手下手。它相似于实行基于注解重构的复杂框架组件。这个框架组件基于注解,将撑持类和办法的转换,因而该接口有相似上面的界说:
publicinterfaceInstrumentor
{
publicvoidinstrumentClass(ClassGenclassGen,Annotationa);
publicvoidinstrumentMethod(ClassGenclassGen,MethodGenmethodGen,Annotationa);
}
ClassGen和MethodGen都是BCEL类,它们利用了Builder形式(pattern)。也就是说,它们为改动别的不成变的(immutable)对象、和可变的和不成变的体现(representation)之间的转换供应了办法。
如今我必要为接口编写完成,它必需用得当的StatusManager挪用改换@Status注解。后面提到,我但愿把这些挪用包括在try-finally代码块中。请注重,要到达这个方针,我们所利用的注解必需用@Retention(RetentionPolicy.CLASS)举行标志,它唆使Java编译器在编译过程当中不要丢弃注解。因为在后面我把@Status声明为@Retention(RetentionPolicy.SOURCE)的,我必需更新它。
在这类情形下,重构字节码分明比重构源代码更庞大。其缘故原由在于try-finally是一种仅仅存在于源代码中的观点。Java编译器把try-finally代码块转换为一系列的try-catch代码块,并在每个前往之前拔出对finally代码块的挪用。因而,为了把try-finally代码块增加到已有的字节码中,我也必需实行相似的事件。
上面是体现一个一般办法挪用的字节码,它被StatusManager更新围绕着:
0:ldc#2;//字符串动静
2:invokestatic#3;//办法StatusManager.push:(LString;)V
5:invokestatic#4;//办法doSomething:()V
8:invokestatic#5;//办法StatusManager.pop:()V
11:return
上面是不异的办法挪用,可是位于try-finally代码块中,因而,假如它发生了非常会挪用StatusManager.pop():
0:ldc#2;//字符串动静
2:invokestatic#3;//办法StatusManager.push:(LString;)V
5:invokestatic#4;//办法doSomething:()V
8:invokestatic#5;//办法StatusManager.pop:()V
11:goto20
14:astore_0
15:invokestatic#5;//办法StatusManager.pop:()V
18:aload_0
19:athrow
20:return
Exceptiontable:
fromtotargettype
5814any
141514any
你能够发明,为了完成一个try-finally,我必需复制一些指令,并增加了几个跳转和非常表纪录。侥幸的是,BCEL的InstructionList类使这类事情相称复杂。
在运转时重构字节码
如今我具有了一个基于注解修正类的接口和该接口的详细完成了,下一步是编写挪用它的实践框架组件。实践上我将编写大批的框架组件,先从运转时重构一切类的框架组件入手下手。因为这类操纵会在build过程当中产生,我决意为它界说一个Ant事件。build.xml文件中的重构方针的声明应当以下:
<instrumentclass="com.pkg.OurInstrumentor">
<filesetdir="$(classes.dir)">
<includename="**/*.class"/>
</fileset>
</instrument>
为了完成这类事件,我必需界说一个完成org.apache.tools.ant.Task接口的类。我们的事件的属性和子元素(sub-elements)都是经由过程set和add办法挪用传送出去的。我们挪用实行(execute)办法来完成事件所要实行的事情--在示例中,就是重构<fileset>中指定的类文件。
publicclassInstrumentTaskextendsTask{
...
publicvoidsetClass(StringclassName){...}
publicvoidaddFileSet(FileSetfileSet){...}
publicvoidexecute()throwsBuildException{
Instrumentorinst=getInstrumentor();
try{
DirectoryScannerds=fileSet.getDirectoryScanner(project);
//Java1.5的"for"语法
for(Stringfile:ds.getIncludedFiles()){
instrumentFile(inst,file);
}
}catch(Exceptionex){
thrownewBuildException(ex);
}
}
...
}
用于该项操纵的BCEL5.1版本有一个成绩--它不撑持剖析注解。我能够载进正在重构的类并利用反射(reflection)检察注解。可是,假如如许,我就不能不利用RetentionPolicy.RUNTIME来取代RetentionPolicy.CLASS。我还必需在这些类中实行一些静态的初始化,而这些操纵大概载进当地类库或引进别的的依附干系。侥幸的是,BCEL供应了一种插件(plugin)机制,它同意客户端剖析字节码属性。我编写了本人的AttributeReader的完成(implementation),在呈现注解的时分,它晓得怎样剖析拔出字节码中的RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations属性。BCEL将来的版本应当会包括这类功效而不是作为插件供应。
编译时候的字节码重构办法显现在示例代码的code/02_compiletime目次中。
可是这类办法有良多缺点。起首,我必需给创建历程增添分外的步骤。我不克不及基于命令行设置或别的编译时没有供应的信息来决意翻开或封闭重构操纵。假如重构的或没有重构的代码必要同时在产物情况中运转,那末就必需创建两个独自的.jars文件,并且还必需决意利用哪个。
在类载进时重构字节码
更好的办法多是提早字节码重构操纵,直到字节码被载进的时分才举行重构。利用这类办法的时分,重构的字节码不必保留起来。我们的使用程序启动时候的功能大概会遭到影响,可是你却能够基于本人的体系属性或运转时设置数据来把持举行甚么操纵。
Java1.5之前,我们利用定制的类载进程序大概完成这类类文件保护操纵。可是Java1.5中新增添的java.lang.instrument程序包供应了多数附加的工具。出格地,它界说了ClassFileTransformer的观点,在尺度的载进过程当中我们可使用它来重构一个类。
为了在得当的时分(在载进任何类之前)注册ClassFileTransformer,我必要界说一个premain办法。Java在载进主类(mainclass)之前将挪用这个办法,而且它传送出去对Instrumentation对象的援用。我还必需给命令行增添-javaagent参数选项,告知Java我们的premain办法的信息。这个参数选项把我们的agentclass(代办署理类,它包括了premain办法)的全名和恣意字符串作为参数。在例子中我们把Instrumentor类的全名作为参数(它必需在统一行当中):
-javaagent:boxpeeking.instrument.InstrumentorAdaptor=
boxpeeking.status.instrument.StatusInstrumentor
如今我已布置了一个回调(callback),它在载进任何含有注解的类之前城市产生,而且我具有Instrumentation对象的援用,能够注册我们的ClassFileTransformer了:
publicstaticvoidpremain(StringclassName,
Instrumentationi)
throwsClassNotFoundException,
InstantiationException,
IllegalAccessException
{
ClassinstClass=Class.forName(className);
Instrumentorinst=(Instrumentor)instClass.newInstance();
i.addTransformer(newInstrumentorAdaptor(inst));
}
我们在此处注册的适配器将充任下面给出的Instrumentor接口和Java的ClassFileTransformer接口之间的桥梁。
publicclassInstrumentorAdaptor
implementsClassFileTransformer
{
publicbyte[]transform(ClassLoadercl,StringclassName,ClassclassBeingRedefined,
ProtectionDomainprotectionDomain,byte[]classfileBuffer)
{
try{
ClassParsercp=newClassParser(newByteArrayInputStream(classfileBuffer),className+".java");
JavaClassjc=cp.parse();
ClassGencg=newClassGen(jc);
for(Annotationan:getAnnotations(jc.getAttributes())){
instrumentor.instrumentClass(cg,an);
}
for(org.apache.bcel.classfile.Methodm:cg.getMethods()){
for(Annotationan:getAnnotations(m.getAttributes())){
ConstantPoolGencpg=cg.getConstantPool();
MethodGenmg=newMethodGen(m,className,cpg);
instrumentor.instrumentMethod(cg,mg,an);
mg.setMaxStack();
mg.setMaxLocals();
cg.replaceMethod(m,mg.getMethod());
}
}
JavaClassjcNew=cg.getJavaClass();
returnjcNew.getBytes();
}catch(Exceptionex){
thrownewRuntimeException("instrumenting"+className,ex);
}
}
...
}
这类在启动时重构字节码的办法位于在示例的/code/03_startup目次中。
非常的处置
文章后面提到,我但愿编写附加的代码利用分歧目标的@Status注解。我们来思索一下一些分外的需求:我们的使用程序必需捕获一切的未处置非常并把它们显现给用户。可是,我们不是供应Java仓库跟踪,而是显现具有@Status注解的办法,并且还不该该显现任何代码(类或办法的称号或行号等等)。
比方,思索上面的仓库跟踪信息:
java.lang.RuntimeException:CouldnotloaddataforsymbolIBM
atboxpeeking.code.YourCode.loadData(UnknownSource)
atboxpeeking.code.YourCode.go(UnknownSource)
atboxpeeking.yourcode.ui.Main+2.run(UnknownSource)
atjava.lang.Thread.run(Thread.java:566)
Causedby:java.lang.RuntimeException:Timedout
atboxpeeking.code.YourCode.connectToDB(UnknownSource)
...更多信息
这将招致中所示的GUI弹出框,下面的例子假定你的YourCode.loadData()、YourCode.go()和YourCode.connectToDB()都含有@Status注解。请注重,非常的序次是相反的,因而用户开始失掉的是最具体的信息。
.显现在毛病对话框中的仓库跟踪信息
为了完成这些功效,我必需对已有的代码举行略微的修正。起首,为了确保在运转时@Status注解是能够看到的,我就必需再次更新@Retention,把它设置为@Retention(RetentionPolicy.RUNTIME)。请记着,@Retention把持着JVM甚么时分丢弃注解信息。如许的设置意味着注解不但能够被编译器拔出字节码中,还可以利用新的Method.getAnnotation(Class)办法经由过程反射来举行会见。
如今我必要布置吸收代码中没有明白处置的任何非常的关照了。在Java1.4中,处置任何特定线程上未处置非常的最好办法是利用ThreadGroup子类并给该范例的ThreadGroup增加本人的新线程。可是Java1.5供应了分外的功效。我能够界说UncaughtExceptionHandler接口的一个实例,并为任何特定的线程(或一切线程)注册它。
请注重,在例子中为特定非常注册大概更好,可是在Java1.5.0beta1(#4986764)中有一个bug,它使如许操纵没法举行。可是为一切线程设置一个处置程序是能够事情的,因而我就如许操纵了。
如今我们具有了一种截取未处置非常的办法了,而且这些非常必需被呈报给用户。在GUI使用程序中,典范情形下如许的操纵是经由过程弹出一个包括全部仓库跟踪信息或复杂动静的形式对话框来完成的。在例子中,我但愿在发生非常的时分显现一个动静,可是我但愿供应仓库的@Status形貌而不是类和办法的称号。为了完成这个目标,我复杂地在Thread的StackTraceElement数组中查询,找到与每一个框架相干的java.lang.reflect.Method对象,并查询它的仓库注解列表。不幸的是,它只供应了办法的称号,没有供应办法的特性量(signature),因而这类手艺不撑持称号不异的(但@Status注解分歧的)重载办法。
完成这类办法的示例代码能够在peekinginside-pt2.tar.gz文件的/code/04_exceptions目次中找到。
取样(Sampling)
我如今有举措把StackTraceElement数组转换为@Status注解仓库。这类操纵比标明看到的加倍有效。Java1.5中的另外一个新特征--线程检查(introspection)--使我们可以从以后正在运转的线程中失掉正确的StackTraceElement数组。有了这两部分信息以后,我们就能够机关JstatusBar的另外一种完成。StatusManager将不会在产生办法挪用的时分吸收关照,而是复杂地启动一个附加的线程,让它卖力在一般的距离时代抓取仓库跟踪信息和每一个步骤的形态。只需这个距离时代充足短,用户就不会感到到更新的提早。
上面使"sampler"线程面前的代码,它跟踪另外一个线程的经由:
classStatusSamplerimplementsRunnable
{
privateThreadwatchThread;
publicStatusSampler(ThreadwatchThread)
{
this.watchThread=watchThread;
}
publicvoidrun()
{
while(watchThread.isAlive()){
//从线程中失掉仓库跟踪信息
StackTraceElement[]stackTrace=watchThread.getStackTrace();
//从仓库跟踪信息中提取形态动静
List<Status>statusList=StatusFinder.getStatus(stackTrace);
Collections.reverse(statusList);
//用形态动静创建某种形态
StatusStatestate=newStatusState();
for(Statuss:statusList){
Stringmessage=s.value();
state.push(message);
}
//更新以后的形态
StatusManager.setState(watchThread,state);
//休眠到下一个周期
try{
Thread .sleep(SAMPLING_DELAY);
}catch(InterruptedExceptionex){}
}
//形态复位
StatusManager.setState(watchThread,newStatusState());
}
}
与增添办法挪用、手动或经由过程重构比拟,取样对程序的损害性(invasive)更小。我基本不必要改动创建历程或命令行参数,或修正启动历程。它也同意我经由过程调剂SAMPLING_DELAY来把持占用的开支。不幸的是,当办法挪用入手下手或停止的时分,这类办法没有明白的回调。除形态更新的提早以外,没有缘故原由请求这段代码在谁人时分吸收回调。可是,将来我可以增添一些分外的代码来跟踪每一个办法的正确的运转时。经由过程反省StackTraceElement是能够准确地完成如许的操纵的。
经由过程线程取样完成JStatusBar的代码能够在peekinginside-pt2.tar.gz文件的/code/05_sampling目次中找到。
在实行过程当中重构字节码
经由过程把取样的办法与重构组合在一同,我可以构成一种终极的完成,它供应了各类办法的最好特征。默许情形下可使用取样,可是使用程序的消费工夫最多的办法能够被一般地举行重构。这类完成基本不会安装ClassTransformer,可是作为取代,它会一次一个地重构办法以呼应取样过程当中搜集到的数据。
为了完成这类功效,我将创建一个新类InstrumentationManager,它能够用于重构和不重构自力的办法。它可使用新的Instrumentation.redefineClasses办法来修正余暇的类,同时期码则能够不中断实行。后面部分中增添的StatusSampler线程如今有了分外的职责,它把任何本人"发明"的@Status办法增加到汇合中。它将周期性地找出最坏的冲克者并把它们供应给InstrumentationManager以供重构。这同意使用程序加倍准确地跟踪每一个办法的启动和停止时候。
后面提到的取样办法的一个成绩是它不克不及辨别长工夫运转的办法与在轮回中屡次挪用的办法。因为重构会给每次办法挪用增添必定的开支,我们有需要疏忽频仍挪用的办法。侥幸的是,我们可使用重构办理这个成绩。除复杂地更新StatusManager以外,我们将保护每一个重构的办法被挪用的次数。假如这个数值凌驾了某个极限(意味着保护这个办法的信息的开支太年夜了),取样线程将会永久地作废对该办法的重构。
幻想情形下,我将把每一个办法的挪用数目存储在重构过程当中增加到类的新字段中。不幸的是,Java1.5中增添的类转换机制不同意如许操纵;它不克不及增添或删除任何字段。作为取代,我将把这些信息存储在新的CallCounter类的Method对象的静态映照中。
这类夹杂的办法能够在示例代码的/code/06_dynamic目次中找到。
归纳综合
供应了一个矩形,它显现了我给出的例子相干的特征和价值。
.重构办法的剖析
你能够发明,静态的(Dynamic)办法是各类计划的优秀组合。与利用重构的一切示例相似,它供应了办法入手下手或停止时候的明白的回调,因而你的使用程序能够正确地跟踪运转时并当即为用户供应反应信息。可是,它还可以作废某种办法的重构(它被过于频仍地挪用),因而它不会遭到别的的重构计划碰到的功能成绩的影响。它没有包括编译时步骤,而且它没有增添类载进过程当中的分外的事情。
将来的趋向
我们能够给这个项目增添大批的附件特征,使它加倍合用。个中最有效的特征多是静态的形态信息。我们可使用新的java.util.Formatter类把相似printf的形式交换(patternsubstitution)用于@Status动静中。比方,我们的connectToDB(Stringurl)办法中的@Status("Connectingto%s")注解能够把URL作为动静的一部分呈报给用户。
在源代码重构的匡助下,这大概显得微乎其微,由于我将利用的Formatter.format办法利用了可变参数(Java1.5中增添的"把戏"功效)。重构过的版本相似上面的情况:
publicvoidconnectToDB(Stringurl){
Formatterf=newFormatter();
Stringmessage=f.format("Connectingto%s",url);
StatusManager.push(message);
try{
...
}finally{
StatusManager.pop();
}
}
不幸的是,这类"把戏"功效是完整在编译器中完成的。在字节码中,Formatter.format把Object[]作为参数,编译器明白地增加代码来包装每一个原始的范例并拆卸该数组。假如BCEL没有抓紧填补,而我又必要利用字节码重构,我将不能不从头完成这类逻辑。
因为它只能用于重构(这类情形下办法参数是可用的)而不克不及用于取样,你大概但愿在启动的时分重构这些办法,或起码使静态完成倾向于任何办法的重构,还能够在动静中利用替换形式。
你还能够跟踪每一个重构的办法挪用的启动次数,因而你还能够加倍准确地呈报每一个办法的运转次数。你乃至于能够保留这些次数的汗青统计数据,并利用它们构成一个真实的进度条(取代我利用的不断定的版本)。这类才能将付与你在运转时重构某种办法的一个很好的来由,由于跟踪任何自力的办法的开支都是很能很分明的。
你能够给进度条增添"调试"形式,它不论办法挪用是不是包括@Status注解,呈报取样过程当中呈现的一切办法挪用。这关于任何但愿调试逝世锁或功能成绩的开辟者来讲都是价值千金。实践上,Java1.5还为逝世锁(deadlock)检测供应了一个可编程的API,在使用程序锁住的时分,我们可使用该API把历程条酿成白色。
本文中创建的基于注解的重构框架组件大概很有市场。一个同意字节码在编译时(经由过程Ant事件)、启动时(利用ClassTransformer)和实行过程当中(利用Instrumentation)举行重构的工具关于大批别的新项目来讲毫无疑问地十分有代价。
总结
在这几个例子中你能够看到,元数据编程(meta-programming)多是一种十分壮大的手艺。呈报长工夫运转的操纵的历程仅仅是这类手艺的使用之一,而我们的JStatusBar仅仅是相同这些信息的一种前言。我们能够看到,Java1.5中供应的良多新特征为元数据编程供应了加强的撑持。出格地,把注解和运转时重构组合在一同为面向属性的编程供应了真正静态的情势。我们能够进一步利用这些手艺,使它的功效超出已有的框架组件(比方XDoclet供应的框架组件的功效)。
认真的记,感觉很紧张根本就没有时间和能力,来对技术知识点进行思考。这样课下就只能对知识进行简单的理解,其实简单的理解就是记忆课堂上讲的知识点, |
|