前一段时间在公司比较闲看了看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.……错误。