第二章 Spring Boot MVC

本章是《 Spring Boot 快速入门 》系列教程的第二章,若要查看本系列的所有章节,请点击 这里 前端

目录

  • 简介
  • 源码下载
  • 软件版本
  • 编写 Controller 类
  • 自动装载请求参数
  • 处理复杂类型的返回值
  • 自定义返回值转化类 HttpMessageConverter
  • 总结说明

简介

在上一章《Hello Spring Boot》中,咱们从一个最简单的应用程序入手,体验了 Spring Boot 的开发流程。 本章咱们会带着你们继续 Spring Boot 体验之旅,将 Hello Spring Boot 程序升级成 Web 版。 如今WEB开发比较流行的开发模式是先后端分离,也就是前端工程师专一于写前端的代码,用 ajax 调用后端的 Http Restful API 获取数据,然后台工程师则专一于实现 Http Restful API 便可,先后端在代码工程和部署上都是彻底分离的,这样只要接口定义好了就能够各自开发相互不干扰,协做效率更高。 所以,本文的重点是描述如何用 Spring Boot 开发 Http Restful API 服务。java

源码下载

本章的示例代码放在“码云”上,你们能够免费下载或浏览:git

https://git.oschina.net/terran4j/springboot/tree/master/springboot-web程序员

软件版本

相关软件使用的版本:web

  • Java: 1.8
  • Maven: 3.3.9

程序在以上版本均调试过,能够正常运行,其它版本仅做参考。ajax

编写 Controller 类

Spring Boot 默认使用 Spring MVC 做为WEB框架,处理HTTP请求须要编写 Controller 类,以下代码所示:spring

package com.terran4j.springboot.web;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HelloController {

	@Autowired
	private HelloService helloService;
	
	@RequestMapping(value = "/hello", method = RequestMethod.GET)
	public void sayHello(HttpServletRequest request, HttpServletResponse response) throws IOException {
		String name = request.getParameter("name");
		String msg = helloService.hello(name);
		response.getWriter().println(msg);
	}
	
}

首先, 要在类上面加 @Controller 注解,而后处理 HTTP 请求的方法上要加 @RequestMapping 注解,如:数据库

@RequestMapping(value = "/hello", method = RequestMethod.GET)

其中 value 是匹配的路径,method 是匹配的方法,主要有GET, POST, PUT, DELETE这么几种,上面这行的意思是路径为 /hello 的 GET 请求,都会让这个方法来处理,如:编程

http://localhost:8080/hello?name=terran4j

这里方法参数咱们先用原始的 HttpServletRequest 和 HttpServletResponse 做为入参,如:json

sayHello(HttpServletRequest request, HttpServletResponse response)

有的场景下咱们直接使用 HttpServletRequest 和 HttpServletResponse 对象会更灵活,但大多数实际场景下咱们用 @RequestParam 直接自动装载参数会更简单一些(这点后面的例子会讲到)。

而后咱们写一个 main 函数并运行它:

package com.terran4j.springboot.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloWebApp {

	public static void main(String[] args) {
		SpringApplication.run(HelloWebApp.class, args);
	}

}

运行起来后,咱们在浏览器输入URL:

http://localhost:8080/hello?name=terran4j

访问结果为:

Spring Boot MVC 1.png

自动装载请求参数

上面的示例中咱们直接从 HttpServletRequest 对象中读取参数,然而Spring MVC提供了更简单的方式,就是经过注解 @RequestParam 自动装载请求参数,以下代码所示:

// URL示例:  http://localhost:8080/hello2/param?name=terran4j
	@RequestMapping(value = "/hello2/param", method = RequestMethod.GET)
	@ResponseBody
	public String sayHelloByParam(@RequestParam("name") String name) {
		return helloService.hello(name);
	}

代码 @RequestParam("name") String name 的意思是自动读取key为 name 的 HTTP 请求参数,值装载到方法入参 name 中。 但这样写的话,若是HTTP请求中没有 name 参数时Spring MVC就会报错,解决的方法是这样写 @RequestParam(value = "name", defaultValue = "") 意思是给它设置一个默认值,当没有请求参数时就用默认值,默认值为空串。

这种自动装载的方式很智能,它还会根据参数对象的类型自动解析,好比还要一个 count 参数是数字类型的,能够直接定义成Integer 或 Long 类型的,如:

sayHello(@RequestParam("count") Integer count, @RequestParam("name") String name)

Spring MVC 会根据方法入参的类型自动解析成 Integer 类型,固然若是对应的 HTTP 参数不是数字就会报错。

另外,还能够用 @PathVariable 来装载URL路径中的值做为参数,如:

// URL示例:  http://localhost:8080/hello2/path/terran4j
	@RequestMapping(value = "/hello2/path/{name}", method = RequestMethod.GET)
	@ResponseBody
	public String sayHelloByPath(@PathVariable("name") String name) {
		return helloService.hello(name);
	}

它会自动从URL路径中 /hello2/path/ 后面的一节做为 name 参数的值。 若是读者比较了解 Restful 风格的 API 就知道这种方式很是适合 Restful API 。

注意,方法上必定不能少了 @ResponseBody 注解,它的意思是该方法的返回结果直接写入 HTTP Response Body 中。 至于写入的内容具体是什么,则由 HttpMessageConverter 来决定,Spring MVC中默认的 HttpMessageConverter 是将返回对象自动转成的json串,关于这一点下一节会讲到。

处理复杂类型的返回值

目前为止,咱们的返回值只是简单的 String 类型,若是是一个本身定义的复杂的 java bean 会怎么样呢? 咱们先本身定义一个名为 HelloBean 的java bean类,代码以下:

package com.terran4j.springboot.web;

import java.util.Date;

public class HelloBean {
	
	private String name;
	
	private String message;
	
	private Date time;

	public final String getName() {
		return name;
	}

	public final void setName(String name) {
		this.name = name;
	}

	public final String getMessage() {
		return message;
	}

	public final void setMessage(String message) {
		this.message = message;
	}

	public final Date getTime() {
		return time;
	}

	public final void setTime(Date time) {
		this.time = time;
	}

	@Override
	public String toString() {
		return "Hello [name=" + name + ", message=" + message + ", time=" + time + "]";
	}
	
}

这个 Bean 中有 name, msg 两个 String 类型的属性,还有 time 这个 Date 类型的属性,以及它们的 getter , setter 方法。

而后咱们将以前的Controller类改形成这样:

package com.terran4j.springboot.web;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController3 {
	
	private static final Logger log = LoggerFactory.getLogger(HelloController3.class);

	@Autowired
	private HelloService helloService;
	
	// URL示例:  http://localhost:8080/hello3?name=terran4j
	@RequestMapping(value = "/hello3", method = RequestMethod.GET)
	public HelloBean sayHello(@RequestParam("name") String name) {
		HelloBean hello = new HelloBean();
		hello.setName(name);
		hello.setMessage(helloService.hello(name));
		hello.setTime(new Date());
		if (log.isInfoEnabled()) {
			log.info("hello bean is: {}", hello);
		}
		return hello;
	}
	
}

注意: 上面代码中,类上的注解从 @Controller 变成 @RestController,而方法的注解少了个 @ResponseBody 。 其实 @RestController = @Controller + @ResponseBody,也就是说有了@RestController以后,至关于每一个方法(有@RequestMapping的)自动加上了 @ResponseBody 注解。

上面代码中,方法返回一个 HelloBean 对象,咱们运行main程序,而后访问URL:

http://localhost:8080/hello3?name=terran4j

结果以下:

Spring Boot MVC 3.1

能够看到Spring MVC 自动将 java bean 对象转成 json 串返回了。

但这仍是有一个问题, 属性 Date time 显示成了 long 类型的时间戳格式,而不是人类容易理解的格式(如“2017-03-09 18:42:00”),还有这个 json 串被压缩成一行很很差看,一般正式的项目返回的数据是比较多的,若是也像这样挤成一坨,那调试程序时看着也费劲啊。

那有没有办法对返回的 json 串结果优化一下呢? 答案显然是有的,下一节咱们将讲解如何经过注入自定义的 HttpMessageConverter 将返回值转化成咱们但愿的json串格式。

自定义返回值转化类 HttpMessageConverter

Spring MVC 在调用 Controller 的方法后,是用 HttpMessageConverter 类来将返回值转化成最终结果并写入到 Http Response Body 中的。 咱们能够本身定义一个 HttpMessageConverter 的对象,来代替 Spring MVC 默认提供的 HttpMessageConverter 对象。

以下代码所示:

package com.terran4j.springboot.web;

import java.text.SimpleDateFormat;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;

@EnableWebMvc
public class HelloWebMvcConfigurer extends WebMvcConfigurerAdapter {
	
	private static final Logger log = LoggerFactory.getLogger(HelloWebMvcConfigurer.class);

	public static final ObjectMapper createObjectMapper() {
		ObjectMapper objectMapper = new ObjectMapper();

		objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);

		// 属性为空时(包括 null, 空串,空集合,空对象),不参与序列化。
		objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

		// Date 对象在序列化时,格式为 yyyy年MM月dd日 HH时mm分ss秒 。
		objectMapper.setDateFormat(new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒"));

		objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
		objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
		objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

		// json串以良好的格式输出。
		objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);

		// 当属性为空或有问题时不参与序列化。
		objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

		// 未知的属性不参与反序列化。
		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		
		if (log.isInfoEnabled()) {
			log.info("create objectMapper done.");
		}
		return objectMapper;
	}

	@Override
	public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		ObjectMapper objectMapper = createObjectMapper();
		MappingJackson2HttpMessageConverter convertor = new MappingJackson2HttpMessageConverter(objectMapper);
		converters.add(0, convertor);
	}

}

首先咱们用createObjectMapper()方法本身建立一个 ObjectMapper 对象,ObjectMapper 是由开源项目 Jackson 提供的一个高性能 json 处理工具,它提供了 json 与 java 对象互相转化能力,而且有很是强大的可配置性,好比下面这行代码:

// Date 对象在序列化时,格式为 yyyy年MM月dd日 HH时mm分ss秒 。
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒"));

它的意思是说,能够把 Date 类型的对象,转成本身但愿的日期格式。 固然 ObjectMapper 还有不少配置项,就不一一详述了,有兴趣的朋友们能够自行在网上查找相关资料了解。

而后咱们将自定义的 HttpMessageConverter 注入到 Spring MVC中,以下代码所示:

@Override
	public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		ObjectMapper objectMapper = createObjectMapper();
		MappingJackson2HttpMessageConverter convertor = new MappingJackson2HttpMessageConverter(objectMapper);
		converters.add(0, convertor);
	}

Spring MVC 中可能有不少 HttpMessageConverter 对象,运行时是从 converters 列表中遍历,直到找到了一个 HttpMessageConverter 对象能处理当前的HTTP请求为止。 咱们要把本身的 HttpMessageConverter 对象加到 converters 列表的第1位,以便得到最高优先权,如代码: converters.add(0, convertor); 咱们使用了 jackson 项目提供的现成的 MappingJackson2HttpMessageConverter 类(它实现了 HttpMessageConverter 接口),咱们只要把本身建立的 ObjectMapper 对象传进去就能够了。

最后,咱们用 @Import 注解在主程序中引入这个类,代码以下:

package com.terran4j.springboot.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

@Import(HelloWebMvcConfigurer.class)
@SpringBootApplication
public class HelloWebApp {

	public static void main(String[] args) {
		SpringApplication.run(HelloWebApp.class, args);
	}

}

上面的 @Import(HelloWebMvcConfigurer.class) 就引入了咱们定义的 HelloWebMvcConfigurer 类到主程序中。

最后,咱们运行主程序,在浏览器输入URL:

http://localhost:8080/hello3?name=terran4j

结果以下:

引入 HelloWebMvcConfigurer 以后的结果

与以前的 json 串结果相比,格式变优雅了,Date 的显示格式也易读了: 引入 HelloWebMvcConfigurer 以前的结果

总结说明

本章是《 Spring Boot 快速入门 》系列的第二章,本章咱们讲解了如何使用 Spring Boot 开发 Http Restful 服务,下一章咱们会讲解在 Spring Boot 中如何访问数据库,欢迎你们继续学习下一章《 Spring Boot JPA 》。

点击 这里 能够查看本系列的所有章节。 (本系列的目标是帮助有 Java 开发经验的程序员们快速掌握使用 Spring Boot 开发的基本技巧,感觉到 Spring Boot 的极简开发风格及超爽编程体验。)

另外,咱们有一个名为 SpringBoot及微服务 的微信公众号,感兴趣的同窗请扫描下面的二维码关注下吧,关注后就能够收到咱们按期分享的技术干货哦! SpringBoot及微服务-公众号二维码

相关文章
相关标签/搜索