近期需要使用webService,所以看了一下目前比较常用的几个webservie框架,经过比较,最终决定使用能和spring无缝结合的cxf框架。~~从此走上了不归路了
- 先上,maven引用,因为好久没有使用ssm框架了,所以直接就上ssm了,当练手了~
3.1.12
junit
junit
3.8.1
test
mysql
mysql-connector-java
5.1.37
runtime
c3p0
c3p0
0.9.1.2
org.mybatis
mybatis
3.3.0
org.mybatis
mybatis-spring
1.2.3
org.springframework
spring-core
4.1.7.RELEASE
org.springframework
spring-beans
4.1.7.RELEASE
org.springframework
spring-context
4.1.7.RELEASE
org.springframework
spring-jdbc
4.1.7.RELEASE
org.springframework
spring-tx
4.1.7.RELEASE
org.springframework
spring-web
4.1.7.RELEASE
org.springframework
spring-webmvc
4.1.7.RELEASE
org.apache.cxf
cxf-rt-frontend-jaxws
${cxf.version}
org.apache.cxf
cxf-rt-transports-http
${cxf.version}
- 创建spring-dao.xml、mybatis-config.xml、jdbc.properties....反正就是ssm框架那一套,作为本文非主要内容,不做讲解了,主要讲一下cxf的配置——创建spring-cxf.xml
- 接下来是web.xml
contextConfigLocation
classpath:spring-*.xml
org.springframework.web.context.ContextLoaderListener
seckill-dispatcher
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-*.xml
seckill-dispatcher
/
cxf
org.apache.cxf.transport.servlet.CXFServlet
cxf
/ws/*
- 紧接着是要创建将要发布的接口和类
@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 {
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 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修改为
- 客户端也要加入拦截器
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 {
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 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,据说很厉害的样子
- ...