|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
诸如RMI,EJB等一些技术并不是你说的那么复杂,而是它们把一些复杂的工具封装成不复杂的工具了,理解这些工具是需要些时间。我问你,.net里有这些工具吗?要简单多少?。程序处置庞大和不熟习Java代码的手艺
级别:中级
AbhijitBelapurkar(abhijit_belapurkar@infosys.com)
初级手艺架构师,InfosysTechnologiesLimited
2004年3月
假如您已经接受而且必需保护某个基于Java的使用程序,那末本文就是为您筹办的。作者AbhijitBelapurkar将向您展现怎样利用面向方面编程(aspect-orientedprogramming,AOP)来对即便最不通明的遗留使用程序取得亘古未有的看法。
软件体系一般从一组无限的失掉优秀了解的需求入手下手。但是,跟着年夜多半乐成体系的演进,它们承当起愈来愈多的需求,表现在有数的功效和非功效性方面。在一个企业情况中,您终极很简单向这个凌乱的模块组合增加很多第三方库和框架,它们全都相互交互,并在体系一样平常事情的外表之下互相共同。实践上,用不了几年,最后具有很复杂、可办理的需求集的体系就会酿成庞然年夜物:难于把持和愚笨的代码。
因而步进这类情况的Java开辟职员就有了一个一样平常保护和改善的新义务。假如您就是这个开辟职员,那末您的第一个义务就是深入了解该体系的布局。了解布局将是加强体系和诊断不成制止会产生的成绩的关头。固然,第一次探求任何未知的体系都是提及来简单做起来难。在某些情形下,您可以征询本来的开辟职员,而在其他情形下却不克不及。可是即便可以找到本来的开辟团队,有些体系也会由于太甚复杂,而没法在没无机械匡助下熟习和了解它。
固然有很多可用的工具可以匡助您了解庞大的程序(请参阅参考材料),可是年夜多半工具都很高贵、进修起来很耗工夫,而且功效局限无限(也就是说,假如该工具没法满意必要,您将乞助无门)。在本文中,我将倡议一种替换的办法。面向方面编程是成熟的编程范型,它能够使用于普遍的编程场景,包含遗留使用程序的了解和保护。
请注重,本文假定您大抵熟习AspectJ之下的AOP,出格是AspectJ的静态和静态横切手艺。固然我将鄙人一节供应关于AOP横切的扼要概述,可是您应当参考参考材料,猎取更多信息。
整体概述
基于Java的AOP利用了天真而丰厚的表达言语,您可使用它以近乎无穷种体例来分化庞大的使用程序。基于Java的AOP的语法相似于Java言语,您应当很简单就会把握它。一旦把握,AOP就是一种具有很多使用的编程手艺。除了解遗留体系外部细节外,您还可使用AOP来非强迫性地重构和加强如许的体系。固然本文将完整利用AspectJ,不外这里会商的年夜多半手艺都可移植到其他盛行的基于Java的AOP完成,好比AspectWerkz和JBossAOP(请参阅参考材料)。
关于横切
任何使用程序都由多个功效性和体系性存眷点(concern)构成。功效性存眷点与使用程序的一样平常利用相干,而体系性存眷点则与体系的全体安康和保护相干。比方,一个银行使用程序的功效性存眷点包含账户保护和同意借/贷操纵,它的体系性存眷点包含平安、事件、功能和审计日记纪录。即便利用最好的编程办法学来开辟使用程序,您终极也会发明它的功效性和体系性存眷点会以超过多个使用程序模块的情势互相混同在一同。
横切是一种AOP手艺,用于确保自力的存眷点坚持模块化,同时仍旧充足天真地在全部使用程序中的分歧点使用。横切包含静态和静态两品种别。静态横切表现为经由过程在感乐趣的特定点织进(weavein)新的举动来改动对象的实行举动。静态横切同意我们经由过程注进(injectin)附加的办法和/或属性来间接改动对象的布局。
静态横切的语法与静态横切很不不异。以下术语合用于静态横切:
毗连点(joinpoint)是Java程序中的某个特定实行点,好比某个类中的一个办法。
切进点(pointcut)是特定于言语的布局,它暗示或捕获某个特定的毗连点。
关照(advice)是在抵达某个特定的切进点时要实行的一段代码(一般是一个横切功效)。
方面(aspect)是界说切进点和关照和它们之间的映照的一个布局。方面由AOP编译器用来在现有对象中的特定实行点织进附加功效。
本文中的一切代码演示都将使用静态横切。请参阅参考材料,取得关于静态横切的更多信息。
AspectJ之下的AOP
为了进修本文中的例子,您应当熟习以下特定于AspectJ之下的AOP的特征。
AspectJ供应一个名为ajc的编译器/字节代码织进器,它编译AspectJ和Java言语文件。ajc依据必要将方面交叉在一同,以发生与任何Java假造机(1.1或更高版本)相容的.class文件。
AspectJ撑持以下如许的方面,即这些方面划定某个特定的毗连点应当永久不会抵达。假如ajc历程判别出情形不是如许,它将收回一个编译时告诫或毛病(详细取决于该方面)。
使用程序和体系剖析
鄙人面几节中,您将进修两种利用AOP的分歧的使用程序和体系剖析机制。第一种机制我称之为静态剖析,它请求您做以下事变:
从CVS(或您所利用的其他任何代码版本把持体系)中把全部使用程序代码库签出到当地地区中。
修正天生文件(buildfile)以使其利用AspectJ编译器(ajc)。
在得当的地位包含方面(aspect)类。
实行一次完全的体系天生历程。
第二种机制我称之为静态剖析,它请求您不但在当地地区天生(build)体系,并且要运转该体系的详细用例(usecase),同时将运转时反射中的信息搜集到运转体系中。鄙人面几节中,我将具体会商每种机制,同时利用代码例子来讲明关头观点。
静态剖析
我将研讨很多对遗留使用程序实行静态剖析的手艺,并将它们使用于三种罕见的保护场景,以下所示:
评价接口变动所带来的影响。
辨认逝世的或不成抵达的代码。
创立松懈耦合。
上面就让我们入手下手吧!
评价接口变动所带来的影响
面向对象的遗留使用程序或体系应当包括很多模块,它们向客户端公然界说优秀的接口。假定您承受了一个整合新的需求的义务,这个义务必要改动现有的接口。因为不熟习代码,您起首面对的应战就是弄清改动这个接口将对体系的客户端带来的影响。为便利申明这里的例子,这里隐含的影响只不外就是复杂地改动每一个客户端,以使办法挪用切合改动后的署名。因而,关于断定完成新需求的最好办法和断定该需求关于给定的体系是不是值得来讲,晓得该接口的一切客户端大概是有匡助的。我将起首利用静态剖析来断定该接口的客户端,然后评价接口变动给体系带来的影响。
这类手艺基于如许一些方面,它们在发明特定的毗连点可达时收回编译时告诫。在这类情形下,您将编写毗连点来捕获针对该特定接口的一切办法的挪用。您必需起首在当地地区中提掏出使用程序代码库,修正天生文件以使其利用AspectJ编译器和在得当的地位包含方面类,然后运转体系的完全天生文件。假如准确地捕获到了毗连点,您预期会看到关于向代码库中的方针办法收回挪用的编译时告诫。
如清单1所示的编译时方面检测并显现挪用接口com.infosys.setl.apps.AppA.InterfaceA(及实在现)的一切类,同时实行(一个或多个)完成者(implementor)类自己。因而关于示例InterfaceA、它的完成者类ClassA和挪用者类ClassB,该方面将天生一个关于挪用ClassB中的a.methodA()的编译时告诫。
清单1.利用InterfaceA的类
packagecom.infosys.setl.apps.AppA;
publicinterfaceInterfaceA
{
publicStringmethodA();
publicintmethodB();
publicvoidmethodC();
}
packagecom.infosys.setl.apps.AppA;
publicclassClassAimplementsInterfaceA
{
publicStringmethodA()
{
return"Hello,World!";
}
publicintmethodB()
{
return1;
}
publicvoidmethodC()
{
System.out.println("Hello,World!");
}
}
packagecom.infosys.setl.apps.AppB;
importcom.infosys.setl.apps.AppA.*;
publicclassClassB
{
publicstaticvoidmain(String[]args)
{
try
{
InterfaceAa=
(InterfaceA)Class.forName("com.infosys.setl.apps.AppA.ClassA").newInstance();
System.out.println(a.methodA());
}
catch(Exceptione)
{
e.printStackTrace();
}
}
}
packagecom.infosys.setl.aspects;
publicaspectInterfaceCallersAspect
{
pointcutcallerMethodA():call(*com.infosys.setl.apps.AppA.InterfaceA.methodA(..))&&
!within(com.infosys.setl.apps.AppA.InterfaceA+);
pointcutcallerMethodB():call(*com.infosys.setl.apps.AppA.InterfaceA.methodB(..))&&
!within(com.infosys.setl.apps.AppA.InterfaceA+);
pointcutcallerMethodC():call(*com.infosys.setl.apps.AppA.InterfaceA.methodC(..))&&
!within(com.infosys.setl.apps.AppA.InterfaceA+);
declarewarning:callerMethodA():"CalltoInterfaceA.methodA";
declarewarning:callerMethodB():"CalltoInterfaceA.methodB";
declarewarning:callerMethodC():"CalltoInterfaceA.methodC";
}
接口变动对完成者类的影响可经由过程如清单2所示的方面来断定。关于前述清单中的示例类,这个方面为ClassA天生了一个编译时告诫。
清单2.完成InterfaceA的类
packagecom.infosys.setl.aspects;
publicaspectInterfaceImplAspect
{
pointcutimpls():staticinitialization(com.infosys.setl.apps.AppA.InterfaceA+)&&
!(within(com.infosys.setl.apps.AppA.InterfaceA));
declarewarning:impls():"InterfaceAImplementer";
}
辨认逝世代码
体系跟着加强哀求断断续续地到场而变得琐屑。每当对体系或使用程序的某个部分作出变动,分明如许的变动对其他部分的影响是很主要的。比方,重构模块A中的代码大概招致其他某个中央的代码(不论是某个类中的办法仍是全部类自己)酿成不成抵达(或逝世的),由于把持/数据流已被更新而绕过了它。对逝世代码充耳不闻,终极大概会由于体系中的未用代码过量而招致功能成绩。
侥幸的是,用于断定代码加强所带来的影响的不异手艺(如前一节所示)也可使用于辨认逝世的或不成达的代码。就像清单1所示的编译时方面可用于检测被挪用的接口中的一切办法一样,它们也能够指出那些不属于该接口的办法。这个接口中不会天生编译时告诫的任何办法都能够以为是逝世的,因此能够平安地删除。
创立松懈耦合
偶然您大概必需保护如许的遗留使用程序,即它是利用硬编码的挪用或针对特定组件而不是针对接口来开辟的。修正如许的体系(比方增加新的组件或替换组件)大概相称贫苦,也十分具有应战。侥幸的是,可使用AOP来从体系平分离特定的组件,并将它们交换为通用的、基于接口的组件。如许做确保了实践的完成者能够静态地拔出。
思索一下代码清单3所示的示例类。ClassA的methodA办法具有硬编码情势的日记纪录,个中的挪用是间接针对System.out收回的。
清单3.具有硬编码日记纪录挪用的类
packagecom.infosys.setl.apps.AppA;
publicclassClassA
{
publicintmethodA(intx)
{
System.out.println("Abouttodouble!");
x*=2;
System.out.println("Havedoubled!");
returnx;
}
publicstaticvoidmain(Stringargs[])
{
ClassAa=newClassA();
intret=a.methodA(2);
}
}
将现有的日记纪录挪用手动交换为针对通用日记纪录器接口的挪用明显很贫苦。更好的选择是利用如清单4所示的方面来查找和交换一切如许的挪用。然后能够经由过程LoggerInterface来供应通用的日记纪录器接口,工场类LoggerFactory可用于取得特定的日记纪录器实例(代码清单4中的SETLogger)。
清单4.交换硬编码挪用的通用接口
packagecom.infosys.setl.utils;
publicinterfaceLoggerInterface
{
publicvoidlog(StringlogMsg,Objecttarget);
}
packagecom.infosys.setl.utils;
publicclassLoggerFactory
{
privatestaticLoggerFactory_instance=null;
privateLoggerFactory(){}
publicstaticsynchronizedLoggerFactorygetInstance()
{
if(_instance==null)
_instance=newLoggerFactory();
return_instance;
}
publicLoggerInterfacegetLogger()
{
LoggerInterfacel=null;
try
{
l=(LoggerInterface)Class.forName("com.infosys.setl.utils.SETLogger").newInstance();
}
catch(Exceptione)
{
e.printStackTrace();
}
finally
{
returnl;
}
}
}
packagecom.infosys.setl.utils;
publicclassSETLoggerimplementsLoggerInterface
{
publicvoidlog(StringlogMsg,Objecttarget)
{
System.out.println(logMsg);
}
}
packagecom.infosys.setl.aspects;
importcom.infosys.setl.utils.*;
publicaspectUnplugAspect
{
voidaround(StringlogMsg,Objectsource):
call(voidjava.io.PrintStream.println(String))&&within(com.infosys.setl.apps.AppA.ClassA)&&
!within(com.infosys.setl.aspects..*)&&args(logMsg)&&target(source)
{
logger.log(logMsg,source);
}
privateLoggerInterfacelogger=LoggerFactory.getInstance().getLogger();
}
静态剖析
本节将研讨几种静态剖析手艺,然后一样将它们使用于熟习的保护场景。将利用静态剖析来办理的场景以下:
天生静态挪用图。
评价非常对一个体系的影响。
基于特定前提定制体系。
回想一下,静态AOP剖析提纲求将使用程序代码库签出到当地地区中,再修正天生文件以使其利用AspectJ编译器并在得当地位包含方面类,以后天生全部体系,然后运转体系所针对的用例。假定您准确地指定了感乐趣的毗连点,您将可以经由过程反射把丰厚的信息搜集到运转的使用程序中。
天生静态挪用图
单个用例稳定地遍历多个程序模块。经由过程在您的头脑中完全地运转一遍代码的实行序列,从而构成这类遍历路径的一种精力暗示情势,这类办法是常常利用的。明显,跟着遍历路径变得更长,记着一切这些信息就更坚苦,因而关于较年夜的体系来讲其实不有用。在本节中,您将进修怎样天生一个静态的挪用图,它是一个嵌套的跟踪轨迹,刻画了用例从开首运转到开头的过程当中,仓库帧在实行仓库上压进和弹出的历程。
在清单5中,您能够看到怎样利用一个编译时方面来静态天生一个反应用例实行流程的挪用图。
清单5.天生把持流图
packagecom.infosys.abhi;
publicclassClassA
{
publicvoidmethodA(intx)
{
x*=2;
com.infosys.abhi.ClassBb=newcom.infosys.abhi.ClassB();
b.methodB(x);
}
}
packagecom.infosys.abhi;
publicclassClassB
{
publicvoidmethodB(intx)
{
x*=2;
com.infosys.bela.ClassCc=newcom.infosys.bela.ClassC();
c.methodC(x);
}
}
packagecom.infosys.bela;
publicclassClassC
{
publicvoidmethodC(intx)
{
x*=2;
com.infosys.bela.ClassDd=newcom.infosys.bela.ClassD();
d.methodD(x);
}
}
packagecom.infosys.bela;
publicclassClassD
{
publicvoidmethodD(intx)
{
x*=2;
}
}
packagecom.infosys.setl.aspects;
publicaspectLogStackAspect
{
privateintcallDepth=0;
pointcutcgraph():!within(com.infosys.setl.aspects..*)&&execution(*com.infosys..*.*(..));
Objectaround():cgraph()
{
callDepth++;
logEntry(thisjoinpoint,callDepth);
Objectresult=proceed();
callDepth--;
logExit(thisjoinpoint,callDepth);
returnresult;
}
voidlogEntry(joinpointjp,intcallDepth)
{
StringBuffermsgBuff=newStringBuffer();
while(callDepth>0)
{
msgBuff.append("");
callDepth--;
}
msgBuff.append("->").append(jp.toString());
log(msgBuff.toString());
}
voidlogExit(joinpointjp,intcallDepth)
{
StringBuffermsgBuff=newStringBuffer();
while(callDepth>0)
{
msgBuff.append("");
callDepth--;
}
msgBuff.append("<-").append(jp.toString());
log(msgBuff.toString());
}
voidlog(Stringmsg)
{
System.out.println(msg);
}
}
Output:
=======
->execution(voidcom.infosys.abhi.ClassA.methodA(int))
->execution(voidcom.infosys.abhi.ClassB.methodB(int))
->execution(voidcom.infosys.bela.ClassC.methodC(int))
->execution(voidcom.infosys.bela.ClassD.methodD(int))
<-execution(voidcom.infosys.bela.ClassD.methodD(int))
<-execution(voidcom.infosys.bela.ClassC.methodC(int))
<-execution(voidcom.infosys.abhi.ClassB.methodB(int))
<-execution(voidcom.infosys.abhi.ClassA.methodA(int))
AspectJ还供应了一个名为cflowbelow的布局,它经由过程一个切进点来指出每一个被选择出的毗连点的把持流以后的毗连点(实行点)。经由过程利用这个布局,您能够把挪用图截取就任意深度。这关于以下情况是很有效的,个中把持流完全运转一个较年夜的轮回,招致不异的挪用图输入一次又一次地反复(这反过去又招致图的尺寸增年夜,从而下降了其合用性)。
评价非常的影响
一般,您必要调试一个临盆情况中发生的非常。如许的非常大概招致不良的影响,比方将仓库跟踪转储到用户界面上,大概没有开释诸如共享锁之类的争用性资本。
固然评价非常对全体体系的影响很主要,可是很难在开辟情况中摹拟某些非常。这多是由非常(好比收集非常)的性子决意的,大概因为开辟情况并非临盆情况的准确正本。比方,天生情况中因为数据库损坏而产生的非常,在不切实晓得损坏产生的详细地位的情形下就很难摹拟。捕获临盆数据库的快照以转移到开辟服务器上也大概没法完成。
除摹拟非常以便弄清成绩存在于使用程序中的那边以外,您还但愿可以测试代码修复,以确保它能准确地处置该成绩。除非能够在补钉代码中发生该非常并察看到它是怎样被处置的,不然这就是有成绩的。在本节中,您将看到怎样利用AOP手艺来摹拟由于挪用某个对象的办法而抛出非常的历程,而不用正确地从头创立招致该非常真正被抛出的运转时前提。
思索一下清单6所示的示例类com.infosys.setl.apps.AppA.ClassA。对这个类的methodA()的挪用大概在某些情况下招致抛出一个java.io.IOException。我们想晓得的是在抛出这个非常时methodA()(统一个清单中所示的类com.infosys.setl.apps.AppB.ClassB)的挪用者的举动。但是,您不但愿泯灭工夫和资本来创建发生某个详细非常的前提。
为办理这个成绩,可使用方面GenException的切进点genExceptionA来中止methodA()的实行,从而在运转时招致抛出一个java.io.IOException。然后您能够测试使用程序(这里暗示为ClassB)是不是可以按划定处置该非常,如清单6所示。(固然,您也能够将该关照修正为“after”关照,它能够在methodA()的实行以后实行,如切进点genExceptionAfterA所示。)
注重在实际场景中,ClassA多是诸如JDBC驱动程序之类的第三方库。
清单6.从运转的代码中天生非常
packagecom.infosys.abhi;
importjava.io.*;
publicclassClassA
{
publicvoidmethodA()throwsIOException
{
System.out.println("Hello,World!");
}
}
packagecom.infosys.bela;
publicclassClassB
{
publicstaticvoidmain(String[]args)
{
try
{
com.infosys.abhi.ClassAa=newcom.infosys.abhi.ClassA();
a.methodA();
com.infosys.abhi.ClassCc=newcom.infosys.abhi.ClassC();
System.out.println(c.methodC());
}
catch(java.io.IOExceptione)
{
e.printStackTrace();
}
catch(Exceptione)
{
e.printStackTrace();
}
}
}
packagecom.infosys.abhi;
publicclassClassC
{
publicStringmethodC()
{
System.out.println("Hello,World!");
return"Hi,World!";
}
}
packagecom.infosys.setl.aspects;
publicaspectGenException
{
pointcutgenExceptionA():
execution(publicvoidcom.infosys.abhi.ClassA.methodA()throwsjava.io.IOException);
pointcutgenExceptionC():
call(voidjava.io.PrintStream.println(String))&&
withincode(publicStringcom.infosys.abhi.ClassC.methodC());
pointcutgenExceptionAfterA():
call(publicvoidcom.infosys.abhi.ClassA.methodA()throwsjava.io.IOException);
voidaround()throwsjava.io.IOException:genExceptionA()
{
java.io.IOExceptione=newjava.io.IOException();
throwe;
}
after()throwsjava.io.IOException:genExceptionAfterA()
{
java.io.IOExceptione=newjava.io.IOException();
throwe;
}
after()throwsjava.lang.OutOfMemoryError:genExceptionC()
{
java.lang.OutOfMemoryErrore=newjava.lang.OutOfMemoryError();
throwe;
}
}
在相似的场景中,您大概碰到使用程序抛出未反省的非常(好比像NullPointerException如许的java.lang.RuntimeException的子类)或毛病(好比像OutOfMemoryError如许的java.lang.Error的子类)的情形。这两种非常范例都难于在开辟情况中摹拟。相反,您可使用切进点genExceptionC连同对应的after关照(如下面所示)来招致运转代码抛出一个OutOfMemory毛病,然后挪用ClassC的methodC()中的System.out.println()。
基于特定前提定制体系
一般,AOP在横切多个体系模块的存眷点情况中是幻想的。但是,关于仅会合于体系的特定部分的存眷点,AOP也有效武之地。比方,您大概必要对体系作出一些十分明白的变动,好比让主顾坚持兴奋,大概反应中央律例中的某个变动。有些变动乃至多是临时的(也就是说,它们必需在经由特定的工夫段以后从体系中删除)。
在如许的情形下,您大概会发明,因为以下这些缘故原由,对代码举行分支以便间接在代码中作出相干的加强大概没法完成:
您终极大概必需在CVS中办理具有分歧版本的多个代码分支。这是一个办理成绩,它增添了堕落的大概性。即便假定一切哀求的加强都是给整体产物偏向带来主动影响的无益特征,起首经由过程方面引进这些特征也会给开辟团队供应一试技艺的时机,而不用将变动提交给CVS。
假如必需从体系删除某个一时特征,那末假如该特征是间接引进代码中的,所必要的回回测试将是很耗资本而且很简单堕落的。另外一方面,假如该特征是经由过程AOP引进的,那末删除历程就只是从天生文件(buildfile)中扫除该方面以后从头编译体系。
侥幸的是,很简单经由过程方面织进如许的定制特征。织进历程能够在编译时举行(利用AspectJ),大概在加载时举行(利用AspectWerkz)。在计划方面时,服膺AOP完成大概具有关于情况公然(contextexposure)的固无限制(比方,AspectJ不同意经由过程实行情况向关照代码公然办法的当地变量)是很主要的。但是一样平常来说,利用方面来定制体系将带来更干净的完成,它使得断绝的存眷点与体系代码的基线版本更分明地分别。
停止语
在本文中,您进修了怎样利用面向方面编程来了解和非强迫性地保护年夜型的庞大遗留体系。本文分离两种剖析手艺和静态横切的壮大才能来办理很多罕见保护场景的成绩,同时夸大毛病诊断和非强迫性地加强现有代码。假如您卖力某个遗留使用程序的一样平常保护,那末这里展现的一切手艺都应当证实是很可贵的。
参考材料
能够从AspectJ项目主页下载AspectJ。
经由过程“利用面向Aspect的编程改善模块性”(developerWorks,2002年1月)取得关于AOP和AspectJ的深切先容。
经由过程AspectJ和仿照对象的测试天真性(developerWorks,2002年5月)懂得关于最出名的基于Java的AOP作风的更多信息。
“AOP办理严密耦合的困难”(developerWorks,2004年2月)是关于静态横切手艺的有用先容。
AspectWerkz是一个轻量级、开放源代码、基于Java的AOP完成。
JBossAOP是基于Java的AOP天下中的另外一个合作者。
Rigi是一个交互式的可视化工具(由加拿年夜年夜不列颠哥伦比亚省维多利亚年夜学盘算机迷信系的研讨职员开辟),它旨在匡助您更好地舆解软件,偏重新体例文档。
KlocworkInSight可用于间接从现有源代码(C、C++和Java代码)中提取软件计划的准确图形视图,以便周全懂得使用程序的布局和计划。
从developerWorksJava手艺专区能够找到关于Java编程各个方面的数百篇文章。
请会见DeveloperBookstore,取得手艺书本的具体列表,个中包含数百本Java相干的图书。
还能够参阅Java手艺专区教程主页,从developerWorks取得收费的Java相干教程的完全列表。
关于作者
AbhijitBelapurkar从印度德里市的印度理工学院取得了盘算机迷信方面的手艺学士学位。他在散布式使用程序的系统布局和平安方面具有近10年的履历,而且在利用Java平台构建N-层使用程序方面具有5年以上的履历。他以后在印度班加罗尔市的InfosysTechnologiesLimited担当J2EE方面的初级手艺架构师。
什么时候上述的三种开发工具能和三为一,什么时候java的竞争力才更强,才有机会拉拢更多的程序员投入到对java的开发上,因为到时的开发工具将会比.net的更简单。还有一点也很关键,什么时候java推出的jsf能成为真正意义上的标准。 |
|