cxf使用及常见报错分析

目录

apache cxf简介

Spring集成cxf

引入cxf日志

常见报错-部署编译失败或提示找不到xxx类

常见报错-调用超时


apache cxf简介

Apache CXF 是一个开源的 WebService 框架,CXF可以用来构建和开发 WebService,这些服务可以支持多种协议,比如:SOAP、POST/HTTP、HTTP ,CXF 大大简化了WebService并且可以天然地和 Spring 进行无缝集成。CXF是 Celtrix (ESB框架)和 XFire(webserivice) 合并而成,核心是org.apache.cxf.Bus(总线),类似于Spring的 ApplicationContext,CXF默认是依赖于Spring的,另 CXF 发行包中的jar,如果全部放到lib中,需要 JDK1.6 及以上,否则会报JAX-WS版本不一致的问题。CXF 内置了Jetty服务器 ,它是servlet容器。

这段话是摘自别人的,这里划重点需要jdk1.6以上,一定要是jdk,千万别搞成了jre。

CXF框架特点:

  • 与spring相结合,支持注解的方式来发布webservice
  • 能够根据需求添加拦截器,例如后文会提到的输入拦截器、输出拦截器 :输入日志信息拦截器、输出日志拦截器、用户权限认证的拦截器

cxf主要用来调用webService接口,以笔者目前的工作经验来看cxf不支持rpc模式发布的webservice,有个同事是写dephi的,他用dephi写了个webservice,我这边使用cxf调不通,最后走soap协议调的,下一篇文章会写到webservice三要素及要点。
这里先简要介绍下webservice:

  • webservice是一种跨平台,跨语言的规范,用于不同平台,不同语言开发的应用之间的交互。利用webservice发布的接口无需关心对方什么平台上开发以及使用何种语言开发。程序员只关心调用对方发布webservice接口方法和参数。开发人员一般就是在基于某种语言开发webservice接口,以及调用webservice接口;通常情况下开发语言都有自己的webservice实现框架。而CXF是java开发webService的一种实现框架技术。目前,CXF也是主流的webService实现框架

Spring集成cxf

首先需要在命名空间中声明cxf的配置,如下图所示
spring cxf配置
然后定义一个接口:

@WebService
public interface IWebServicePublisher {
	public abstract String test(String xml);
}

再定义一个实现类实现这个接口

//endpointInterface是具体的接口包全路径
@WebService(endpointInterface="com.IWebServicePublisher ")
@Service
public class WebServicePublisherImpl implements IWebServicePublisher {
	public String test(String xml) {
		// TODO Auto-generated method stub
		//通常建议使用一个参数,然后按照xml或者json来解析
		//使用xml是因为有cdata可以避免特殊字符带来的麻烦
		return "hello world!";
	}
}

引入cxf日志

默认的话是不开启的,如果有需要是可以通过代码或者注解的形式开启,上图中的注释代码已经阐述了cxf-spring的日志开启方式,这里介绍代码的形式,先写一个CDATAOutInterceptor类,用于继承AbstractPhaseInterceptor,
这个了就是我们输出拦截类

import java.io.OutputStream;
import javax.xml.stream.XMLStreamWriter;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.staxutils.StaxUtils;

public class CDATAOutInterceptor extends AbstractPhaseInterceptor<Message> {
	public CDATAOutInterceptor() {
		super(Phase.WRITE);
	}

	public void handleMessage(Message message) {
		message.put("disable.outputstream.optimization", Boolean.TRUE);
		XMLStreamWriter writer = StaxUtils.createXMLStreamWriter(message.getContent(OutputStream.class));
		message.setContent(XMLStreamWriter.class, new CDATAXMLStreamWriter(writer));
	}
}

接下来给出调用的代码,LoggingInInterceptor()和LoggingOutInterceptor()是内置的日志类

	@Test
	public void logTest() {

		try {

			JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
			Client client = dcf.createClient("http://localhost:9001/Test/service?wsdl");
			client.getInInterceptors().add(new LoggingInInterceptor());
			client.getOutInterceptors().add(new CDATAOutInterceptor());
			client.getOutInterceptors().add(new LoggingOutInterceptor());
			Object[] objs = new Object[2];
			objs[0] = "20";
			objs[1] = "张三";
			objs = client.invoke("method", objs);
			String s = objs[0].toString();
			System.out.println(s);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

如果还需要进一步对数据进行封装,例如xml格式的数据,则可以如下操作:

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;

public class CDATAXMLStreamWriter extends DelegatingXMLStreamWriter {
	
	private String currentElementName;

	public CDATAXMLStreamWriter(XMLStreamWriter writer) {
		super(writer);
	}

	@Override
	public void writeCharacters(String text) throws XMLStreamException {
		boolean useCData = isNeedCData(text);
		if (useCData) {
			super.writeCData(text);
		} else {
			super.writeCharacters(text);
		}
	}

	private boolean isNeedCData(String text) {
		// 自己拓展哪些属性需要处理CDATA
		if(text != null && !"".equals(text)){
			
			if(text.startsWith("MSH")){
				
				return true;
			}
		}
		
		return false;
	}

	public void writeStartElement(String prefix, String local, String uri) throws XMLStreamException {
		currentElementName = local;
		super.writeStartElement(prefix, local, uri);
	}
}

常见报错-部署编译失败或提示找不到xxx类

引入cxf框架,在实际上线部署后如果采用java -jar或者部署在tomcat容器中出现编译失败,又或者提示找不到xxx类,大家如果上网搜索得到的答案一般是说当前环境是Jre,而cxf需要jdk环境下的tools.jar,也就是说cxf是需要Jdk环境的,所以解决方案很简单,如果是跑Jar包,那就进入jdk/bin路径下执行java -jar,如果是跑容器,通常情况下容器都是读取环境变量的jdk路径其实就是jre,那就修改容器的环境为jdk路径就可以了。例如tomcat是在bin\setclasspath.bat的文件中加入:
set JAVA_HOME=JDK路径
set JRE_HOME=JRE路径(直接改为JDK路径)
然后重启就可以了,主要目的是让服务器使用jdk环境而不是jre环境即可

常见报错-调用超时

cxf正常调用webservice的格式如下:

JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient("http://localhost:9001/Test/service?wsdl");
try {
Object[] objs = new Object[2];
objs[0] = "20";
objs[1] = "张三";
objs = client.invoke("method", objs);
String s = objs[0].toString();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}

cxf处理超时调用问题:

JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient("http://localhost:9001/Test/service?wsdl");
//一般超时是一分钟,如果一个请求在一分钟内没有结束,又来个新的请求,如此周而复始,CXF就会缓存起来,当数缓存太大时 就会挂掉
//加入日志
client.getInInterceptors().add(new LoggingInInterceptor());
//这个输出类是我自己加的,可以删除,该类继承AbstractPhaseInterceptor
client.getOutInterceptors().add(new CDATAOutInterceptor());
client.getOutInterceptors().add(new LoggingOutInterceptor());
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setAllowChunking(false); //取消块编码
httpClientPolicy.setConnectionTimeout(180 * 1000); //连接超时3分钟
httpClientPolicy.setReceiveTimeout(300 * 1000); //响应超时
http.setClient(httpClientPolicy);

加入配置后,正常调用即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值