背景:有的系统还用soap协议来作webservice.特别要和他们对接的时候。咱们须要实现一套。
今天说说,利用spring-ws来(部署,调用)webservcie,能很好的和主流架构(spring-mvc)结合。
参考资料,官方文档https://docs.spring.io/spring-ws/docs/3.0.0.RELEASE/reference/前端
spring-ws像spring-mvc同样,在集成到web项目时,前端有个servlet分发请求消息的概念。
这个servlet接受soap消息,经过映射转发到后端的服务实现类方法中(Endpiont)
在请求进来处理过程当中,能够添加,拦截器(Interceptor),异常处理器(ExceptionResolver)。
经过拦截器能够作一些额外的定制功能,好比安全。经过异常处理器定制异常信息显示,处理等。java
一个soap消息进来的处理流程图以下:web
依赖的jar:
官方给出的依赖jar关系图:spring
最新版,须要jdk1.8.后端
我在jdk1.7用的包依赖版本api
<dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-core</artifactId> <version>2.4.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-xml</artifactId> <version>2.4.0.RELEASE</version> </dependency> <dependency> <groupId>wsdl4j</groupId> <artifactId>wsdl4j</artifactId> <version>1.6.3</version> </dependency>
没有用spring-oxmspring-mvc
1,配置servlet web.xml文件里添加servlet 拦截webservice消息请求。缓存
<servlet> <servlet-name>spring-ws</servlet-name> <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>spring-ws</servlet-name> <!--webservice拦截路径--> <url-pattern>/webservice/*</url-pattern> </servlet-mapping>
2,定义,发布webservice服务,具体定义服务操做和操做的请求返回的xml格式。这里咱们在/WEB-INF/hr.xsd目录下,编写好xsd文件。安全
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:hr="http://j2eeweb.wannshan.cn/hr/schemas" elementFormDefault="qualified" targetNamespace="http://j2eeweb.wannshan.cn/hr/schemas"> <!--按约定,每一个operationNameRequest格式的element每一个对应一个operation,同时operationNameRequest是请求参数元素名称--> <!--同理 operationNameResponse 格式的element是对应一个operation的返回参数--> <xs:element name="saveCountryRequest"> <xs:complexType> <xs:sequence> <xs:element name="country" type="hr:country"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="saveCountryResponse"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> <!--getCountry操做,请求返回值定义--> <xs:element name="getCountryRequest"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="getCountryResponse"> <xs:complexType> <xs:sequence> <xs:element name="country" type="hr:country"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="country"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="population" type="xs:int"/> <xs:element name="capital" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:schema>
经过xsd文件咱们定义个两个操做(方法)和每一个方法的请求和返回格式
到这里咱们虽然尚未服务实现,但能够以wsdl形式发布服务了。具体:架构
在WEB-INF目录下,新建spring-ws-servlet.xml文件([servletName-servlet.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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:sws="http://www.springframework.org/schema/web-services" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!--动态生成wsdl文件url http://host:port/webservice/queryService/queryContry.wsdl id值是wsdl文件路径--> <sws:dynamic-wsdl id="queryContry" portTypeName="query" locationUri="/webservice/queryService/" targetNamespace="http://j2eeweb.wannshan.cn/hr/schemas"> <!--hr.xsd路径--> <sws:xsd location="/WEB-INF/hr.xsd"/> </sws:dynamic-wsdl> <!--静态wsdl 指定文件 通常现有动态生成,最后上生产考到静态文件中去--> <!--<sws:static-wsdl id="queryContry" location="/WEB-INF/queryContry.wsdl"/>--> </beans>
这里说下,或许咱们不太会编写wsdl文件,上面的配置文件里,spring-ws给咱们提供了一种动态生成wsdl的机制。
由于动态生成有缓存,能够在生产环境上配置静态wsdl,测试环境用动态。
而后启动web服务。
3,编写webservice服务实现类(Endpoint)完成具体的服务业务
编写前,咱们能够用maven-jaxb2-plugin插件根据wsdl文件生成业务请求对象类
GetCountryRequest,GetCountryRequest,SaveCountryResponse,GetCountryResponse,Country
里面有jaxb绑定注解。具体在项目pom.xml文件里配置
<build> <plugins> <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <version>0.13.1</version> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <schemaLanguage>WSDL</schemaLanguage> <!--pojo存放包名--> <generatePackage>cn.wannshan.j2ee.ws.dto</generatePackage> <schemas> <schema> <url>http://localhost:8080/webservice/queryService/queryContry.wsdl</url> </schema> </schemas> </configuration> </plugin> </plugins> </build>
而后对项目执行 maven package 任务
类生成好后,能够编写webservice实现类了,我本身的实现,以下:
package cn.wannshan.j2ee.ws; import cn.wannshan.j2ee.ws.dto.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ws.server.endpoint.annotation.Endpoint; import org.springframework.ws.server.endpoint.annotation.PayloadRoot; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; /** * xxx * */ @Endpoint public class CountryQuery { private static final String NAMESPACE_URI = "http://j2eeweb.wannshan.cn/hr/schemas"; //注入spring bean @Autowired CountryRepository countryRepository; /*** * 查询country * @param request * @return * @throws Exception */ @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest") @ResponsePayload public GetCountryResponse queryCountry(@RequestPayload GetCountryRequest request) throws Exception { GetCountryResponse response = new GetCountryResponse(); response.setCountry(countryRepository.findCountry(request.getName())); return response; } /** * 保存country * @param request * @return * @throws Exception */ @PayloadRoot(namespace = NAMESPACE_URI, localPart = "saveCountryRequest") @ResponsePayload public SaveCountryResponse saveCountry(@RequestPayload SaveCountryRequest request) throws Exception { SaveCountryResponse response = new SaveCountryResponse(); countryRepository.putCounttry(request.getCountry()); response.setName(request.getCountry().getName()); return response; } } //CountryRepository 类,简单利用map在内存中保存数据 package cn.wannshan.j2ee.ws; import cn.wannshan.j2ee.ws.dto.Country; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import javax.annotation.PostConstruct; import java.util.HashMap; import java.util.Map; /** * * */ @Component public class CountryRepository { private static final Map<String, Country> countries = new HashMap(); @PostConstruct public void initData() { Country spain = new Country(); spain.setName("Spain"); spain.setCapital("Madrid"); spain.setPopulation(46704314); countries.put(spain.getName(), spain); Country poland = new Country(); poland.setName("Poland"); poland.setCapital("Warsaw"); poland.setPopulation(38186860); countries.put(poland.getName(), poland); Country uk = new Country(); uk.setName("United Kingdom"); uk.setCapital("London"); uk.setPopulation(63705000); countries.put(uk.getName(), uk); } public String putCounttry(Country country){ Assert.notNull(country, "The country must not be null"); Assert.notNull(country.getName(), "The country's name must not be null"); countries.put(country.getName(),country); return country.getName(); } public Country findCountry(String name) { Assert.notNull(name, "The country's name must not be null"); return countries.get(name); } }
这里简单说下,CountryQuery 类:
类要用@Endpoint注解,代表它是spring-ws承认的webservice服务类。
类中两个方法,一个查询country,一个保存新的country.
两个方法上都用到了注解
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
@ResponsePayload
@PayloadRoot:实现soap消息的映射路由功能,简单点说,就是soap消息里,namespace = NAMESPACE_URI而且有getCountryRequest元素节点的,才能由这个方法接受处理。
@ResponsePayload:表示这个方法有返回值,而且会把返回值封装格式化为soap消息格式。
而方法名参数上用到了@RequestPayload GetCountryRequest request,表示soap消息的,有效负载,映射到参数request对象上。
以上都是spring-ws自动帮你作好的。
3,部署webservice服务实现
实现类作好后,在spring-ws-servlet.xml文件文件里加入以下配置,重启web服务。就能够接受处理合适的webservice soap消息请求了。
<!--webservice实现类 endpoint 所在包-->
<context:component-scan base-package="cn.wannshan.j2ee.ws"></context:component-scan>
<!--开启sws扫描,beans里要加入xmlns:sws命名空间-->
<sws:annotation-driven/>
以上是webservcie服务部署过程。
spring-ws还提供了webservcie服务客户端类,用于请求soap webservice叫WebServiceTemplate。只要在spring文件里配置一个bean
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"> <constructor-arg ref="messageFactory"/> <!--webserivce服务地址--> <property name="defaultUri" value="http://localhost:8080/webservice/queryService"/> </bean>
而后就能够用了
下面是我写的测试类片断
@Test public void testWebservice() throws JAXBException { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); //这是个包名,是你利用maven插件根据xsd文件生成pojo类,存放包名 marshaller.setContextPath("cn.wannshan.j2ee.ws.dto"); //指定Jaxb方案实现类。spring提供Jaxb2Marshaller webServiceTemplate.setMarshaller(marshaller); webServiceTemplate.setUnmarshaller(marshaller); //查询Country GetCountryRequest getCountryRequest=new GetCountryRequest(); getCountryRequest.setName("Spain"); GetCountryResponse getCountryResponse= (GetCountryResponse) webServiceTemplate.marshalSendAndReceive(getCountryRequest); Assert.assertEquals(getCountryResponse.getCountry().getName(), "Spain"); //保存Country Country country=new Country(); country.setName("中国"); country.setCapital("北京"); country.setPopulation(1400000000); SaveCountryRequest saveCountryRequest=new SaveCountryRequest(); saveCountryRequest.setCountry(country); SaveCountryResponse saveCountryResponse= (SaveCountryResponse) webServiceTemplate.marshalSendAndReceive(saveCountryRequest); Assert.assertEquals(saveCountryResponse.getName(), "中国"); }