近期需要使用webService,所以看了一下目前比较常用的几个webservie框架,经过比较,最终决定使用能和spring无缝结合的cxf框架。~~从此走上了不归路了
- 先上,maven引用,因为好久没有使用ssm框架了,所以直接就上ssm了,当练手了~
<properties>
<cxf.version>3.1.12</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- 2.数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- DAO: MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 4.Spring -->
<!-- 1)Spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<!-- 2)Spring DAO层 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<!-- 3)Spring web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<!--cxf-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
- 创建spring-dao.xml、mybatis-config.xml、jdbc.properties....反正就是ssm框架那一套,作为本文非主要内容,不做讲解了,主要讲一下cxf的配置——创建spring-cxf.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://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 该文件是在cxf-core-3.1.12.jar的类路径下的META-INF/cxf/cxf.xml -->
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<!--wsdl的访问地址为web.xml中cxf的前缀+fileReceive-->
<jaxws:server address="business" serviceClass="com.demo.cxf.FileReceive">
<!-- 服务接口的实现类 -->
<jaxws:serviceBean>
<bean class="com.ssm.service.impl.BaseServiceImpl"></bean>
</jaxws:serviceBean>
<!-- <jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
</jaxws:outInterceptors>-->
</jaxws:server>
</beans>
- 接下来是web.xml
<!--spring-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--因为在这就已经将cxf的xml导入进来了,所以别地方不用特殊导入了 -->
<param-value>classpath:spring-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>seckill-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置springMVC需要加载的配置文件
spring-dao.xml,spring-service.xml,spring-web.xml
Mybatis - > spring -> springmvc
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-*.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>seckill-dispatcher</servlet-name>
<!-- 默认匹配所有的请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--cxf-->
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
- 紧接着是要创建将要发布的接口和类
@WebService
@MTOM //开启MTOM功能之后,能够发送文件信息
public interface BaseService {
String sayHello(@XmlMimeType(value = "application/octet-stream") DataHandler name);
//@XmlMimeType(value = "application/octet-stream")如果发送文件,这个是死格式,而且必须声明
}
- 然后是实现它,这个地方有个坑,就是如果你想传入文件进来,那么必须用InputStream.read(byte[] b)的方式获取文件,不然文件就是乱码(猜想了一下,因为cxf传输文件的类型必须是DataHandler,而这个类型是byte数组的),还有一个坑,就是必须把targetNamespace给写出来,value是类所在包名反过来(这个没怎么懂必须要这样的目的,暂时先按照教程来吧)
@WebService(endpointInterface="com.ssm.service.BaseService",targetNamespace="http://service.ssm.com/")
public class BaseServiceImpl implements BaseService {
public String sayHello(DataHandler name) {
InputStream sn = null;
OutputStream bw = null;
try{
bw = new FileOutputStream("d:/dsd.jpg");
sn = name.getInputStream();
byte[] bytes = new byte[1024];
int i=-1;
while ((i=sn.read(bytes))!=-1){
bw.write(bytes,0,i);
}
}catch (Exception e){
e.printStackTrace();
}finally {
try{
sn.close();
bw.close();
}catch (Exception e){
e.printStackTrace();
}
}
return "ok";
}
}
- 好了,这就是最简单的cxf服务端配置方式了,装上tomcat测试一下看看有没有发布成功吧
路径: http://localhost:9090/ws/business?wsdl
简单的说一下路径为什么是这个,前面的就不说了,主要说说为什么是/ws,这个实在web.xml中定义的,在servlet-mapping中的url上,/business就是在spring-cxf.xml中定义的了,在发布时定义的——jaxws:endpoint的address指定了url最终路径。
- 简单的创建一个客户端访问一下吧(采用JaxWsDynamicClientFactory的方式访问,有点和服务端完全解耦,不依靠服务端的任何文件)
JaxWsDynamicClientFactory dynamicClient = JaxWsDynamicClientFactory.newInstance();
Client client = dynamicClient.createClient("http://localhost:9090/ws/business?wsdl");
DataHandler dataHandler = new DataHandler( new FileDataSource(new File("98226cffc175d.jpg")));
Object[] o = client.invoke("sayHello",dataHandler);
一下为加入账号密码验证
- 服务端现在加入账号验证(在接受时拦截,查看账号密码,匹配)
import java.util.List;
import javax.xml.soap.SOAPException;
import org.apache.cxf.binding.soap.SoapHeader;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class AuthIntercetpr extends AbstractPhaseInterceptor<SoapMessage> {
private static final String USERNAME="root";
private static final String PASSWORD="admin";
public AuthIntercetpr() {
//定义在哪个阶段进行拦截
super(Phase.PRE_PROTOCOL);
}
public void handleMessage(SoapMessage soapMessage) throws Fault {
List<Header> headers = null;
String username=null;
String password=null;
try {
headers = soapMessage.getHeaders();
} catch (Exception e) {
}
if (headers == null) {
throw new Fault(new IllegalArgumentException("找不到Header,无法验证用户信息"));
}
//获取用户名,密码
for (Header header : headers) {
SoapHeader soapHeader = (SoapHeader) header;
Element e = (Element) soapHeader.getObject();
NodeList usernameNode = e.getElementsByTagName("username");
NodeList pwdNode = e.getElementsByTagName("password");
username=usernameNode.item(0).getTextContent();
password=pwdNode.item(0).getTextContent();
if( StringUtils.isEmpty(username)||StringUtils.isEmpty(password)){
throw new Fault(new IllegalArgumentException("用户信息为空"));
}
}
//校验用户名密码
if(!(username.equals(USERNAME) && password.equals(PASSWORD))){
SOAPException soapExc = new SOAPException("认证失败");
throw new Fault(soapExc);
}
}
}
- spring-cxf.xml修改为
<!-- 该文件是在cxf-core-3.1.12.jar的类路径下的META-INF/cxf/cxf.xml -->
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<!--发布出去,请求就是http:xxxxxxxxxx/business?wsdl-->
<jaxws:server address="business" serviceClass="com.demo.cxf.FileReceive">
<!-- 服务接口的实现类 -->
<jaxws:serviceBean>
<bean class="com.ssm.service.impl.BaseServiceImpl"></bean>
</jaxws:serviceBean>
<jaxws:inInterceptors>
<bean class="com.ssm.interceptor.AuthIntercetpr"></bean>
</jaxws:inInterceptors>
<!-- <jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
</jaxws:outInterceptors>-->
</jaxws:server>
- 客户端也要加入拦截器
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import java.util.List;
public class LoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private String username="root";
private String password="admin";
public LoginInterceptor(String username, String password) {
//设置在发送请求前阶段进行拦截
super(Phase.PREPARE_SEND);
this.username=username;
this.password=password;
}
@Override
public void handleMessage(SoapMessage soapMessage) throws Fault {
List<Header> headers = soapMessage.getHeaders();
Document doc = DOMUtils.createDocument();
Element auth = doc.createElementNS("http://service.ssm.com/","SecurityHeader");
Element UserName = doc.createElement("username");
Element UserPass = doc.createElement("password");
UserName.setTextContent(username);
UserPass.setTextContent(password);
auth.appendChild(UserName);
auth.appendChild(UserPass);
headers.add(new Header(new QName("SecurityHeader"),auth));
}
}
- 在请求时加入拦截器
JaxWsDynamicClientFactory dynamicClient = JaxWsDynamicClientFactory.newInstance();
Client client = dynamicClient.createClient("http://localhost:9090/ws/business?wsdl");
//加入拦截器
client.getOutInterceptors().add(new LoginInterceptor("root","admin"));
DataHandler dataHandler = new DataHandler(
new FileDataSource(new File("C:\\Users\\lht\\Desktop\\a\\a.jpg")));
Object[] o = client.invoke("sayHello",dataHandler);
System.out.println();
留下点问题,看看等什么时候去研究一下
- 因为这个还是传统的SOAP方式,什么时候看一下REST风格WebService,据说很厉害的样子
- ...