Spring是目前最流行的JavaEE Framework,但是使用Spring的Spring-WS開發WebService卻十分繁瑣。XFire是一個簡化WebService開發的開源項目,通過Spring和XFire的結合可以大大簡化基于Spring Framework的應用中的WebService開發。
Spring和XFire可以通過多種方式結合,下文介紹的是筆者常用的一種簡單而實用的方法。所用的Spring版本為2.0,XFire版本為1.2.6。
1 配置XFire Servlet
在web.xml中加入如下配置:
<servlet>
<servlet-name>XFireServlet</servlet-name>
<servlet-class>
org.codehaus.xfire.spring.XFireSpringServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/servlet/XFireServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
2 配置Spring的監聽器,同基于spring的Web項目一樣Spring的監聽器是必不可少的。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:org/codehaus/xfire/spring/xfire.xml,
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
以下是完整的web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:org/codehaus/xfire/spring/xfire.xml,
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>XFireServlet</servlet-name>
<servlet-class>
org.codehaus.xfire.spring.XFireSpringServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/servlet/XFireServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
3 定義接口及實現服務
定義接口,這個接口中定義要通過WebService暴露的方法
package org.ccsoft;
publicinterface HelloWS {
public String sayHello(String sb);
}
實現服務
package org.ccsoft;
publicclass HelloWSImp implements HelloWS {
public String sayHello(String sb) {
// TODO Auto-generated method stub
return"Hello "+sb;
}
}
4 配置服務
將上文中實現的服務,加入到spring的配置文件中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="helloWS" class="org.ccsoft.HelloWSImp"/>
<bean name="helloService" class="org.codehaus.xfire.spring.ServiceBean">
<property name="serviceBean" ref="helloWS"/>
<property name="serviceClass" value="org.ccsoft.HelloWS"/>
<property name="inHandlers">
<list>
<ref bean="addressingHandler"/>
</list>
</property>
</bean>
<bean id="addressingHandler" class="org.codehaus.xfire.addressing.AddressingInHandler"/>
</beans>
好了現在你可以通過http://127.0.0.1:8080/XFireWS1/services/HelloWS?wsdl來驗證是否部署成功了。
—————————————————————————————————————
蔡 超
SCEA , SCBCD , MCSD
IBM Certified Specialist RUP
IBM Certified Solution Designer OOA&D UML v2
北京天融信軟件架構師
SUN,Microsoft培訓中心特邀高端教師
常年提供架構咨詢服務
chaocai2001@yahoo.com.cn , 010-82776427
public class HibernateTestBean implements SessionBean {
SessionContext sessionContext;
SessionFactory sf;
public void setSessionContext(SessionContext sessionContext) {
this.sessionContext = sessionContext;
try {
InitialContext ctx = new InitialContext();
sf=(SessionFactory) ctx.lookup("java:/hibernate/SessionFactory");
} catch (NamingException ex) {
ex.printStackTrace();
}
}
public void tran(){
tran1();
tran2();
}
public void tran1() {
Session session=sf.getCurrentSession();
Message msg=new Message();
msg.setCreateTime(new Date());
msg.setDetail("trans1");
session.save(msg);
System.out.println("Session:"+session.hashCode());
session.flush();
session.close();
}
public void tran2() {
Session session=sf.getCurrentSession();
Message msg=new Message();
msg.setCreateTime(new Date());
msg.setDetail("trans2");
session.save(msg);
System.out.println("Session:"+session.hashCode());
// throw new RuntimeException("wrong");
}
……
}
注:EJB采用CMT,各方法的事務屬性是required
客戶端調用tran以上代碼可以正確運行嗎?
如果把tran1中的sf.getCurrentSession();改為sf.openSession()可以正確運行嗎?
辨析:
1 上述代碼是不能正確運行的,運行tran2時會拋出異常,告訴你session is closed.
其實這是應為getCurrentSession()會使用環境已有的Session,同時注意getCurrentSession()要在事務的環境中使用。
這是也許你一定會問,那么什么時候關閉Session呢?答案是事務完成的時候(提交或是回滾)。
2 如果上述代碼tran1中的sf.getCurrentSession();改為sf.openSession()代碼將可以正確運行。這是因為openSession()每次都會返回一個新的Session。而在tran2種的sf.getCurrentSession()并不會使用tran1中的session,而是會使用當前事務環境中的默認的session.
也許你會問如果tran2種的調用拋出RuntimeException,tran1所作的操作還可以回滾嗎?
答案是仍然可以回滾的.
蔡超
北京天融信 軟件架構師
SCEA,SCBCD,MCSD
IBM Certified Specialist RUP v2003
IBM Certified Solution Designer OOA&D UMLv2
蔡 超
SCEA,SCBCD,MCSD,IBM RUP Specilist
北京天融信軟件架構師
SUN,Microsoft培訓中心特邀高端教師
常年提供架構咨詢服務
chaocai2001@yahoo.com.cn ,010-82776427
問題:
在spring+hibernate的常見架構中,常會應為hibernate的延遲加載遇到一些麻煩。如Hibernate的引入使用脫管領域對象直接取代了DTO,然而前臺組織顯示時常會應為脫管領域對象的一些關系域未被加載而拋出異常(其實延遲加載是優化系統性能的一種有效方式)。
為了使顯示層正常工作,我們就必須在業務層顯式的加載這些表現層會用到的延遲加載的關系域對象。而這樣的工作不僅需要額外的代碼,并且這些代碼往往也與所進行的業務邏輯無關。并且表現層的變化較多這樣一來如果上述代碼進入業務層就會導致業務層跟隨表現層的變化。
解決方案:
在“Spring構建應用系統的最佳架構與模式實踐(1)”中將邏輯層分為了Façade和ApplicationService兩層。
Façade的方法組織是針對客戶端請求的,所以如果我們把業務邏輯封裝在ApplicationService對象中,而把脫管對象產生(包括根據表現層初始化延遲加載對象,關閉Session)。這樣便可以有效的防止表現層邏輯混入業務邏輯中。
蔡 超
SCEA,SCBCD,MCSD,IBM RUP Specilist
北京天融信軟件架構師
SUN,Microsoft培訓中心特邀高端教師
常年提供架構咨詢服務
chaocai2001@yahoo.com.cn ,010-82776427
引言
在使用Spring構建應用時和采用EJB構建應用一樣同樣也存在不少常用模式和最佳實踐,當然很多Core J2EE Pattern仍然是我們構建spring應用中的優秀模式,不過有的也不再適用了(如:IoC的應用使得Service Locator不再適用,Hibernate取代Entity Bean使得DTO不再適用等)。
下文是一些在以Spring為核心的架構中的常見模式和架構最佳實踐。

圖表 1 Spring應用常見架構模式列表
DAO模式
雖然采用了諸如Hibernate這樣的O/R Mapping工具或是iBates這樣的SQL Mapping工具但是采用DAO模式仍有相當好處。
在實際應用中我們常會遇到如下問題:
1 性能問題:由于性能問題我們可能要改變數據訪問方式,如采用JDBC替換Hibernate這時,這時如果采用了DAO模式,數據訪問被有效的封裝和業務邏輯完全分離,易于實現替換。
2 移植問題:需要支持多種數據庫,DAO模式仍然是一種穩妥的選擇。雖然諸如Hibernate及iBates也提供很好的數據庫無關性,但如果使用某些數據庫的特殊功能時,就會出現問題。
當然,對于是否采用DAO模式也不可一概而論,應為他畢竟會增加應用的開發復雜性。個人認為很關鍵的一個判定條件是業務邏輯是否會和持久化邏輯混合。
Application Service模式
封裝一定的業務邏輯和功能,提高復用性,即防止Façade和業務對象的臃腫。注意在此可以應用Command模式及Strategy模式提供系統可維護性和可擴展性。
這個模式很重要,但常常被大家忽略,這時應為我們常會把邏輯放在Façade中,其實這是很錯誤的。例如:我們可能會提供多種不同Façade以適應不同的訪問方式,這樣就會出現大量重復的業務邏輯代碼。
Façade模式
為客戶端提供訪問業務組件的統一模式。便于實現對不同訪問方式的支持(如:遠程或本地)。特別在遠程調用時通過暴露粗粒度的接口,提高系統的性能。
同時,可以根據客戶的不同,通過不同Façade來控制客戶的操作。
目前,很多人都認為
spring
不論在那個方面都會比
SLSB
有更高的效率,真的是這樣嗎?
spring
中的
POJO
的生命周期可以是
Singlton
或每請求創建(或是
2.0
支持的
session
及
application,request
等范圍),
SLSB
是通過實例池經心管理的。如果
spring POJO
不采用
singlton
的形式那么就需要承受創建和銷毀
POJO
的消耗,當然
SLSB
的出池和入池同樣會有同步的消耗,由于現在的虛擬機對象的創建和消耗速度大幅提高所以不一定比
SLSB
獲取的速度慢,但是如果每個對象構建的資源消耗很大如總是需要構建或初始化復雜對象,那么
SLSB
的速度顯然有優勢。如果
spring
采用
singleton
模式,那么其中如果需要同步,則雖然省去了創建和銷毀的消耗,但是大量的同步會使性能的殺手。并且在多
CPU
的服務器上沒有同步的多線程并行效果更好(可以在不同的處理器上單獨運行)
1 MDB可以通過@Timeout或實現java.ejb.TimedObject
來實現定時回調
2 EJB3
規范要求在部署
EJB
時必須綁定到各業務接口的全限定名上,最終可有
JNDI
查找
如:
ctx.lookup(TaxRate.class.getName());
3? @Remove
表示
SFSB
中的刪除方法,如果存在
@PreDestory
則在其后執行
4
會話
Bean
不實現
SessionBean
接口,
MDB
不必實現
MessageDerivenBean
,只要通過
@Annotation
標明或在部署文件中說明。
5 EJBContext
中加入了
lookup
方法,用于查找和
Bean
綁帶的
JNDI
名
蔡
超
SCEA
,
SCBCD
,
MCSD
北京天融信軟件架構師
SUN,Microsoft
培訓中心特邀高端教師
常年提供架構咨詢服務
chaocai2001@yahoo.com.cn
,
010-82776427
很多設計模式的書中都用這樣的語言來描述
”
橋模式
”(GOF 95) –
“把抽象與實現分開”(
”Prefactoring”, Ken Pugh,2006
)
,
這樣的描述實在有些讓人很難體會該模式的精髓。其實在我看來橋模式就是一種面向對象技術中“極度分割”
思想的體現。
下面看一個常見的說明橋模式的例子:
1
一個可以以不同方式輸出日志的工具類(輸出到文件或控制臺):
2
如果此時我們要求可以以多種不同格式來輸出日志(如:
XML
和普通文本格式),為了適應這一需求我調整一下類的設計
設計一:
?
從以上類圖可以看出這一設計會產生大量的類,分析其原因可以發現導致這一問題產生的是根類涉及了太多方面,如果這些方面都同時進行擴展就會形成復雜的繼承
(
層次較深
)
。如果我們把這些可擴展的不同方面進行分割,就會得到如下設計
以上設計正是傳說中的“橋模式”。
經過以上分析我們可看到橋模式本質就是“極度分割”思想的一種體現。
蔡超
(
SCEA
,
MCSD
,
IBM RUP Specialist
)
Spring Reference
中介紹如何在采用
@AspectJ
方式在剖面中如何獲取
target
和
JoinPoint
并給出了示例,但并沒有給出采用
XML
配置方式時介紹及示例,下面附上一個簡單的小例子供大家參考。
package aop;
?
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
?
/**
?* @author Administrator
?*
?* TODO To change the template for this generated type comment go to
?* Window - Preferences - Java - Code Style - Code Templates
?*/
public class LogAdvice1 {
?????? public void log(JoinPoint jp,MathImp imp){
?
????????????? System.out.println("log:"+imp+" "+jp.toLongString());
?????????????
?????? }
}
?
/*
?* Created on 2006-11-1
?*
?* TODO To change the template for this generated file go to
?* Window - Preferences - Java - Code Style - Code Templates
?*/
package aop;
?
/**
?* @author Administrator
?*
?* TODO To change the template for this generated type comment go to
?* Window - Preferences - Java - Code Style - Code Templates
?*/
public class MathImp /*implements Math*/{
?
?????? /* (non-Javadoc)
??????
?* @see aop.Math#add(int, int)
??????
?
*/
?????? public void add(int op1, int op2) {
?????????????
// TODO Auto-generated method stub
?????????????
?????? }
?
?????? /* (non-Javadoc)
??????
?* @see aop.Math#addtest(int, int)
??????
?*/
?????? public void addtest(int op1, int op2) {
????????????? // TODO Auto-generated method stub
?????????????
??????
}
?
?????? /* (non-Javadoc)
??????
?* @see aop.Math#sub(int, int)
??????
?
*/
?????? public void sub(int op1, int op2) {
?????????????
// TODO Auto-generated method stub
?????????????
?????? }
?
}
?
配置:
<beans xmlns="http://www.springframework.org/schema/beans"
????? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
????? xmlns:aop="http://www.springframework.org/schema/aop"
????? xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
?
?? <!-- this is the object that will be proxied by Spring's AOP infrastructure -->
?? <bean id="mathImp" class="aop.MathImp"/>
?
?? <!-- this is the actual advice itself -->
?? <bean id="logger" class="aop.LogAdvice1"/>
?
?? <aop:config>
????? <aop:aspect ref="logger">
?
???????? <aop:pointcut id="addLog"
??????????????????? expression="execution(* aop.MathImp.*(..)) and target(imp) and JoinPoint(jp)"? />
?
???????? <aop:before pointcut-ref="addLog"
????????????????? method="log" arg-names="jp,imp" />
?
????? </aop:aspect>
?? </aop:config>
?
</beans>
測試
package aop;
?
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
?
/**
?* @author Administrator
?*
?* TODO To change the template for this generated type comment go to
?* Window - Preferences - Java - Code Style - Code Templates
?*/
public class Test {
?
?????? public static void main(String[] args) {
????????????? AbstractApplicationContext context=new FileSystemXmlApplicationContext("aop2.xml");
????????????? //Math math=(Math) context.getBean("math");
????????????? MathImp math=(MathImp) context.getBean("mathImp");
????????????? math.add(1,2);
????????????? math.addtest(3,4);
????????????? math.sub(5,6);
?????? }
}
摘要: 蔡超
北京天融信,軟件架構師
SUN certified Enterprise Architect
Microsoft certified Solution Developer
...
閱讀全文