仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 645|回复: 12
打印 上一主题 下一主题

[学习教程] JAVA网站制作之J2EE中利用Spring AOP框架和EJB组件

[复制链接]
柔情似水 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-18 11:13:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
由于这些智能化家电的市场需求没有预期的高,Sun放弃了该项计划。就在Oak几近失败之时,随着互联网的发展,Sun看到了Oak在计算机网络上的广阔应用前景,于是改造了Oak,j2ee  疾速开展的开辟职员社区、对各类后端手艺(包含JMS、JTA、JDO、Hibernate、iBATIS等等)的撑持,和(更加主要的)非侵进性的轻量级IoC容器和内置的AOP运转时,这些要素使得SpringFramework关于J2EE使用程序开辟非常具有吸引力。Spring托管的组件(POJO)能够与EJB共存,并同意利用AOP办法来处置企业使用程序中的横切方面――从监控和审计、缓存及使用程序级的平安性入手下手,直各处理特定于使用程序的营业需求。

  本文将向您先容Spring的AOP框架在J2EE使用程序中的实践使用。

  简介

  J2EE手艺为完成服务器端和两头件使用程序供应了坚固的基本。J2EE容器(好比BEAWebLogicServer)能够办理体系级的元素,包含使用程序性命周期、平安性、事件、远程把持和并发性,并且它能够包管为JDBC、JMS和JTA之类的罕见服务供应撑持。但是,J2EE的复杂和庞大性使开辟和测试变得非常坚苦。传统的J2EE使用程序一般严峻依附于经由过程容器的JNDI才可用的服务。这意味着必要大批间接的JNDI查找,大概要利用ServiceLocator形式,后者略微有所改善。这类架构进步了组件之间的耦合度,并使得独自测试某个组件成为几近不成能完成的事变。您能够浏览SpringFramework创立者所撰写的J2EEDevelopmentwithoutEJB一书,个中深切剖析了这类架构的缺点。

  借助于SpringFramework,能够将利用无格局Java对象完成的营业逻辑与传统的J2EE基本架构毗连起来,同时极年夜地削减了会见J2EE组件和服务所需的代码量。基于这一点,能够把传统的OO计划与正交的AOP组件化分离在一同。本文稍后将会演示怎样重构J2EE组件以使用Spring托管的Java对象,然后使用一种AOP办法来完成新特征,从而保护优秀的组件自力性和可测试性。

  与其他AOP工具比拟,Spring供应了AOP功效中的一个无限子集。它的方针是严密地集成AOP完成与SpringIoC容器,从而匡助办理罕见的使用成绩。该集成是以非侵进性的体例完成的,它同意在统一个使用程序中夹杂利用SpringAOP和体现力更强的框架,包含AspectJ。SpringAOP利用无格局Java类,不请求特别的编译历程、把持类装载器条理布局或变动部署设置,而是利用Proxy形式向应当由SpringIoC容器托管的方针对象使用关照。

  能够依据详细情形在两品种型的代办署理之间举行选择:

  • 第一类代办署理基于Java静态代办署理,只合用于接口。它是一种尺度的Java特征,可供应出色的功能。
  • 第二类代办署理可用于方针对象没有完成任何接口的场景,并且这类接口不克不及被引进(比方,关于遗留代码的情形)。它基于利用CGLIB库的运转时字节码天生。

  关于所代办署理的对象,Spring同意利用静态的(办法婚配基于切实称号或正则表达式,大概是正文驱动的)或静态的(婚配是在运转时举行的,包含cflow切进点范例)切进点界说指派特定的关照,而每一个切进点能够与一条或多条关照联系关系在一同。所撑持的关照范例有几种:围绕关照(aroundadvice),前关照(beforeadvice),前往后关照(afterreturningadvice),抛出非常后关照(afterthrowingadvice),和引进关照(introductionadvice)。本文稍后将给出围绕关照的一个例子。想要懂得更具体的信息,能够参考SpringAOP框架文档。

  正如先条件到的那样,只能够关照由SpringIoC容器托管的方针对象。但是,在J2EE使用程序中,组件的性命周期是由使用服务器托管的,并且依据集成范例,可使用一种罕见的端点范例把J2EE使用程序组件公然给远程或当地的客户端:

  • 无形态的、有形态的或实体bean,当地的或远程的(基于RMI-IIOP)
  • 监听当地或内部JMS行列和主题或进站JCA端点的动静驱动bean(MDB)
  • Servlet(包含Struts或其他终端用户UI框架、XML-RPC和基于SOAP的接口)


.罕见的端点范例

  要在这些端点上利用Spring的AOP框架,必需把一切的营业逻辑转移到Spring托管的bean中,然后利用服务器托管的组件来托付挪用,大概界说事件分别和平安高低文。固然本文不会商事件方面的成绩,可是能够在“参考材料”部分中找到相干文章。

  我将具体先容怎样重构J2EE使用程序以利用Spring功效。我们将利用XDoclet的基于JavaDoc的元数据来天生home和bean接口,和EJB部署形貌符。能够鄙人面的“下载”部分中找到本文中一切示例类的源代码。
<P>  重构EJB组件以利用Spring的EJB类

  想像一个复杂的股票报价EJB组件,它前往以后的股票买卖代价,并同意设置新的买卖代价。这个例子用于申明同时利用SpringFramework与J2EE服务的各个集成方面和最好理论,而不是要展现怎样编写股票办理使用程序。依照我们的请求,TradeManager营业接口应当就是上面这个模样:

publicinterfaceTradeManager{
publicstaticStringID="tradeManager";

publicBigDecimalgetPrice(Stringname);

publicvoidsetPrice(Stringname,BigDecimalprice);

}

  在计划J2EE使用程序的过程当中,一般利用远程无形态会话bean作为耐久层中的表面和实体bean。上面的TradeManager1Impl申明了无形态会话bean中TradeManager接口的大概完成。注重,它利用了ServiceLocator来为当地的实体bean查找home接口。XDoclet正文用于为EJB形貌符声明参数和界说EJB组件的已公然办法。

/**
*@ejb.bean
*name="org.javatx.spring.aop.TradeManager1"
*type="Stateless"
*view-type="both"
*transaction-type="Container"
*
*@ejb.transactiontype="NotSupported"
*
*@ejb.home
*remote-pattern="{0}Home"
*local-pattern="{0}LocalHome"
*
*@ejb.interface
*remote-pattern="{0}"
*local-pattern="{0}Local"
*/
publicclassTradeManager1ImplimplementsSessionBean,TradeManager{
privateSessionContextctx;

privateTradeLocalHometradeHome;

/**
*@ejb.interface-methodview-type="both"
*/
publicBigDecimalgetPrice(Stringsymbol){
try{
returntradeHome.findByPrimaryKey(symbol).getPrice();
}catch(ObjectNotFoundExceptionex){
returnnull;
}catch(FinderExceptionex){
thrownewEJBException("Unabletofindsymbol",ex);
}
}

/**
*@ejb.interface-methodview-type="both"
*/
publicvoidsetPrice(Stringsymbol,BigDecimalprice){
try{
try{
tradeHome.findByPrimaryKey(symbol).setPrice(price);
}catch(ObjectNotFoundExceptionex){
tradeHome.create(symbol,price);
}
}catch(CreateExceptionex){
thrownewEJBException("Unabletocreatesymbol",ex);
}catch(FinderExceptionex){
thrownewEJBException("Unabletofindsymbol",ex);
}
}


publicvoidejbCreate()throwsEJBException{
tradeHome=ServiceLocator.getTradeLocalHome();
}

publicvoidejbActivate()throwsEJBException,RemoteException{
}

publicvoidejbPassivate()throwsEJBException,RemoteException{
}

publicvoidejbRemove()throwsEJBException,RemoteException{
}

publicvoidsetSessionContext(SessionContextctx)throwsEJBException,RemoteException{
this.ctx=ctx;
}

}

  假如要在举行代码变动以后测试如许一个组件,那末在运转任何测试(一般是基于公用的容器内测试框架,好比Cactus或MockEJB)之前,必需要经由构建、启动容器和部署使用程序这全部周期。固然在复杂的用例中类的热部署能够节俭从头部署的工夫,可是当类形式变化(比方,增加域或办法,大概修正办法名)以后它就不可了。这个成绩自己就是把一切逻辑转移到无格局Java对象中的最好来由。正如您在TradeManager1Impl代码中所看到的那样,大批的粘和代码把EJB中的一切内容组合在一同,并且您没法从环绕JNDI会见和非常处置的复制事情中抽身。但是,Spring供应笼统的便当类,可使用定制的EJBbean对它举行扩大,而无需间接完成J2EE接口。这些笼统的超类同意移除定制bean中的年夜多半粘和代码,并且供应用于猎取Spring使用程序高低文的实例的办法。

  起首,必要把TradeManager1Impl中的一切逻辑都转移到新的无格局Java类中,这个新的类还完成了一个TradeManager接口。我们将把实体bean作为一种耐久性机制,这不但由于它超越了本文的会商局限,还由于WebLogicServer供应了大批用于调优CMPbean功能的选项。在特定的用例中,这些bean能够供应十分好的功能。我们还将利用SpringIoC容器把TradeImpl实体bean的home接口注进到TradeDao的机关函数中,您将从上面的代码中看到这一点:

publicclassTradeDaoimplementsTradeManager{
privateTradeLocalHometradeHome;

publicTradeDao(TradeLocalHometradeHome){
this.tradeHome=tradeHome;
}

publicBigDecimalgetPrice(Stringsymbol){
try{
returntradeHome.findByPrimaryKey(symbol).getPrice();
}catch(ObjectNotFoundExceptionex){
returnnull;
}catch(FinderExceptionex){
thrownewEJBException("Unabletofindsymbol",ex);
}
}

publicvoidsetPrice(Stringsymbol,BigDecimalprice){
try{
try{
tradeHome.findByPrimaryKey(symbol).setPrice(price);
}catch(ObjectNotFoundExceptionex){
tradeHome.create(symbol,price);
}
}catch(CreateExceptionex){
thrownewEJBException("Unabletocreatesymbol",ex);
}catch(FinderExceptionex){
thrownewEJBException("Unabletofindsymbol",ex);
}
}

}

  如今,可使用Spring的AbstractStatelessSessionBean笼统类重写TradeManager1Impl,该笼统类还能够匡助您取得下面所创立的TradeDaobean的一个Spring托管的实例:

/**
*@ejb.home
*remote-pattern="TradeManager2Home"
*local-pattern="TradeManager2LocalHome"
*extends="javax.ejb.EJBHome"
*local-extends="javax.ejb.EJBLocalHome"
*
*@ejb.transactiontype="NotSupported"
*
*@ejb.interface
*remote-pattern="TradeManager2"
*local-pattern="TradeManager2Local"
*extends="javax.ejb.SessionBean"
*local-extends="javax.ejb.SessionBean,org.javatx.spring.aop.TradeManager"
*
*@ejb.env-entry
*name="BeanFactoryPath"
*value="applicationContext.xml"
*/
publicclassTradeManager2ImplextendsAbstractStatelessSessionBeanimplementsTradeManager{
privateTradeManagertradeManager;

publicvoidsetSessionContext(SessionContextsessionContext){
super.setSessionContext(sessionContext);
//makesuretherewillbetheonlyoneSpringbeanconfig
setBeanFactoryLocator(ContextSingletonBeanFactoryLocator.getInstance());
}

publicvoidonEjbCreate()throwsCreateException{
tradeManager=(TradeManager)getBeanFactory().getBean(TradeManager.ID);
}

/**
*@ejb.interface-methodview-type="both"
*/
publicBigDecimalgetPrice(Stringsymbol){
returntradeManager.getPrice(symbol);
}

/**
*@ejb.interface-methodview-type="both"
*/
publicvoidsetPrice(Stringsymbol,BigDecimalprice){
tradeManager.setPrice(symbol,price);
}

}

  如今,EJB把一切挪用都托付给在onEjbCreate()办法中从Spring取得的TradeManager实例,这个办法是在AbstractEnterpriseBean中完成的,它处置一切查找和创立Spring使用程序高低文所需的事情。可是,必需在EJB部署形貌符中为EJB声明BeanFactoryPathenv-entry,以便将设置文件和bean声明的地位告知Spring。下面的例子利用了XDoclet正文来天生这些信息。

  别的还要注重,我们重写了setSessionContext()办法,以便告知AbstractStatelessSessionBean跨一切EJBbean利用Sping使用程序高低文的单个实例。

  如今,能够在applicationContext.xml中声明一个tradeManagerbean。基础上必要创立一个下面TradeDao的新实例,把从JNDI取得的TradeLocalHome实例传送给它的机关函数。上面给出了大概的界说:

<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEbeansPUBLIC"-//SPRING//DTDBEAN//EN""spring-beans.dtd">

<beans>

<beanid="tradeManager"class="org.javatx.spring.aop.TradeDao">
<constructor-argindex="0">
<beanclass="org.springframework.jndi.JndiObjectFactoryBean">
<propertyname="jndiName">
<beanid="org.javatx.spring.aop.TradeLocalHome.JNDI_NAME"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
</property>
<propertyname="proxyInterface"value="org.javatx.spring.aop.TradeLocalHome"/>
</bean>
</constructor-arg>
</bean>

</beans>

  在这里,我们利用了一个匿名界说的TradeLocalHome实例,这个实例是利用Spring的JndiObjectFactoryBean从JNDI取得的,然后把它作为一个机关函数参数注进到tradeManager中。我们还利用了一个FieldRetrievingFactoryBean来制止硬编码TradeLocalHome的实践JNDI称号,而是从静态的域(在这个例子中为TradeLocalHome.JNDI_NAME)猎取它。一般,利用JndiObjectFactoryBean时声明proxyInterface属性是一个不错的主张,如下面的例子所示。

  另有另外一种复杂的办法能够会见会话bean。Spring供应一个LocalStatelessSessionProxyFactoryBean,它同意立即取得一个会话bean而无需经由home接口。比方,上面的代码申明了怎样利用经由过程Spring托管的另外一个bean中的当地接口会见的MyComponentImpl会话bean:

<beanid="tradeManagerEjb"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<propertyname="jndiName">
<beanid="org.javatx.spring.aop.TradeManager2LocalHome.JNDI_NAME"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
</property>
<propertyname="businessInterface"value="org.javatx.spring.aop.TradeManager"/>
</bean>

  这类办法的长处在于,能够很简单地从当地接口切换到远程接口,只需利用SimpleRemoteStatelessSessionProxyFactoryBean修正Spring高低文中的一处bean声明便可。比方:

<beanid="tradeManagerEjb"
class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean">
<propertyname="jndiName">
<beanid="org.javatx.spring.aop.TradeManager2Home.JNDI_NAME"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
</property>
<propertyname="businessInterface"value="org.javatx.spring.aop.TradeManager"/>
<propertyname="lookupHomeOnStartup"value="false"/>
</bean>

  注重,lookupHomeOnStartupproperty被设置为false,以撑持提早初始化。

  上面,我总结一下到此为止所进修的内容:

  • 下面的重构已为利用初级的Spring功效(也就是依附性注进和AOP)奠基了基本。
  • 在没有修正客户端API的情形下,我把一切营业逻辑都移出表面会话bean,这就使得这个EJB不惧修正,并且易于测试。
  • 营业逻辑如今位于一个无格局Java对象中,只需该Java对象的依附性不必要JNDI中的资本,就能够在容器内部对其举行测试,大概可使用存根或仿照(mock)来取代这些依附性。
  • 如今,能够代进分歧的tradeManager完成,大概修正初始化参数和相干组件,而无需修正Java代码。

  至此,我们已完成了一切筹办步骤,能够入手下手办理对TradeManager服务的新需求了。
<P>  关照由Spring托管的组件

  在后面的内容中,我们重构了服务出口点,以便利用Spring托管的bean。如今,我将向您申明如许做将怎样匡助改善组件和完成新功效。

  起首,假定用户想看到某些标记的代价,而这些代价并不是由您的TradeManager组件所托管。换句话说,您必要毗连到一个内部服务,以便取得以后您不处置的所哀求标记确当前市场代价。您可使用雅虎流派中的一个基于HTTP的收费服务,可是实践的使用程序将毗连到供应及时数据的供给商(好比Reuters、Thomson、Bloomberg、NAQ等等)的及时数据更新服务(datafeed)。

  起首,必要创立一个新的YahooFeed组件,该组件完成了不异的TradeManager接口,然后从雅虎金融流派取得代价信息。天然的完成可使用HttpURLConnection发送一个HTTP哀求,然后利用正则表达式剖析呼应。比方:

publicclassYahooFeedimplementsTradeManager{
privatestaticfinalStringSERVICE_URL="http://finance.yahoo.com/d/quotes.csv?f=k1&s=";

privatePatternpattern=Pattern.compile(""(.*)-(.*)"");

publicBigDecimalgetPrice(Stringsymbol){
HttpURLConnectionconn;
StringresponseMessage;
intresponseCode;
try{
URLserviceUrl=newURL(SERVICE_URL+symbol);
conn=(HttpURLConnection)serviceUrl.openConnection();
responseCode=conn.getResponseCode();
responseMessage=conn.getResponseMessage();
}catch(Exceptionex){
thrownewRuntimeException("Connectionerror",ex);
}

if(responseCode!=HttpURLConnection.HTTP_OK){
thrownewRuntimeException("Connectionerror"+responseCode+""+responseMessage);
}

Stringresponse=readResponse(conn);
Matchermatcher=pattern.matcher(response);
if(!matcher.find()){
thrownewRuntimeException("Unabletoparseresponse["+response+"]forsymbol"+symbol);
}
Stringtime=matcher.group(1);
if("N/A".equals(time)){
returnnull;//unknownsymbol
}
Stringprice=matcher.group(2);
returnnewBigDecimal(price);
}

publicvoidsetPrice(Stringsymbol,BigDecimalprice){
thrownewUnsupportedOperationException("Cantsetpriceof3rdpartytrade");
}

privateStringreadResponse(HttpURLConnectionconn){
//...
returnresponse;
}

}
  完成这类完成并测试(在容器内部!)以后,就能够把它与其他组件举行集成。传统的做法是向TradeManager2Impl增加一些代码,以便反省getPrice()办法前往的值。这会使测试的次数最少增添一倍,并且请求为每一个测试用例设定附加的先决前提。但是,假如利用SpringAOP框架,就能够更大度地完成这项事情。您能够完成一条关照,假如初始的TradeManager没有前往所哀求标记的值,该关照将利用YahooFeed组件来猎取代价(在这类情形下,它的值是null,可是也大概会失掉一个UnknownSymbol非常)。

  要把关照使用到详细的办法,必要在Spring的bean设置中声明一个Advisor。有一个便利的类叫做NameMatchMethodPointcutAdvisor,它同意经由过程称号选择办法,在本例中还必要一个getPrice办法:

<beanid="yahooFeed"class="org.javatx.spring.aop.YahooFeed"/>

<beanid="foreignTradeAdvisor"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<propertyname="mappedName"value="getPrice"/>
<propertyname="advice">
<beanclass="org.javatx.spring.aop.ForeignTradeAdvice">
<constructor-argindex="0"ref="yahooFeed"/>
</bean>
</property>
</bean>

  正如您所看到的,下面的advisor指派了一个ForeignTradeAdvice给getPrice()办法。针对关照类,SpringAOP框架利用了AOPAllianceAPI,这意味着围绕关照的ForeignTradeAdvice应当完成MethodInterceptor接口。比方:

publicclassForeignTradeAdviceimplementsMethodInterceptor{
privateTradeManagertradeManager;

publicForeignTradeAdvice(TradeManagermanager){
this.tradeManager=manager;
}

publicObjectinvoke(MethodInvocationinvocation)throwsThrowable{
Objectres=invocation.proceed();
if(res!=null)returnres;

Object[]args=invocation.getArguments();
Stringsymbol=(String)args[0];
returntradeManager.getPrice(symbol);
}

}

  下面的代码利用invocation.proceed()挪用了一个原始的组件,并且假如它前往null,它将挪用另外一个在关照创立时作为机关函数参数注进的tradeManager。拜见下面foreignTradeAdvisorbean的声明。

  如今能够把在Spring的bean设置中界说的tradeManager从头定名为baseTradeManager,然后利用ProxyFactoryBean把tradeManager声明为一个代办署理。新的baseTradeManager将成为一个方针,我们将利用下面界说的foreignTradeAdvisor关照它:

<beanid="baseTradeManager"
class="org.javatx.spring.aop.TradeDao">
...sameastradeManagerdefinitionintheaboveexample
</bean>

<beanid="tradeManager"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="proxyInterfaces"value="org.javatx.spring.aop.TradeManager"/>
<propertyname="target"ref="baseTradeManager"/>
<propertyname="interceptorNames">
<list>
<idreflocal="foreignTradeAdvisor"/>
</list>
</property>
</bean>

  基础上,就是如许了。我们完成了附加的功效而没有修正原始的组件,并且仅利用Spring使用程序高低文来从头设置依附性。要想不借助于SpringAOP框架在典范的EJB组件中完成相似的修正,要末必需为EJB增加附加的逻辑(这会使其难以测试),要末必需利用decorator形式(实践上增添了EJB的数目,同时也进步了测试的庞大性,延伸了部署工夫)。在下面的例子中,您能够看到,借助于Spring,能够轻松地不修正现有组件而向这些组件增加附加的逻辑。如今,您具有的是几个轻量级组件,而不是严密耦合的bean,您能够自力测试它们,利用SpringFramework组装它们。注重,利用这类办法,ForeignTradeAdvice就是一个自包括的组件,它完成了本人的功效片段,能够看成一个自力单位在使用服务器内部举行测试,上面我将对此举行申明。
<P>  测试关照代码

  您大概注重到了,代码不依附于TradeDao或YahooFeed。如许就能够利用仿照对象完整自力地测试这个组件。仿照对象测试办法同意在组件实行之前声明希冀,然后考证这些希冀在组件挪用时代是不是失掉满意。要懂得有关仿照测试的更多信息,请拜见“参考材料”部分。上面我们将会利用jMock框架,该框架供应了一个天真且功效壮大的API来声明希冀。

  测试和实践的使用程序利用不异的Springbean设置是个不错的主张,可是关于特定组件的测试来讲,不克不及利用实践的依附性,由于这会损坏组件的伶仃性。但是,Spring同意在创立Spring的使用程序高低文时指定一个BeanPostProcessor,从而置换选中的bean和依附性。在这个例子中,可使用仿照对象的一个Map,这些仿照对象是在测试代码中创立的,用于置换Spring设置中的bean:

publicclassStubPostProcessorimplementsBeanPostProcessor{
privatefinalMapstubs;

publicStubPostProcessor(Mapstubs){
this.stubs=stubs;
}

publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName){
if(stubs.containsKey(beanName))returnstubs.get(beanName);
returnbean;
}

publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName){
returnbean;
}

}
  在测试用例类的setUp()办法中,我们将利用baseTradeManager和yahooFeed组件的仿照对象来初始化StubPostProcessor,而这两个组件是利用jMockAPI创立的。然后,我们就能够创立ClassPathXmlApplicationContext(设置其利用BeanPostProcessor)来实例化一个tradeManager组件。发生的tradeManager组件将利用仿照后的依附性。

  这类办法不但同意伶仃要测试的组件,还能够确保在Springbean设置中准确界说关照。实践上,要在不摹拟大批容器基本架构的情形下利用如许的办法来测试在EJB组件中完成的营业逻辑是不成能的:

publicclassForeignTradeAdviceTestextendsTestCase{
TradeManagertradeManager;
privateMockbaseTradeManagerMock;
privateMockyahooFeedMock;

protectedvoidsetUp()throwsException{
super.setUp();

baseTradeManagerMock=newMock(TradeManager.class,"baseTradeManager");
TradeManagerbaseTradeManager=(TradeManager)baseTradeManagerMock.proxy();

yahooFeedMock=newMock(TradeManager.class,"yahooFeed");
TradeManageryahooFeed=(TradeManager)yahooFeedMock.proxy();v
Mapstubs=newHashMap();
stubs.put("yahooFeed",yahooFeed);
stubs.put("baseTradeManager",baseTradeManager);

ConfigurableApplicationContextctx=newClassPathXmlApplicationContext(CTX_NAME);
ctx.getBeanFactory().addBeanPostProcessor(newStubPostProcessor(stubs));

tradeManager=(TradeManager)proxyFactory.getProxy();
}
...
  在实践的testAdvice()办法中,能够为仿照对象指按期看并考证(比方)baseTradeManager上的getPrice()办法是不是前往null,然后yahooFeed上的getPrice()办法也将被挪用:

publicvoidtestAdvice()throwsThrowable{
Stringsymbol="testSymbol";
BigDecimalexpectedPrice=newBigDecimal("0.222");

baseTradeManagerMock.expects(newInvokeOnceMatcher()).method("getPrice")
.with(newIsEqual(symbol)).will(newReturnStub(null));

yahooFeedMock.expects(newInvokeOnceMatcher()).method("getPrice")
.with(newIsEqual(symbol)).will(newReturnStub(expectedPrice));

BigDecimalprice=tradeManager.getPrice(symbol);
assertEquals("Invalidprice",expectedPrice,price);
baseTradeManagerMock.verify();
yahooFeedMock.verify();
}
  这段代码利用jMock束缚来指定,baseTradeManagerMock希冀只利用一个即是symbol的参数挪用getPrice()办法一次,并且此次挪用将前往null。相似地,yahooFeedMock也希冀对统一办法只挪用一次,可是前往expectedPrice。这同意在setUp()办法中运转所创立的tradeManager组件,并断言前往的了局。

  这个测试用例很简单参数化,从而涵盖一切大概的用例。注重,当组件抛出非常时,能够很简单地声明希冀。

测试baseTradeManageryahooFeed希冀挪用前往抛出挪用前往抛出了局t非常1true0.22-false--0.22-2true-e1false---e13truenull-true0.33-0.33-4truenull-truenull-null-5truenull-true-e2-e2<P>  可使用这个表更新测试类,使其利用一个涵盖了一切大概场景的参数化序列:

...publicstaticTestSuitesuite(){
BigDecimalv1=newBigDecimal("0.22");
BigDecimalv2=newBigDecimal("0.33");
RuntimeExceptione1=newRuntimeException("e1");
RuntimeExceptione2=newRuntimeException("e2");
TestSuitesuite=newTestSuite(ForeignTradeAdviceTest.class.getName());
suite.addTest(newForeignTradeAdviceTest(true,v1,null,false,null,null,v1,null));
suite.addTest(newForeignTradeAdviceTest(true,null,e1,false,null,null,null,e1));
suite.addTest(newForeignTradeAdviceTest(true,null,null,true,v2,null,v2,null));
suite.addTest(newForeignTradeAdviceTest(true,null,null,true,null,null,null,null));
suite.addTest(newForeignTradeAdviceTest(true,null,null,true,null,e2,null,e2));
returnsuite;
}

publicForeignTradeAdviceTest(
booleanbaseCall,BigDecimalbaseValue,ThrowablebaseException,
booleanyahooCall,BigDecimalyahooValue,ThrowableyahooException,
BigDecimalexpectedValue,ThrowableexpectedException){
super("test");

this.baseCall=baseCall;
this.baseWill=baseException==null?
(Stub)newReturnStub(baseValue):newThrowStub(baseException);
this.yahooCall=yahooCall;
this.yahooWill=yahooException==null?
(Stub)newReturnStub(yahooValue):newThrowStub(yahooException);
this.expectedValue=expectedValue;
this.expectedException=expectedException;
}

publicvoidtest()throwsThrowable{
Stringsymbol="testSymbol";

if(baseCall){
baseTradeManagerMock.expects(newInvokeOnceMatcher())
.method("getPrice").with(newIsEqual(symbol)).will(baseWill);
}

if(yahooCall){
yahooFeedMock.expects(newInvokeOnceMatcher())
.method("getPrice").with(newIsEqual(symbol)).will(yahooWill);
}

try{
BigDecimalprice=tradeManager.getPrice(symbol);
assertEquals("Invalidprice",expectedValue,price);
}catch(Exceptione){
if(expectedException==null){
throwe;
}
}
baseTradeManagerMock.verify();
yahooFeedMock.verify();
}

publicStringgetName(){
returnsuper.getName()+""+
baseCalled+""+baseValue+""+baseException+""+
yahooCalled+""+yahooValue+""+yahooException+""+
expectedValue+""+expectedException;
}...
  在更庞大的情形下,下面的测试办法能够很简单地扩大为年夜很多的输出参数汇合,并且它仍旧会立即运转且易于办理。别的,把一切参数移进一个内部设置文件大概乃至Excel电子表格是公道的做法,这些设置文件或电子表格能够由QA团队办理,大概间接依据需求天生。
<P>  组合和链接关照

  我们已利用了一个复杂的拦阻器关照来完成附加的逻辑,而且将其看成一个自力的组件举行了测试。当应当在不举行修正而且与其他组件没有附加耦合的情形下扩大大众实行流时,这类计划非常无效。比方,当代价已产生变更时,假如必要利用JMS或JavaMail发送关照,我们能够在tradeManagerbean的setPrice办法上注册另外一个拦阻器,并利用它来向相干组件关照有关这些变更的情形。在良多情形下,这些方面都合用于非功效性需求,好比很多AOP相干的文章和教程中常常用作“helloworld”例子的跟踪、登录或监控。

  另外一个传统的AOP使用程序是缓存。比方,一个基于CMP实体bean的TradeDao组件将从WebLogicServer供应的缓存功效中受害。但是关于YahooFeed组件来讲却并不是云云,由于它必需经由过程Internet毗连到雅虎流派。这分明是一个应当使用缓存的地位,并且它还同意削减内部毗连的次数,并终极下降全部体系的负载。注重,基于停止工夫的缓存也会在革新信息时带来一些提早,可是在良多情形下,它仍旧是能够承受的。要使用缓存功效,能够界说一个yahooFeedCachingAdvisor,它将把CachingAdvice附加到yahooFeedbean上的getPrice()办法。在“下载”部分中,您能够找到一个CachingAdvice完成的例子。

<beanid="getPriceAdvisor"abstract="true"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<propertyname="mappedName"value="getPrice"/>
</bean>

<beanid="yahooFeedCachingAdvisor"parent="getPriceAdvisor">
<propertyname="advice">
<beanclass="org.javatx.spring.aop.CachingAdvice">
<constructor-argindex="0"ref="cache"/>
</bean>
</property>
</bean>
  由于getPrice()办法已成为几种关照的大众联合点,以是声明一个笼统的getPriceAdvisorbean,然后在yahooFeedCachingAdvisor中对其举行扩大,指定详细的关照CachingAdvice。注重,也能够修正后面的foreignTradeAdvisor,使其利用统一个getPriceAdvisor父bean。

  如今能够更新yahooFeedbean的界说,并将它包装在一个ProxyFactoryBean中,然后利用yahooFeedCachingAdvisor关照它。比方:

<beanid="yahooFeed"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="proxyInterfaces"value="org.javatx.spring.aop.TradeManager"/>
<propertyname="target">
<beanclass="org.javatx.spring.aop.YahooFeed">
</property>
<propertyname="interceptorNames">
<list>
<value>yahooFeedCachingAdvisor</value>
</list>
</property>
</bean>
  当哀求射中已保留在缓存中的数据时,下面的修正将极年夜地进步功能,可是假如传进多个针对统一个标记的哀求,而该标记还没有进进缓存大概已到期,我们将看到多个并发的哀求抵达服务供应者,哀求统一个标记。对此,存在一种不言而喻的优化,就是中止对统一个标记的一切哀求,直到第一个哀求完成为止,然后利用第一个哀求取得的了局。EJB标准(拜见“ProgrammingRestrictions”,2.1版本的25.1.2部分)一样平常不保举利用这类办法,由于它对运转在多个JVM上的集群情况不见效。但是,最少在单个的节点中这类优化能够改善功能。所示的图表对照申明了优化之前和优化以后的情形:


.优化之前和优化以后

  该优化也能够完成为关照,并增加在yahooFeedbean中的拦阻器链的末了:

...
<propertyname="interceptorNames">
<list>
<idreflocal="yahooFeedCachingAdvisor"/>
<idreflocal="syncPointAdvisor"/>
</list>
</property>
  实践的拦阻器完成应当像上面如许:

publicclassSyncPointAdviceimplementsMethodInterceptor{
privatelongDEFAULT_TIMEOUT=10000L;

privateMaprequests=Collections.synchronizedMap(newHashMap());

publicObjectinvoke(MethodInvocationinvocation)throwsThrowable{
Stringsymbol=(String)invocation.getArguments()[0];
Object[]lock=(Object[])requests.get(symbol);
if(lock==null){
lock=newObject[1];
requests.put(symbol,lock);
try{
lock[0]=invocation.proceed();
returnlock[0];
}finally{
requests.remove(symbol);
synchronized(lock){
lock.notifyAll();
}
}
}

synchronized(lock){
lock.wait(DEFAULT_TIMEOUT);
}
returnlock[0];
}

}
  能够看出,关照代码相称复杂,并且不依附于其他的组件,这使得JUnit测试变得非常复杂。在“参考材料”部分,您能够找到SyncPointAdvice的JUnit测试的完全源代码。关于庞大的并发场景来讲,利用Java5中java.util.concurrent包的同步机制大概针对老的JVM利用其backport是一种不错的做法。

  停止语

  本文先容了一种把J2EE使用程序中的EJB转换为Spring托管组件的办法,和转换以后能够接纳的壮大手艺。它还给出了几个实践的例子,申明怎样借助于Spring的AOP框架、使用面向方面的办法来扩大J2EE使用程序,并在不修正现有代码的情形下完成新的营业需求。

  在EJB中利用SpringFramework将削减代码间的耦合,并使很多壮大的功效立即失效,从而进步可扩大性和天真性。这还使得使用程序的单个组件变得加倍易于测试,包含新引进的AOP关照和拦阻器,它们用于完成营业功效大概处置非功效性的需求,好比跟踪、缓存、平安性和事件。
到时我们不用学struts,不用学spring,不用学Hibernate,只要能把jsf学会了,完全可以替代所有的框架,包括AJAX,都知道AJAX并不是新技术,虽说我没深入学习jsf但我认为jsf应该已经能通过其它技术替代AJAX,实现无缝刷新。
只想知道 该用户已被删除
沙发
发表于 2015-1-20 17:29:43 | 只看该作者
是一种使网页(Web Page)产生生动活泼画面的语言
若相依 该用户已被删除
板凳
发表于 2015-1-20 21:39:37 | 只看该作者
有时间再研究一下MVC结构(把Model-View-Control分离开的设计思想)
柔情似水 该用户已被删除
地板
 楼主| 发表于 2015-1-30 05:45:36 | 只看该作者
学Java必读的两个开源程序就是Jive和Pet Store.。 Jive是国外一个非常著名的BBS程序,完全开放源码。论坛的设计采用了很多先进的技术,如Cache、用户认证、Filter、XML等,而且论坛完全屏蔽了对数据库的访问,可以很轻易的在不同数据库中移植。论坛还有方便的安装和管理程序,这是我们平时编程时容易忽略的一部份(中国程序员一般只注重编程的技术含量,却完全不考虑用户的感受,这就是我们与国外软件的差距所在)。
5#
发表于 2015-2-6 07:36:50 | 只看该作者
Java自面世后就非常流行,发展迅速,对C++语言形成了有力冲击。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于个人PC、数据中心、游戏控制台
蒙在股里 该用户已被删除
6#
发表于 2015-2-10 09:35:45 | 只看该作者
不过,每次的执行编译后的字节码需要消耗一定的时间,这同时也在一定程度上降低了 Java 程序的运行效率。
简单生活 该用户已被删除
7#
发表于 2015-3-1 07:43:16 | 只看该作者
Pet Store.(宠物店)是SUN公司为了演示其J2EE编程规范而推出的开放源码的程序,应该很具有权威性,想学J2EE和EJB的朋友不要 错过了。
精灵巫婆 该用户已被删除
8#
发表于 2015-3-10 12:55:04 | 只看该作者
是一种语言,用以产生「小应用程序(Applet(s))
海妖 该用户已被删除
9#
发表于 2015-3-11 06:40:18 | 只看该作者
Java是一种计算机编程语言,拥有跨平台、面向对java
小女巫 该用户已被删除
10#
发表于 2015-3-11 18:15:04 | 只看该作者
吧,现在很流行的Structs就是它的一种实现方式,不过Structs用起来实在是很繁,我们只要学习其精髓即可,我们完全可以设计自己的MVC结构。然后你再研究一下软件Refactoring (重构)和极限XP编程,相信你又会上一个台阶。 做完这些,你不如整理一下你的Java代码,把那些经典的程序和常见的应用整理出来,再精心打造一番,提高其重用性和可扩展性。你再找几个志同道合的朋友成立一个工作室吧
小魔女 该用户已被删除
11#
发表于 2015-3-12 00:32:20 | 只看该作者
你就该学一学Servlet了。Servlet就是服务器端小程序,他负责生成发送给客户端的HTML文件。JSP在执行时,也是先转换成Servlet再运行的。虽说JSP理论上可以完全取代Servlet,这也是SUN推出JSP的本意,可是Servlet用来控制流程跳转还是挺方便的,也令程序更清晰。接下来你应该学习一下Javabean了,可能你早就看不管JSP在HTML中嵌Java代码的混乱方式了,这种方式跟ASP又有什么区别呢?
冷月葬花魂 该用户已被删除
12#
发表于 2015-3-12 10:19:03 | 只看该作者
让你能够真正掌握接口或抽象类的应用,从而在原来的Java语言基础上跃进一步,更重要的是,设计模式反复向你强调一个宗旨:要让你的程序尽可能的可重用。
小妖女 该用户已被删除
13#
发表于 2015-3-19 19:57:57 | 只看该作者
所以现在应用最广泛又最好学的就是J2EE了。 J2EE又包括许多组件,如Jsp,Servlet,JavaBean,EJB,JDBC,JavaMail等。要学习起来可不是一两天的事。那么又该如何学习J2EE呢?当然Java语法得先看一看的,I/O包,Util包,Lang包你都熟悉了吗?然后再从JSP学起。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-11-15 11:43

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表