注解和spring配置相结合
使用cxf与spring集成发布webservice的是非常方便的,下面简单写下基本的步骤以备忘.
1.首先当然是引用cxf相关的jar包.这里使用的是2.7.5的版本.
2.在web.xml文件中配置CXFServlet
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/ws/*</url-pattern><!--CXFServlet拦截RootPath/ws/*的请求-->
</servlet-mapping>
3.在spring配置文件中配置基本的cxf组件
<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"/>
4.编写需要发布的POJO类
接口
@WebService(targetNamespace=Constants.TARGETNAMESPACE)
public interface TestWebService {
@WebMethod(operationName="SendMessage",action="http://xxx.xxx.Webservice/SendMessage")
@WebResult(name="SendMessageResult",targetNamespace="http://yyy.yyy.Webservice/")
public String receiveMsg(@WebParam(name="msg",targetNamespace=Constants.TARGETNAMESPACE) String msg);
}
@WebMethod的属性operationName的值对应wsdl中的operationName,也即ws客户端所谓的方法名称。action的值对应soapAction的值。
@WebResult的属性name,ws会参数转换成xml的格式进行传递,而属性name指定该xml的根节点的名称;targetNameSpace则指定该名称所属的命名空间
@WebParam的属性name指定参数的名称,java虚拟机编译源文件时,通常情况下参数名称不会记录,而会用arg1,arg2之类的替换,转换成xml后对应的节点名称则为<arg0></arg0>,设置name属性可以指定该xml节点的名称为<msg></msg>.
实现
@WebService(serviceName="TestWebService", targetNamespace=Constants.TARGETNAMESPACE, endpointInterface="ws.TestWebService")
public class TestWebServiceImpl implements TestWebService {
@Override
public String receiveMsg(String msg) {
return "接收成功";
}
}
serviceName对应wsdl中的<wsdl:service name="TestWebService">中的name.
endpointInterface为该实现类的接口的全限定名称.
5.在spring配置文件中发布该pojo为webservice
<jaxws:endpoint address="SendMessage"
implementor="ws.impl.TestWebServiceImpl">
</jaxws:endpoint>
address指定了该webservice对应的URI,结合web.xml文件中的CXFServlet配置,访问该webservice的Url为
http://hostip:port/WebRoot/ws/SendMessage
wsdl链接为
http://hostip:port/WebRoot/ws/SendMessage?wsdl
6.部署并通过wsdl链接来验证webservice是否发布成功.
以上发布webservice的方法会把接口中所有的方法发布成webservice接口
零配置发布pojo为webservice
第1,2,3步与上面一致.
4.编写需要发布的pojo类
接口
public interface TestWebService {
public String receiveMsg(String msg);
public String sendMessage(String msg);
}
实现
public class TestWebServiceImpl implements TestWebService {
@Override
public String receiveMsg(String msg) {
return "接收成功";
}
@Override
public String sendMessage(String msg)
{
return "接收成功";
}
}
5.发布pojo类为webservice
ReflectionServiceFactoryBean rsfb = new ReflectionServiceFactoryBean();
List<Method> list = new ArrayList<Method>();
Class clazz = Thread.currentThread().getContextClassLoader().loadClass("ws.impl.TestWebService");
Method [] methods = clazz.getDeclaredMethods();
for(int i=0;i<methods.length;i++){
if(methods[i].getName().equals("sendMessage")){
list.add(methods[i]);
}
}
//设置需要过滤到的方法,既这些方法不需要被发布成webservice接口
rsfb.setIgnoredMethods(list);
//指定endpointName
rsfb.setEndpointName(new QName("TestEndpointName"));
//指定serviceName
rsfb.setServiceName(new QName("http://lqin.szse.cn/test","ServiceName1",XMLConstants.DEFAULT_NS_PREFIX));
List<AbstractServiceConfiguration> listAsc = rsfb.getServiceConfigurations();
if(listAsc == null){
listAsc = new ArrayList<AbstractServiceConfiguration>();
}
listAsc.add(new AbstractServiceConfiguration(){
//设置soapAction为"http://lqin.com/sa",可以根据不同的方法返回不同的soapAction
@Override
public String getAction(OperationInfo op, Method method) {
return "http://lqin.com/sa";
}
//设置发布的webservice方法名称,此处可以根据不同的方法返回不同的方法名称
@Override
public QName getOperationName(InterfaceInfo service, Method method) {
return new QName("CustomOperationName");
}
});
ServerFactoryBean sfb = new ServerFactoryBean(rsfb);
Object o = SpringUtils.getBean(serviceBeanId);
sfb.setServiceClass(clazz);
sfb.setServiceBean(new TestServiceImpl());
sfb.setAddress("/test");
sfb.create();
在前一节中,是通过注解和spring配置的方式配置webservice的,那么在零配置的情况下webservice所需的参数从哪里来呢?答案在于
List<AbstractServiceConfiguration> listAsc = rsfb.getServiceConfigurations();
在获取某一个属性的值时,比如operationName,会遍历listAsc中的AbstractServiceConfiguration#getOperationName方法并返回第一个不为null的值,否则返回空字符串.
ReflectionServiceFactoryBean默认注册了一个DefaultServiceConfiguration,该ServiceConfiguration会返回系统默认生成的属性值,比如operationName默认是方法的名称,soapAction默认为空.如此,如果想要自定义opertaionName的值,那么必须先删除DefaultServiceConfiguration再实现一个AbstractServiceConfiguration返回operationName,或者把返回自定义的operationName放到DefaultServiceConfiguration的前面(建议自定义的放前面).
在上面的代码中,我们提供了一个AbstractServiceConfiguration的实现并重写了getAction方法和getOperationName方法,并且把它加到了DefaultServiceConfiguration的后面,根据上面的分析,soapAction自定义有效,而operationName则无效.
将上面的代理在servlet容器启动之后执行,访问
http://hostip:port/WebRoot/ws/test?wsdl路径可以看到webservice发布成功.
附上ServerFactoryBean#create()方法应用AbstractServiceConfiguration的执行路径:
ServerFactoryBean#create
AbstractWSDLBasedEndPointFactory#createEndPoint
ReflectionServerFactoryBean#create
ReflectionServerFactoryBean#initializeServiceModel
ReflectionServerFactoryBean#buildServiceFromClass//这里还有一个方法叫做buildServiceFromWSDL
ReflectionServerFactoryBean#createInterface
ReflectionServerFactoryBean#createOperation
ReflectionServerFactoryBean#getOperationName