CXF2.3.0 +myEclipse 8.5 +tomcat 6.0 +sping 3.0.5 集成方案
Posted on 2010-12-13 15:35 為自己代言 閱讀(6575) 評論(6) 編輯 收藏前一段時間在公司比較閑看了看CXF2.3 與SPRING 3.0.5集成,以前用XFire開發東西,去XFire社區轉時候都說建議換用CXF。哈哈不說廢話了開始吧:
準備:CXF2.3.0 所有jar 包,去官網下就可以了,如果為了測試建議把lib下所有jar包導入工程中(它里邊包含了spring所必需的jar包版本是spring3.0.5的,如果你不想使用CXF2.3.0中的spirng jar包可以不導入,導入自己的)。
oracle 數據驅動包:ojdbc14.jar
spirng -jdbc 包: org.springframework.jdbc-3.0.5.RELEASE.jar(因為本用例需要用spring JDBC 做持久層測試)
第一步:建立一個webproject ,把CXF包導入工程lib目錄下;
接口代碼如下:
import javax.jws.WebService;
/*
* @author zzz
* @version 1.0
* @ userservice接口;
*/
@WebService
public interface UserService {
public String getSomething(String data);
}
實現類:
import java.sql.SQLException;
import javax.jws.WebService;
import org.springframework.jdbc.core.JdbcTemplate;
import com.tchzt.demo.user.service.UserService;
@WebService
public class UserServiceImpl extends JdbcTemplate implements UserService{
public String getSomething(String data) {
System.out.println("this is server message :"+data);
try {
System.out.println("獲得數據庫連接:"+getDataSource().getConnection());
//這里是用的spring 的JDBC,可以在這里寫持久化代碼;
} catch (SQLException e) {
e.printStackTrace();
}
return data;
}
}
在src目錄下建立一個applicationContext-client-beans.xml 這個名字可以自己定,但在web.xml 加載名字要一致:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schema/jaxws.xsd">
<!-- 這是JNDI配置數據源方式
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:com/chtt/cfg/dbconn/oracle_jndi.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>${oraconn.jndi}</value>
</property>
</bean>
-->
<!-- 數據據源配置方式 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@110.168.1.51:1521:pos"/>
<property name="username" value="pos"/>
<property name="password" value="pos"/>
</bean>
<!-- 配置DAO -->
<bean id="daoTemplate" abstract="true">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- dao引用 -->
<bean id="userServiceImpl" class="com.tchzt.demo.user.serviceImpl.UserServiceImpl" parent="daoTemplate"/>
<!-- webservice 發布配置 -->
<bean id="client" class="com.test.Client" factory-bean="clientFactory" factory-method="create" />
<bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean" >
<!-- 服務發布供外部調用接口 -->
<property name="serviceClass" value="com.tchzt.demo.user.service.UserService" />
<property name="address" value="http://127.0.0.1:8080/WebServiceCXF/UserService" />
</bean>
</beans>
WEB-INF目錄下建立一個services.xml (名字自己定):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<!-- webService發布地址 , #userServiceImpl引用bean方式,也可以寫成類的全路徑 -->
<!--userServiceImpl 加入數據連接, 在此引用 -->
<jaxws:endpoint id="userService" implementor="#userServiceImpl" address="/UserService"/>
<!-- 第二種方式引用其它bean;
<jaxws:endpoint id="userService"
implementorClass="demo.spring.HelloWorld"
address="/UserService">
<jaxws:implementor>
<bean ref="userServiceImpl"/>
</jaxws:implementor>
<jaxws:implementor>
<bean ref="userServiceImpl2"/>
</jaxws:implementor>
</jaxws:endpoint>
-->
</beans>
web.xml 文件內容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-client-beans.xml,WEB-INF/services.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 加載數據源
<resource-ref>
<res-ref-name>jdbc/cdbank</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
-->
</web-app>
測試類內容:
package com.test;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.tchzt.demo.user.service.UserService;
import com.tchzt.demo.user.serviceImpl.UserServiceImpl;
public class Client {
public static Client client = new Client();
private UserService userService=null;
//這是spring得到服務實例構造方法;
private Client(){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext-client-beans.xml");
userService=(UserService) context.getBean("client");
}
//這是普通客戶端得到服務實例;
private Client(UserService userService){
this.userService=userService;
}
public String getText(String text) throws Exception {
String data = userService.getSomething(text);
return data;
}
/*
*單獨測試spring 加載數據連接;
*/
public static void getConnectTest(){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext-client-beans.xml");
UserServiceImpl userServiceImpl=(UserServiceImpl) context.getBean("userServiceImpl");
System.out.println("數據庫連接測試:"+userServiceImpl.getSomething("adfasdf"));
}
/*
* CXF 普通客戶端測試;
*/
public static void testCxfClient()throws Exception{
//這里不用Spring 得到bean實例;
JaxWsProxyFactoryBean factory=new JaxWsProxyFactoryBean();
//設置服務類的名字;
factory.setServiceClass(UserService.class);
//設置服務的地址;
factory.setAddress("http://127.0.0.1:8080/WebServiceCXF/UserService");
//得到服務類的實例;
UserService userService=(UserService) factory.create();
Client client=new Client(userService);
System.out.println(client.getText("這是webserviceCXF測試數據"));
}
/*
* spring 與CXF 集成發布測試;
*/
public static void testSpringCxfClient()throws Exception{
//這是用spring 得到服務實例;
System.out.println(client.getText("這是webserviceCXF與 spring集成發布的測試數據!"));
}
public static void main(String[] args)throws Exception {
//測試數據連接是否成功!
//Client.getConnectTest();
//普通測試;
//Client.testCxfClient();
//集成測試;
Client.testSpringCxfClient();
}
}
測試:把工程發布到tomcat上運行測試類出現下面結果:
Client.testSpringCxfClient()方法測試:
客戶端輸出:
這是webserviceCXF與 spring集成發布的測試數據!
服務器端的輸出:
this is server message :這是webserviceCXF與 spring集成發布的測試數據!
獲得數據庫連接:oracle.jdbc.driver.OracleConnection@7fb878
Client.testCxfClient()測試:
客戶端輸出:
這是webserviceCXF測試數據
服務器端的輸出:
this is server message :這是webserviceCXF測試數據
獲得數據庫連接:oracle.jdbc.driver.OracleConnection@6276e5
Client.getConnectTest()數據庫連接測試:
this is server message :adfasdf
獲得數據庫連接:oracle.jdbc.driver.OracleConnection@c38157
數據庫連接測試:adfasdf
好了到此CXF+spring 集成完畢(不足這處沒有加入聲明式事物的管理,只加入了spring 連接數據庫和持久層JDBC,有時間在完善下),如果有什么問題請聯系我:zzzlyr@163.com
有時間更新這篇文章了,加入聲明式事物,這個很簡單。在把項目里用的CXF細節和遇到問題說明下:
把appcliatContext.xml 這個文件加入Spring包 自動掃描,在你的業務層上加下面基于注解事物:
開起注解事物 在需要的方法上加入即可
默認Spring為每個方法開啟一個事務,如果方法發生運行期錯誤unchecked(RuntimeException),事務會進行回滾
如果發生checked Exception,事務不進行回滾.
@Transactional(propagation=Propagation.REQUIRED)
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
重點說下在項目中用CXF2.3 遇到問題:
第一個:就是CXF命名空間問題,在網上好多例子,就簡單是一個例子。在項目中CXF發布webService ,一般做法是實現類和接口沒有在一個包中;這是項目中要用到的:
這是接口:
package com.tchzt.service;
import javax.jws.WebResult;
import javax.jws.WebService;
/*
* @author zzz
* @version 1.0
* @ userservice接口;
*/
/*
* webService接口綁定;
* com.tchzt.webService
*
*/
@WebService(targetNamespace="com.tchzt.seals.service")
public interface SealHandOverService {
/*
* 反回值標注,可以改變返回參數WSDL發布名字,還有參數名字;
*/
public @WebResult(name="sealHandOverService") String getDocumentByBantchNo(String nantchNo)throws Exception;
}
實現類:
package com.tchzt.service.imp;
import java.util.LinkedList;
import java.util.List;
import javax.jws.WebService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.tchzt.dao.PrintlogDaoIf;
import com.tchzt.dao.SealTypeDaoIf;
import com.tchzt.dao.SealsDaoIf;
import com.tchzt.po.AppSeals;
import com.tchzt.po.AppSealtype;
import com.tchzt.po.Printlog;
import com.tchzt.service.SealHandOverService;
import com.tchzt.util.Document4jUtils;
@Service
@WebService(targetNamespace="com.tchzt.seals.service",endpointInterface="com.tchzt.service.SealHandOverService")
public class SealHandOverServiceImpl implements SealHandOverService {
@Autowired
@Qualifier("printlogDaoIfImpl")
private PrintlogDaoIf printlogDao;
@Autowired
@Qualifier("sealsDaoIfImpl")
private SealsDaoIf sealsDao;
@Autowired
@Qualifier("sealTypeDaoIfImpl")
private SealTypeDaoIf sealTypeDao;
@Override
public String getDocumentByBantchNo(String banchno) throws Exception {
List<Printlog> list=printlogDao.getPrintlogs(banchno);
List<String> sealXML=new LinkedList<String>();
for(Printlog p:list){
AppSeals seal=sealsDao.getByIdx(AppSeals.class, p.getSealid());
AppSealtype sealType=sealTypeDao.getByIdx(AppSealtype.class, seal.getSealtypeId());
sealXML.add("id:"+seal.getId());
sealXML.add("sealName:"+sealType.getSealName());
sealXML.add("sealType:"+sealType.getId());
sealXML.add("image:"+seal.getImage());
}
for(String s:sealXML){
System.out.println(s);
}
return Document4jUtils.getHandOverScanXML(sealXML,banchno);
}
}
上面注意一個就行targetNamespace="com.tchzt.seals.service"一要寫上,接口和實現類不在同一個包中,接口和實現類一定要一致。但網上好多例子接口和實現類在一包中targetNamespace這個東西默認是一致的,所以不需要指定,如果接口和實現類不在同一個包中,你查看WSDL的時候也可以看到,也不報錯,但就是發布WSDL少信息。
第二客戶端問題:網上好多例子就是一個工程,調自己工程發布的WebService的方法,以下是第二個JAVA工程調用第一個發布成功的webService 方法如下 :
package com.tchzt.test;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import com.tchzt.service.SealHandOverService;
public class WebServiceClientTest {
/*
* CXF 變通客戶端測試;
*/
public static void testCxfClient()throws Exception{
//這里不用Spring 得到bean實例;
JaxWsProxyFactoryBean factory=new JaxWsProxyFactoryBean();
//設置服務類的名字;
factory.setServiceClass(SealHandOverService.class);
//設置服務的地址;
factory.setAddress("http://127.0.0.1:8080/seal/ws/SealHandOverService");
//得到服務類的實例;
SealHandOverService sealHandOverService=(SealHandOverService) factory.create();
System.out.println("======="+sealHandOverService.getDocumentByBantchNo("67801100060039"));
}
/**
* @Title: callService
* @Description: 調用遠程的webservice并返回數據
* @param wsUrl
* webservice地址
* @param method
* 調用的webservice方法名
* @param arg
* 參數
* @return
* @return:String
* @throws
*/
public static String callService(String wsUrl, String method, Object... arg)throws Exception {
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient(wsUrl);
Object[] res = client.invoke(method, arg);
return (String) res[0];
}
public static void main(String[] args)throws Exception {
//URL 組成:http://110.168.1.121:8080/seal ;第二部分:ws/這個是在web.xml配置的一個攔截地址; 第三部分: SealHandOverService?wsdl 是接口中發部的
String s=WebServiceClientTest.callService("http://110.168.1.121:8080/seal/ws/SealHandOverService?wsdl", "getDocumentByBantchNo", new Object[]{"67801100060039"});
System.out.println(s);
WebServiceClientTest.testCxfClient();
}
}
這個測試類里有兩個方法,應該差不多從注釋可以看明白什么意思。
以上方法都在項目中正確調用通過。
以下是網上的一此方法:
由于我們通常不知道提供Web Service的服務器接口及其相關類的代碼,我們也不可能從他人那里獲得。
對此,CXF提供了一些命令行工具,在CXF_HOME/bin下(這里是你下載的CXF包下有目錄)。
使用wsdl2java,可以根據從服務器返回的wsdl文件生成我們所需要的java接口和相關類。
在上面的工程中,我們可以用以下命令生成JAVA代碼,而不是從第一個工程中復制過來。
wsdl2java -p client http://110.168.1.121:8080/seal/ws/SealHandOverService?wsdl (需要在cmd窗口中將路徑切換至CXF_HOME/bin下)
例如:F:\apache-cxf-2.2.7\bin>wsdl2java -d E:\programFiles -client http://127.0.0.1/services/AlarmInformationServices?wsdl
(解釋:wsdljava –p 包路徑 –d 目標文件夾 wsdl 的url地址)
-p 是指生成客戶端代碼存放位置;
需要注意的是,對于接口類和相關類的包路徑,一定要和服務器的一樣, 即:如果服務接口包路徑和實現類路徑,要和客戶端一致。
否則,會出現org.apache.cxf.interceptor.Fault: Unexpected wrapper element {****}addResponse found.……錯誤。