近来有些事,好久不写博客了,惭愧,说好的,连续翻译spring学习系列。我会持续翻译http://spring.io/guides/ 上的这些开发指南。英文好的同学可以直接看英文,本人翻译水平有限。今天翻译的一篇指南是:http://spring.io/guides/gs/rest-service/ 学习spring rest接口开发。阮一峰有两篇对rest接口介绍的文章,写得很通俗,峰哥的文章一贯如此。
理解RESTful架构:http://www.ruanyifeng.com/blog/2011/09/restful.html
RESTful API 设计指南:http://www.ruanyifeng.com/blog/2014/05/restful_api.html
接下来我们要做什么:
我们会创建一个接收http get请求的web服务:
http://localhost:8080/greeting
然后服务器会给我们返回一个json字符串的问候内容:
{"id":1,"content":"Hello, World!"}
阮一峰也有一篇介绍JSON的文章:
数据类型和JSON格式:http://www.ruanyifeng.com/blog/2009/05/data_types_and_json.html
我们也可以在问候请求查询字符串(url)中个性化的添加一个可选的name参数:
http://localhost:8080/greeting?name=shfqps:我叫石锋强,shfq是我姓名的简称。
name参数的值会覆盖掉默认值world,最终会反映到响应结果中:
{"id":1,"content":"Hello, shfq!"}
我们还需要什么?
15分钟的时间
一个喜欢的文本编辑器或者是一个IDE
JDK1.6或者是更高版本
Gradle1.1或者更高版本或者是Maven3.0或者更高版本
ps:Gradle也是类似Maven的一种Java构建工具,本人没有用过,仅Google过相关介绍资料
接下来我们开始搭建工程
我用的IDE是IntellJ IDEA12.1.4,构建工具是Maven
工程结构如图:
首先创建一个代表资源(向我们问候的一个字符串)的类Greeting
接下来我们将要创建一个工程然后搭建一个系统,创建web 服务。
在开始之前,让我们思考一下整个服务交互的过程:
web服务会处理带有/greeting的GET请求的查询字符串(就是一个url),我们还可以加一个可选的name参数。
在正常情况下GET请求会返回一个 200 的状态码表示一切OK和一个响应体,响应体有一个代表问候的JSON字符串。形如这样的一个问候的字符串:
{ "id": 1, "content": "Hello, World!" }
id这个字段唯一的对应着服务器对客户端请求的一个响应(任何一次向服务器的请求都会产生一个新的id),content代表服务器端向客户端响应的内容,就是问候的一句话。
为了建立一个问候的模型,我们创建一个代表资源的类,提供一个包含两个字段、一个构造方法、还有对id、content的访问方法的简单而古老的Java对象。
src/main/java/hello/Greeting.java
package hello; public class Greeting { private final long id; private final String content; public Greeting(long id, String content) { this.id = id; this.content = content; } public long getId() { return id; } public String getContent() { return content; } }
注意:我们将会在接下来的描述过程中,看到Spring使用Jackson JSON库来自动地将Greeting类型转化为JSON字符串。
接下来我们将会创建一个资源控制器,这个资源控制器将负责处理客户端向服务器发送的问候请求。
创建一个资源控制器
在Spring创建的rest接口web服务中,http请求是被控制器处理的。这些组件(一个控制器就是一个组件)很容易地被@Controller注解标识,下面的GreetingController处理了
一个含有/greeting的一个字符串的GET请求,并对每次请求都返回给了客户端一个新的Greeting实例。
src/main/java/hello/GreetingController.java
package hello; import java.util.concurrent.atomic.AtomicLong; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class GreetingController { private static final String template = "Hello, %s!"; private final AtomicLong counter = new AtomicLong(); @RequestMapping("/greeting") public @ResponseBody Greeting greeting( @RequestParam(value="name", required=false, defaultValue="World") String name) { return new Greeting(counter.incrementAndGet(), String.format(template, name)); } }这个控制器简单而简洁,但在这背后仍然有许多值得我们探究的东西,下面我们将会一步一步的来分解、分析它。
@RequestMapping(@RequestMapping(“/greeting”))注解能保证带有/greeting的http请求将会映射到greeting()方法中。
注意:在上面的例子中并没有指定GET、PUT、POST等http请求方法的类型,因为
@RequestMapping默认会映射所有的http操作请求。
@RequestMapping(method=GET)只会映射http GET请求方法。
@RequestMapping绑定了查询参数name中的值到greeting(String name)方法中的name。
查询字符串中的name参数是可选的,如果在查询请求字符串中缺少了name参数,defaultValue “World”将会派上用场。
在方法体的实现中,创建了一个Greeting对象并返回。这个Greeting对象有id、content两个属性,其中id是基于counter自增后的值,content是被name参数使用了问候的模板格式化了。
传统的Spring MVC控制器和Spring rest接口web服务中的控制器有个关键的不同,在Spring rest接口中,响应内容是在控制器中创建的(在本控制器中创建并返回的Greeting就是响应内容,最终被转化成了一个JSON字符串),而在传统的Spring MVC开发中的控制器中返回的一般是一个字符串(见),这个字符串会与一个jsp或者是html文件匹配,而这个文件就是MVC中的view视图。而不是依靠视图技术在服务器端把表现问候的数据转换为HTML,rest接口web服务的控制器仅仅是简单的new一个Greeting对象并返回。这个对象将会转化为JSON格式直接地写给HTTP响应。为了完成这一切,在greeting()方法上的@ResponseBody 注解会告诉spring MVC不需要通过服务器端视图层把问候的Greeting对象来表现,而是把打招呼的Greeting返回作为响应体直接写出。从greeting()方法中新建并返回的Greeting对象一定要转化成JSON对象。幸亏Spring的HTTP消息转化器的支持,我们不需要自己人工转化。因为Jackson 2 在classpath中,Spring的MappingJackson2HttpMessageConverter会自动选择将Greeting实例对象转化为JSON格式的字符串。
使这个应用可执行化
尽管可以把这个服务打包为一个传统的war文件来部署到一个外部的应用服务器上。下面创建了一个孤立的应用,可以相对简单的来演示。我们把所有的都打包成一个可执行的JAR包,被一个古老而好用的main()方法驱动。顺着这条路,我们用Spring对内置的tomcat servlet容器的支持来作为http运行时,而不是作为一个外部的实例来部署。
src/main/java/hello/Application.java
package hello; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.context.annotation.ComponentScan; @ComponentScan @EnableAutoConfiguration public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }main()方法听从SpringApplication帮助类,提供Application.class作为一个参数传递给SpringApplication的run()方法。这样一来,Spring就知道从Application类文件中读取annotation注解元数据,并在Spring 应用上下文中作为一个组件来管理。
@ComponentScan 注解会告诉Spring递归搜索 hello 包和它的子路径下直接或者是间接地标记了Spring的@Component注解的类。这直接保证了Spring能找到并注册@GreetingController,因为它被@Controller注解标记,@Controller反过来也会作为一种@Component注解。
@EnableAutoConfiguration 注解会转化基于classpath上的合理内容。例如,咱们刚才创建的依赖于tomcat-embed-core.jar包内置的tomcat版本的这个应用,tomcat服务器启动中会站在我们的立场上为我们配置一些合理的默认值。
因为应用也依靠Spring MVC(spring-webmvc.jar),在不需要web.xml的情况下为你配置和注册了一个Spring MVC DispatcherServlet。Auto-configuration是一种强大、灵活的机制。查看API 文档可以了解更详细的情况。
测试服务
服务启动以后,访问http://localhost:8080/greeting,你将会看到返回:
(悲剧,电脑的主板出了问题,送售后维修了,现在用的是另外一台电脑,修好后会补上截图)
我们也可以在查询字符串中加一个参数http://localhost:8080/greeting?name=User。我们会发现content的值将由"Hello,World!"变为"Hello User!"
这个变化体现出了在GreetingController中的@RequestParam注解的安排正如我们期望的一样。
name参数已经给了一个默认的值"World",但是我们可以通过在查询字符串中显示的指定一个参数值来覆盖掉默认值。
注意id属性怎样就从1变为2了。这就证明了我们的多次请求是在同一个GreetingController的实例处理的,GreetingController的counter字段
正如我们期望的那样在每次访问后都加1。
总结
恭喜我们,我们已经用Spring开发完了一个rest接口web服务(也恭喜我终于翻译完了,从前天晚上开始翻译,中途有停顿,到现在终于翻译完了,收收获还是挺大的, 在翻译的过程中注意到了很多细节的地方,如果只是阅读可能就不会注意到某些方面了)。