由于最近自己要写几个与外围系统的数据交互接口,并且要使用的webservice;公司最近改革用比较新颖的基于RESTFUL方式的webservice。
之前转过几篇关于RESTFUL框架的介绍,这里就再次做阐述了;废话不多说,下面直接开始上代码:
由于是基于maven 、spring 和cxf上进行搭建的服务;所以首先要大家maven工程;
(1)在pom中引入cxf 、spring的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf-version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-tools-common</artifactId>
<version>${cxf-version}</version>
</dependency>
<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-testutils</artifactId>
<version>${cxf-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf-version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf-version}</version>
<scope>test</scope>
</dependency>
又因为是在jetty下部署,所以pom要引入jetty的插件的依赖
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.5.0.v20110901</version>
<configuration>
<webAppConfig>
<contextPath>/test</contextPath>
</webAppConfig>
</configuration>
</plugin>
(2创建pojo
我这里创建了一个请求和响应的类/**
* @ClassName: Request
* @Description: 请求消息
* @author leo
* @date 2014-8-4 上午10:17:31
*/
public class Request {
/**
* @Fields n_id : 工号
*/
private String n_id;
/**
* @Fields n_name : 姓名
*/
private String n_name;
/**
* @Fields n_passwd : 密码
*/
private String n_passwd;
/**
* @return the n_id
*/
public String getN_id() {
return n_id;
}
/**
* @param n_id the n_id to set
*/
public void setN_id(String n_id) {
this.n_id = n_id;
}
/**
* @return the n_name
*/
public String getN_name() {
return n_name;
}
/**
* @param n_name the n_name to set
*/
public void setN_name(String n_name) {
this.n_name = n_name;
}
/**
* @return the n_passwd
*/
public String getN_passwd() {
return n_passwd;
}
/**
* @param n_passwd the n_passwd to set
*/
public void setN_passwd(String n_passwd) {
this.n_passwd = n_passwd;
}
}
response 类
/**
* @ClassName: Response
* @Description: 响应结果
* @author leo
* @date 2014-8-4 上午10:19:32
*/
@XmlRootElement(name="Response")
public class Response {
/**
* @Fields is_state : 登陆状态
*/
private boolean is_state;
/**
* @Fields cause : 登陆结果
*/
private String cause;
/**
* @return the is_state
*/
public boolean isIs_state() {
return is_state;
}
/**
* @param is_state the is_state to set
*/
public void setIs_state(boolean is_state) {
this.is_state = is_state;
}
/**
* @return the cause
*/
public String getCause() {
return cause;
}
/**
* @param cause the cause to set
*/
public void setCause(String cause) {
this.cause = cause;
}
/* (非 Javadoc)
*
*
* @return
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Response [is_state=" + is_state + ", cause=" + cause + "]";
}
}
【注】: 注解@XmlRootElement指定response为XML的根元素。response类的属性默认指定映射为@XmlElement。@XmlElement用来定义XML中的子元素。@XmlRootElement和@XmlElement允许自定义命名空间和XML中元素的名称。如果没有定义的话,JAXB在运行的时候默认的使用同样的属性名和类名来定义XML元素。
(3)创建服务端
我这里是创建了一个模拟登录的接口服务, 创建ILogin.java 的接口, 已经实现接口服务的实现类,实现了RESTFUL的get post请求
/**
* @ClassName: Loginimpl
* @Description: 登陆接口实现
* @author leo
* @date 2014-8-4 上午10:22:09
*/
@Path("/loginService")
@Produces({MediaType.APPLICATION_JSON}) //返回的类型是JSON
@Consumes({MediaType.APPLICATION_JSON}) //资源方法能够处理的类型是JSON ,
public class Loginimpl implements ILogin {
private static final Log LOG = LogFactory.getLog(Loginimpl.class);
@Override
@POST
@Path("/index") //指定路径是:basePath+/index
public Response login(Request request) {
try {
LOG.info("正在调用POST请求");
LOG.info(request.getN_name());
Response rs = new Response();
rs.setIs_state(true);
rs.setCause("login success");
return rs;
} catch (Throwable throwable) {
return null;
}
}
@Override
@GET
@Path("/login") //指定路径是basePath+/login
public Response login(@Context HttpServletResponse response) {
LOG.info("正在调用GET请求");
Response rs = new Response();
rs.setIs_state(true);
rs.setCause("login success");
response.addHeader("ESB-ResultCode", "1");
return rs;
}
}
【注】:service类 使用@Path、@Produces 、@Consumes、@get
@POST 等注解,指明了登录服务的给路径 是basePath+/login ,@Produces 是用来表示资源类方法能够返回的类型是JOSN,@Consumes 表示资源类方法能否处理的方法是JOSN类型的
接下来要在spring.xml 中配置上server 和login注入依赖:
<jaxrs:server id="mamApplication" address="/rest">
<span style="white-space:pre"> </span><jaxrs:serviceBeans>
<span style="white-space:pre"> </span><!-- 用户登陆 -->
<span style="white-space:pre"> </span><ref bean="login" />
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span></jaxrs:serviceBeans>
<span style="white-space:pre"> </span><jaxrs:extensionMappings>
<span style="white-space:pre"> </span><entry key="json" value="application/json" />
<span style="white-space:pre"> </span></jaxrs:extensionMappings>
<span style="white-space:pre"> </span><jaxrs:languageMappings>
<span style="white-space:pre"> </span><entry key="en" value="en-gb" />
<span style="white-space:pre"> </span></jaxrs:languageMappings>
<span style="white-space:pre"> </span><jaxrs:providers>
<span style="white-space:pre"> </span><ref bean="jacksonJsonProvider" />
<span style="white-space:pre"> </span></jaxrs:providers>
<span style="white-space:pre"> </span></jaxrs:server>
<span style="white-space:pre"> </span><bean id="login" class="com.deppon.rest.service.impl.Loginimpl" />
<span style="white-space:pre"> </span><!-- 定义jackson 日期转换格式 -->
<span style="white-space:pre"> </span><bean id="simpleDataformat" class="java.text.SimpleDateFormat">
<span style="white-space:pre"> </span><constructor-arg value="yyyy-MM-dd HH:mm:ss"></constructor-arg>
<span style="white-space:pre"> </span></bean>
<span style="white-space:pre"> </span><!-- jackson mapper -->
<span style="white-space:pre"> </span><bean id="mapper" class="org.codehaus.jackson.map.ObjectMapper">
<span style="white-space:pre"> </span><property name="dateFormat" ref="simpleDataformat"></property>
<span style="white-space:pre"> </span></bean>
<span style="white-space:pre"> </span><!-- jackson provider -->
<span style="white-space:pre"> </span><bean id="jacksonJsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider">
<span style="white-space:pre"> </span><property name="mapper" ref="mapper" />
<span style="white-space:pre"> </span></bean>
【注】:关于序列化问题, cxf中的数据序列化是可以替换掉使用你实现MessageBodyReader<Object>和MessageBodyWriter<Object>接口就可以啦,针对xml,cxf采用stax2、jaxb、xmlschema、Woodstox库,针对json默认使用jettison实现的几乎都是codehaus作品。知道cxf序列化和反序列化方式就比较容易解决问题啦。默认情况下cxf的jettison对泛型序列化存在问题,因为道行浅,没有具体去研究实现问题,我之前使用过jackson,去处理json问题,而且cxf拥有jackson的MessageBodyReader和MessageBodyWriter实现类,我只要导入包并告诉cxf使用我指定的json provider就可以了,所以在客户端和服务器端双方都指定json privoder,jackson 库对json序列化实现非常的到位,异常的强大 。
(4)接下来就是客户端 ,使用org.apache.cxf.jaxrs.client.WebClient调用RESTful服务;<pre name="code" class="java">/**
* @ClassName: ApalicationCxfHttp
* @Description: 利用cxf创建http客户端
* @author leo
* @date 2014-8-7 上午8:38:24
*/
public class ApalicationCxfHttp {
private static final Log LOG = LogFactory.getLog(ApalicationCxfHttp.class);
private static ApalicationCxfHttp apalicationCxfHttp = new ApalicationCxfHttp();
/**
* @Fields client : httpClient
*/
//使用org.apache.cxf.jaxrs.client.WebClient调用RESTful服务
private static WebClient client;
/**
* 创建http客户端
*/
public static final ApalicationCxfHttp createClient(String uri){
client = WebClient.create(uri);
client.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
return apalicationCxfHttp;
}
public static final Object invoke(Object obj,Class<?> cls){
try {
//json的转换
String json = JacksonTransformer.java2String(obj);
Response response = client.post(json);
//
String message = IOUtils.toString((InputStream) response.getEntity());
return JacksonTransformer.string2java(message, cls);
} catch (JsonGenerationException e) {
LOG.error(e.getMessage(), e);
} catch (JsonMappingException e) {
LOG.error(e.getMessage(), e);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
return null;
}
}
【注】:期间要用到jackson 对json序列化,这里写了一个JacksonTransformer的转换的工具类;
(5)由于是居于webApp下的 要在web.xml 中配置cxfServlet 和spring的监听
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:com/deppon/rest/META-INF/ds-spring.xml</param-value>
</context-param>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 日志配置文件的位置 -->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>CXFService</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFService</servlet-name>
<url-pattern>/rs/*</url-pattern>
</servlet-mapping>
(6) 启动 jetty 服务器,写了一个junit的测试类 ,测试post请求服务
public class ClientTest {
private final String uri = "http://localhost:8080/test/rs/rest/loginService/index";
private static final Log LOG = LogFactory.getLog(ClientTest.class);
@Test
public void cxfTest(){
ApalicationCxfHttp.createClient(uri);
Response response = (Response) ApalicationCxfHttp.invoke(createRequest(), Response.class);
LOG.info(response.toString());
}
public Request createRequest(){
Request request = new Request();
request.setN_id("000001");
request.setN_name("老板");
request.setN_passwd("123456");
return request;
}
}
结果是可以登录成功,说明webService服务成功了