摘要:“约定优于配置”这是一个相当棒的经验,SOAP服务性能差、基于配置、紧耦合,restful服务性能好、基于约定、松耦合,现在我就把使用Spring MVC发布restful服务的过程同大家分享。代码之优雅、过程之简单、编码之愉快,不是发布SOAP服务所能匹敌的。
关键字:java, rest, webservice, spring mvc
前提:IntelliJ IDEA (13.1.5 版本), apache maven (3.2.3 版本), Tomcat(7.0.56版本), Spring(3.2.4版本)
“约定优于配置”这是一个相当棒的经验,对于我来说,恐怕是自“面向对象”以来对我最大冲击的理念了吧。首先,SOAP服务可以说是基于配置的,它在HTTP的基础上使用XML配置表达服务与数据,为了实现远程访问,它生成的服务信息尤其复杂,无论是C#客户端还是JAVA客户端,甚至可以通过WSDL服务描述来自动生成一整套代码,这样基于配置的服务的缺点就在于服务端与客户端有着较强的编码耦合性,服务端接口修改后,需要对客户端的服务依赖代码重新生成。而restful服务是基于约定的,是http的get、delete、post还是put都约定好了不同的含义,我们依照约定来进行http请求便可以实现各式的操作。更不用说SOAP服务的性能之差、与restful服务不是一个数量级之缺点了。基于约定,我们可以实现代码间的松耦合。虽然Hessian性能略优于restful服务、其实是一个数量级、restful编解码做好了就性能差不多,但Hessian也还是基于代码的紧耦合了。
首先,我们先在IDEA中新建一个名为dp-parent的Project,作为所有工程的父节点,其<packaging>pom</packaging>。然后,我们在dp-parent下新建一个名为dp-restfulservice的Module,其<packaging>war</packaging>,并在其pom.xml中添加其所需的spring依赖。如:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
其次,我们在src/main目录下创建名为webapp的目录,为什么要叫这个名字呢?这其实也是一个约定。并且,我们在webapp目录下创建名为WEB-INF的目录(为什么要叫这个名字呢?这其实也是一个约定)。在WEB-INF目录下创建名为web.xml的文件(为什么要叫这个名字呢?这其实也是一个约定,这句话我说了太多遍了,“约定优于配置”此理念真是百试不爽)。下面是web.xml的主要内容。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>restful</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restful</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>restful</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restful</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
然后,我们要在WEB-INF目录下创建一个名为restful-servlet.xml的文件(为什么要叫这个名字呢?这其实也是一个约定,在web.xml中规定的servlet-name加上-servlet约定为servlet配置文件名)。restful-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"
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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.jsc.dp.controllers"></context:component-scan>
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/"></mvc:resources>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
<!-- 如果 .jsp 文件中含有 jstl,需要这一行 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
</bean>
</beans>
<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"
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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.jsc.dp.controllers"></context:component-scan>
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/"></mvc:resources>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
<!-- 如果 .jsp 文件中含有 jstl,需要这一行 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
</bean>
</beans>
下面是要编写代码spring mvc中的C(Controller)了。在main/java中创建名为com.jac.dp.controllers的包,在此包下新建一个名为AbcController的类,其通过@Controller注解注入进spring框架中。具体的restful代码如下图所示。从图中可以看到,其实现了一个名为getAbcs的方法,使用get用以对get式的 http://localhost:8088/restfulservice/abcs 请求进行响应。还实现了一个名为getAbc的方法,使用get以对get式的 http://localhost:8088/restfulservice/abcs/3 请求进行响应,来获取某一对象,返回给前端的是一Json字符串:{"myInt":3,"myString":"myString"}。名为putAbc的方法,其使用put以对put式的 http://localhost:8088/restfulservice/abcs/3 请求进行响应,来更新某一对象。下图中也展示了,使用工具REST Client来进行http put请求测试的过程与结果,在这里,返回的是一个对象转成的Json字符串,在IDEA中可以看到其Response是:{"myInt":-1,"myString":"post true"},与预想中的一样;同样的post和get也可以如此测试,测试get请求也可以直接使用浏览器执行。
代码编写完成后,需要进行服务发布了,我们使用Tomcat作为服务容器。在IDEA中如何进行Tomcat配置就不多说了,这里提一下Run/Debug Configurations。在Application context中配置成restfulservice才有上面的效果哦。
有时候一个理念让人茅塞顿开、醍醐灌顶。“约定优于配置”就是这样的理念。感谢教给我这个理念的人,王某连云港人。