前言:xfire、spring都是比較流行的技術,這里就不再贅述他們各自的優點;本文着重介紹xfire和spring的整合,不會做太深入的探究。
服務端
1. web.xml配置
spring配置部分:contextConfigLocation定義配置文件路徑,可以指定全局的配置文件路徑。
<!-- spring配置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/xfire-servlet.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- spring配置 -->
XFire配置部分:xfire配置1使用spring的DispatcherServlet類來作為xfire的處理類,DispatcherServlet的配置文件名默認為[servletname]-servlet,位於WEB-INF目錄下,也可以通過namespace參數來指定或者通過contextConfigLocation參數自定義配置文檔的位置;這種配置方式只能通過服務名.ws的方式訪問,方便隱藏其它的服務接口;xfire配置2使用xfire的XFireSpringServlet類來作為xfire的處理類,這種配置方式可以直接通過接口名訪問,也可以訪問所有的服務接口。
<!-- XFire 配置 1 使用spring的DispatcherServlet作為xfire的處理類,好處是可以自定義服務的名稱並隱藏所有提供的其它接口,客戶端只能通過服務名.ws的方式訪問--> <servlet> <!-- 配合Spring容器中XFire一起工作的Servlet --> <servlet-name>xfireServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- WebApplicationContext命名空間,默認值是[servlet-name]-servlet,對應DispatcherServlet的定義檔名稱,位於 /WEB-INF下,也可以通過contextConfigLocation參數自定義位置 --> <param-name>namespace</param-name> <param-value>xfire-servlet</param-value> </init-param> <!-- 通過contextConfigLocation參數自定義位置 <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/xfire-servlet.xml </param-value> </init-param>--> </servlet> <servlet-mapping> <servlet-name>xfireServlet</servlet-name> <!-- 在這個URI下開放Web Service服務 --> <url-pattern>*.ws</url-pattern> </servlet-mapping> <!-- XFire 配置1 --> <!-- XFire 配置2 使用xfire的XFireSpringServlet作為xfire的處理類,可以直接使用接口類名來訪問開放的服務,也可以查看所有開發的服務接口 --> <servlet> <!-- 配合Spring容器中XFire一起工作的Servlet --> <servlet-name>xfireServlet2</servlet-name> <servlet-class>org.codehaus.xfire.spring.XFireSpringServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>xfireServlet2</servlet-name> <!-- 在這個URI下開放Web Service服務 --> <url-pattern>/service/*</url-pattern> </servlet-mapping> <!-- XFire 配置2 -->
2.xfire-servlet配置
首先需要引入xfire.xml的配置;第二部分用來定義訪問的服務名,若使用XFireSpringServlet作為servlet時無需此配置,直接通過接口名訪問;第三部分我們通過XFireExporter將業務類導出為Web Service,對於任何導出器,我們都需要引入XFire環境,即serviceFactory和xfire,這是標准的配置。ServiceFactory是XFire的核心類,它可以將一個POJO生成為一個Web Service。在本實例中,我們通過定義一個baseWebService,其余的webService配置都將該bean作為父bean,這樣可以簡化Spring的配置,不需要多次引入serviceFactory和xfire,這其中的inHandlers參數用來定義xfire的SOAP的截取處理類,可以添加多個,用來完成一些安全驗證等功能;最后一部分用來定義業務接口,他們都需要將baseWebService作為父bean。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <import resource="classpath:org/codehaus/xfire/spring/xfire.xml" /> <!-- 定義訪問的url 使用XFireSpringServlet作為servlet時無需此配置 --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlMap"> <map> <entry key="/helloworld.ws"> <ref bean="HelloWorldService" /> </entry> </map> </property> </bean> <!-- 使用XFire導出器 我們通過XFireExporter將業務類導出為Web Service,對於任何導出器, 我們都需要引入XFire環境,即serviceFactory和xfire,這是標准的配置。ServiceFactory是 XFire的核心類,它可以將一個POJO生成為一個Web Service。在本實例中,我們通過定義一個 baseWebService,其余的webService配置都將該bean作為父bean,這樣可以簡化Spring的配置, 不需要多次引入serviceFactory和xfire。--> <bean id="baseWebService" class="org.codehaus.xfire.spring.remoting.XFireExporter" lazy-init="false" abstract="true"> <!-- 引用xfire.xml中定義的工廠 --> <property name="serviceFactory" ref="xfire.serviceFactory" /> <!-- 引用xfire.xml中的xfire實例 --> <property name="xfire" ref="xfire" /> <!-- 安全驗證 --> <property name="inHandlers" ref="AuthenticationHandler"></property> </bean> <!-- 安全認證 --> <bean id="AuthenticationHandler" class="com.AuthenticationHandler"></bean> <bean id="HelloWorldServiceImpl" class="com.impl.HelloWorldServiceImp" /> <bean id="HelloWorldService" parent="baseWebService"> <!-- 業務服務bean --> <property name="serviceBean" ref="HelloWorldServiceImpl" /> <!-- 業務服務bean的窄接口類 --> <property name="serviceClass" value="com.HelloWorldService" /> </bean> </beans>3.AuthenticationHandler安全驗證類
webservice是一種開放的服務的,但有時候我們需要控制用戶的訪問,這里我們采用handler的方式來截取訪問的SOAP報文來判斷其中是否包含驗證信息,這樣就可以完成一個簡單的安全校驗;繼承AbstractHandler類實現invoke方法,通過MessageContext獲取請求的報文信息頭中是否包含我們需要的用戶名、密碼等驗證信息,相應的我們就需要就客戶端調用時放入這些信息,客戶端驗證類ClientPasswordHandler我們在下面的客戶端部分介紹。
public class AuthenticationHandler extends AbstractHandler { public void invoke(MessageContext cfx) throws Exception { if (cfx.getInMessage().getHeader() == null) {// 是否有驗證信息 throw new org.codehaus.xfire.fault.XFireFault("請求必須包含驗證信息", org.codehaus.xfire.fault.XFireFault.SENDER); } Element token = cfx.getInMessage().getHeader() .getChild("AuthenticationToken");// AuthenticationToken為自定義元素值 if (token == null) { throw new org.codehaus.xfire.fault.XFireFault("請求必須包含身份驗證信息", org.codehaus.xfire.fault.XFireFault.SENDER); } String username = token.getChild("Username").getValue(); String password = token.getChild("Password").getValue(); try { // 進行身份驗證 ,只有test@test的用戶為授權用戶 if (username.equals("test") && password.equals("test")) System.out.println("身份驗證通過"); else throw new Exception(); } catch (Exception e) { throw new org.codehaus.xfire.fault.XFireFault("非法的用戶名和密碼", org.codehaus.xfire.fault.XFireFault.SENDER); } } }
4、完整這些配置和接口的類的開發后服務端的工作就完成了,在tomcat中運行這個demo,在瀏覽器中輸入對應的url當出現“Invalid SOAP request.”字樣是表示接口可以正常訪問了,在url后添加?wsdl可以獲得更詳細的接口信息,如下圖:
客戶端
1.ClientPasswordHandler安全驗證類
客戶端安全驗證類也要繼承AbstractHandler類實現invoke方法,以Element的方式構建驗證信息添加到SOAP的報文頭信息中,然后添加這個類到客戶端訪問的服務接口的請求中,就會在被服務端的AuthenticationHandler類截獲完成安全驗證。
public class ClientPasswordHandler extends AbstractHandler { private String username = null; private String password = null; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public ClientPasswordHandler(String username, String password) { this.username = username; this.password = password; } public void invoke(MessageContext context) throws Exception { // 為SOAP Header構造驗證信息 Element el = new Element("header");//標頭 context.getOutMessage().setHeader(el); Element auth = new Element("AuthenticationToken");//自定義元素 Element username_el = new Element("Username"); username_el.addContent(username); Element password_el = new Element("Password"); password_el.addContent(password); auth.addContent(username_el); auth.addContent(password_el); el.addContent(auth); } }
2、訪問服務端接口類
GetServiceBean類用來獲取服務端接口對象,添加客戶端驗證類;ClientTest通過調用GetServiceBean獲取服務端接口對象,然后就可以像使用本地類一樣調用其中的方法了(如下圖),這里一個顯而易見的要求就是客戶端要保留服務端的接口類、對象類才能完成調用。
public class GetServiceBean { private static XFireProxyFactory factory = new XFireProxyFactory(); public static Object getBean(String serviceUrl, Class<?> serviceClass) throws MalformedURLException { Service service = new ObjectServiceFactory().create(serviceClass); Object object = factory.create(service, serviceUrl); Client client = ((XFireProxy) Proxy.getInvocationHandler(object)) .getClient(); // 獲取訪問服務的客戶端 client.addOutHandler(new ClientPasswordHandler("test", "test"));// 添加客戶端驗證類 return object; } }public class ClientTest { public static void main(String[] args) throws Exception { HelloWorldService service = (HelloWorldService) GetServiceBean.getBean( "http://localhost/xfireserver/helloworld.ws", HelloWorldService.class); System.out.println(service.hello("小明")); Person person = new Person(); person = service.getPerson(); System.out .println("id:" + person.getId() + " name:" + person.getName()); List<Person> students = new ArrayList<Person>(); students = service.getList(); for (int i = 0; i < students.size(); i++) { Person per = students.get(i); System.out.println("id:" + per.getId() + " name:" + per.getName()); } } }
結語:個人理解會有偏駁和不對的地方,歡迎大家批評指正!
demo下載地址:http://pan.baidu.com/s/1eQxRjeM




