作为一名优秀的(自认为)Android开发工程师,在工作之余研究一些新的有趣的技术是我的乐趣,每当有一些想法就会想去尝试实现,demo敲的多了就产生了自己制作一款个人的app的想法。但是如今互联网的发展,没有后端支持的单机app很难有亮点,每每一些有趣的想法终止于没有后端api的支持,多次之后终于打定主意,学习后端接口的开发,独立完成app前后端的实现。
后端的语言可选的有很多种,Android开发的我当仁不让地选择Java作为后端开发语言,可以节省不少时间。作为出现了一门十多年的语言,java在后端的发展也经历几个代表性的时代:早期的JSP+Servlet+JavaBean,之后的SSH(Spring+Struts2+Hibernate),以及现如今的SSM(Spring+SpringMVC+MyBatis)。
当你搜索“后端开发”,上述这些词很容易出现在搜索结果中,我也是看了很多资料才理清它们之间的关系。网络上搜索结果有很多JSP+Servlet+JavaBean以及SSH相关的教程,但我最终还是选择的最新的技术方案:SSM,新技术能取代老技术总有它特别的优势,要不更强大,要不使用更简便,因此最新的成熟技术总会是好的。并且工具也没有选择Eclipse或者MyEclipse,而是IDEA,虽然对电脑的配置要求更高,但是在代码提示以及项目管理方面有着很大的优势,而且Android studio也是基于IDEA,界面和快捷方式都是一样的,非常方便android开发者的适应过度。
一开始我也是跟随 这篇文章 搭建环境并运行了第一个web项目。新建项目时用spring boot 的方式,用于取代传统Spring项目繁琐的xml配置,其配置全部可以通过注解的形式进行配置,后续会具体说明。
附带一说:IDEA是需要付费的,而且一年的价格还不便宜,表示买不起啊!!当时找到了在线免费生成IntelliJ IDEA 15.0注册码 由于我还在30天试用期,不确定是否真的有用,但是设置后确实没有弹出提示付费的通知,但愿是有效的。
搭建环境的过程中也遇到一些问题,如果你也遇到了,可以看一下:
Mac 安装mysql问题
如果你对sql语句不熟悉,也可以看下:常用SQL语句汇总
Workbench之类的mysql可视化工具也是必不可少的,可以非常方便地操作数据库。
开始之前最好像了解下MVC设计模式,SpringMVC流程,如:
SpringMVC流程架构图
Spring MVC 流程图
用于标记类,表示这个类是SpringMVC的一个Controller,通常于@RequestMapping配合使用,指定匹配的路径。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloContorller {
@RequestMapping("/hello")//指定匹配路径
@ResponseBody//将返回数据直接放入responseBody中
public String helloWorld() {
return "hello world!";
}
}
由于我们要做的事app的后端接口,因此加上了@ResponseBody注解。如果不清楚responseBody可以看下http协议的相关知识,如:HTTP协议详解(真的很经典)
使用Postman测试一下接口:
@RequestMapping除了放在方法上,也可以放在类上,表示这个Controller类中所有方法的共同路径前缀
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
@RequestMapping("/hello")
public class HelloContorller {
@RequestMapping("/world")
public String helloWorld() {
return "hello world!";
}
}
此时访问的路径为:http://localhost:8080/hello/world
同样的@ResponseBody放在类上表示其下所有方法都默认注解了@ResponseBody
@RequestMapping(value = "/world", method = RequestMethod.GET, produces = "application/json", params = "myParam=myValue", headers = "Refer=http://www.example.com")
@RequestMapping有很多属性用来匹配请求:
value指定路径,默认可以省略
method指定请求方式,只有指定的请求方式可以访问到该方法
consumes指定处理请求的提交内容类型(即Content-Type)
produces指定返回的内容类型
paran指定request中必须包含指定的参数值
header指定request中必须包含指定的header值
@RequestParam注解用于将制定类型的请求参数赋值方法中的形参
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController//相当于同时注解了@Controller + @ResponseBody
public class HelloContorller {
@RequestMapping("/hello")
public String helloWorld(@RequestParam("name") String name, @RequestParam("password") String password) {
return name + "/" + password;
}
}
@RequestParam也有几种属性:
value指定参数名,可以省略,默认于注解的参数名相同
required指定参数是否事必须的,默认必须,如果参数必须但是请求未加参数,则返回400.
defaultValue 如果没有传递参数,使用的默认值
与@RequestParam类似的还有几种注解,功能也类似,用于获取request不同的参数。
@PathVariable 可以获取请求url中的动态参数
@RequestMapping("/pathVariableTest/{id}")
public Integer pathVariable(@PathVariable(required = false) Integer id) {
return id;
}
@RequsetHeader 将请求 的头信息区数据映射到功能处理方法的参数上。
@CookieValue将请求的Cookies数据映射到功能处理方法的参数上。
SpringMVC提供了处理json格式请求/响应的HttpMessageConverter:MappingJackson2HttpMessageConverter。
当使用Psotman发送请求时,需要注意编码格式的请求头的COntentType指定:
1.application/x-www-form-urlencoded,这种情况的数据@RequestParam和@RequestBody都可以处理;
2.multipart/form-data,@RequestBody不能处理;
3.application/json,application/xml等格式必须使用@RequestBody来处理;
@RequestMapping("/json")
public User json(@RequestBody User user) {
LOGGER.info(user.toString());
user.setName("change");
return user;
}
public class User {
private Integer id;
private String name;
private String sex;
private Integer age;
//省略getter,setter和构造
注意发送请求时请求方式为Post,Body属性选择raw,并且text选择JSON
Spring拥有自己独立的数据校验框架Validation,推荐使用实现了Validation的JSR303校验:Hibernate Validator
通过在gradle文件中引入compile group: ‘org.hibernate’, name: ‘hibernate-validator’, version: ‘5.3.4.Final’
dependencies {
compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.0')
compile('org.springframework.boot:spring-boot-starter-web')
runtime('mysql:mysql-connector-java')
testCompile('org.springframework.boot:spring-boot-starter-test')
compile group: 'org.hibernate', name: 'hibernate-validator', version: '5.3.4.Final'
}
就可以使用JSR303数据校验了。
通过添加对应注解增加限制,例如上一例的User
public class User {
private Integer id;
@Size(max = 18 ,message = "姓名不能超过18位字符")
@NotNull
private String name;
@Size(max = 2)
private String sex;
@Max(value = 200,message = "年龄不能大于200")
private Integer age;
//省略getter,setter和构造
}
然后在需要进行校验的方法中添加@Valid注解
@RequestMapping("/json")
public User json(@Valid @RequestBody User user) {
LOGGER.info(user.toString());
user.setName("change");
return user;
}
这里我用到了全局处理异常的Handler
import com.example.demo.bean.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.validation.UnexpectedTypeException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 全局异常处理拦截者,当抛出异常时执行对应的方法
*/
@ResponseBody
@ControllerAdvice
public class ApiExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ApiExceptionHandler.class);
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(UnexpectedTypeException.class)//当抛出此异常时执行该方法
public Result unexpectedType(UnexpectedTypeException exception) {
LOGGER.error("校验方法太多,不确定合适的校验方法。", exception);
return Result.error(exception.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public Result messageNotReadable(HttpMessageNotReadableException exception) {
LOGGER.error("请求参数不匹配,request的json格式不正确", exception);
return Result.error(exception.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result ex(MethodArgumentNotValidException exception) {
LOGGER.error("请求参数不合法。", exception);
BindingResult bindingResult = exception.getBindingResult();
return new Result
当数据校验异常时,实际上抛出MethodArgumentNotValidException异常,@ControllerAdvice注解类中有@ExceptionHandler(MethodArgumentNotValidException.class)注解的方法会执行,将错误信息返回给调用者。
Result类是一个标准的结果实体类:
public class Result {
public static final int CODE_OK = 200;
public static final int CODE_ERROR = 400;
private int code;
private String message;
private T data;
public Result() {
}
public Result(int code, String message) {
this.code = code;
this.message = message;
}
public Result(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static Result error(String message) {
return new Result(CODE_ERROR, message);
}
public static Result ok(T data) {
return new Result(CODE_OK, "ok", data);
}
}
未完待续。。。。