1、课程名称:Restful风格的Spring MVC实现

2、课程内容

对于整个现在求职来讲,包括工作来讲,掌握Restful架构的思想还是很重要的,一切都是资源操作。

2.1、基础整合配置

1、 建立一个SpringRestProject项目,并且为项目添加Spring的支持;

2、 如果要想使用restful风格,实际上还需要有jackson的开发包;

· jackson-core-2.7.5.jar、jackson-annotations-2.7.0.jar、jackson-databind-2.7.5.jar

3、 在web.xml文件里面进行相关配置;

· 很多人在进行Spring MVC开发的时候都愿意单独建立一个配置文件,假设:rest-servlet.xml文件,这个文件本质上就是Spring的配置文件;

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:rest-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

3、 建立rest-servlet.xml文件,这个文件可以直接拷贝applicationContext.xml文件修改;

<context:annotation-config/>
<context:component-scan base-package="cn.mldn"/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>

4、 建立一个Action,这个名称为EmpAction,此Action将使用Restful风格路径进行访问;

package cn.mldn.action;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class EmpAction {
@RequestMapping(value="/hello" , produces="text/plain;charset=UTF-8")
public @ResponseBody String hello() { // 表示返回的内容是回应的主体信息 
return "魔乐科技(www.mldn.cn)" ;
}
}

访问地址:http://localhost/SpringRestProject/hello

5、 之前返回的都是文本数据,文本肯定不够用,所以下面直接返回JSON内容;

@RequestMapping(value="/echo/{msg}" , produces="application/json;charset=UTF-8")
public @ResponseBody String echo(@PathVariable("msg") String msg) {
return "{echo : " + msg + "}" ;
}

地址:http://localhost/SpringRestProject/echo/mldn

整个的操作基于Restful风格的架构,直接进行信息传递以及各种变量使用,本次使用的是路径变量。

2.2、Restful风格的CRUD实现

所谓的Restful的风格核心的本质在于路径的处理上,所以这个时候就可以使用这些操作的形式完成一个标准的Restful风格的CRUD处理操作。

1、 定义一个Emp.java程序类,实现数据的接收以及回应,同时考虑到最终的VO需要转换为JSON或者是XML文件,那么就必须让其使用“@XmlRootElement”注解。

package cn.mldn.vo;
import java.io.Serializable;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
@SuppressWarnings("serial")
@XmlRootElement
public class Emp implements Serializable {
private Integer empno ;
private String ename ;
private Double sal ;
private Date hiredate ;
}

2、 既然有了Date的操作处理,那么自然需要在EmpAction程序类里面增加转换器配置;

@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(sdf,true));
}

那么此时所有的相关准备就到位了。

2.2.1、增加雇员

3、 由于操作之中要以JSON结构返回,所以将json的相关开发包配置到项目之中;

4、  Restful风格的操作返回的结果尽量使用json结构完成,同时Spring MVC的接收参数的过程也非常的简单,只需要编写上VO类即可;

@RequestMapping(value="/emp",method = RequestMethod.POST)
public @ResponseBody Object add(Emp emp) {
System.out.println("【增加雇员】" + emp);
JSONObject obj = new JSONObject() ;
obj.put("msg",true) ;
return obj ; 
}

此时的add()方法使用了“/emp”的模式映射路径,必须使用表单提交才可以执行此方法。

5、 编写表单;

<form action="<%=basePath%>emp" method="post">
雇员编号:<input type="text" name="empno" id="empno" value="7369"><br>
雇员姓名:<input type="text" name="ename" id="ename" value="史密斯"><br>
基本工资:<input type="text" name="sal" id="sal" value="88888.88"><br>
雇佣日期:<input type="text" name="hiredate" id="hiredate" value="1988-10-10"><br>
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>

发送出去的内容是POST提交,而返回的内容是一个JSON的字符串。

2.2.2、取得雇员信息

如果要想完成取得雇员的Restful风格,可以这样编写“/emp/7369”。

6、 在EmpAction中增加一个取得的操作方法,需要使用路径变量传送数据;

@RequestMapping(value = "/emp/{empno:\\d+}", produces = "application/json;charset=UTF-8", method = RequestMethod.GET)
public @ResponseBody Object get(@PathVariable("empno") int empno) {
System.out.println("【取得雇员】" + empno);
Emp vo = new Emp() ;
vo.setEmpno(empno);
vo.setEname("SMITH");
vo.setSal(800.0);
vo.setHiredate(new java.util.Date());
return vo ; 
}

访问路径:http://localhost/SpringRestProject/emp/7369

这个时候对于日期数据的返回严格意义上来讲,返回的是日期时间数字,有了这个数字就可以将其变为日期型的数据,例如:利用JavaScript中的Date类实现操作(var date = new Date(数字))。

但是如果说现在你的代码里面要求只是返回XML结构的数据,那么可以编写一个转换的适配器程序类:

package cn.mldn.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class JaxbDateAdapter extends XmlAdapter<String, Date> {
private static final String STAND_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
@Override
public Date unmarshal(String v) throws Exception {
if (v == null) {
return null;
}
return new SimpleDateFormat(STAND_DATE_FORMAT).parse(v);
}
@Override
public String marshal(Date v) throws Exception {
if (v == null) {
return null;
}
return new SimpleDateFormat(STAND_DATE_FORMAT).format(v); 
}
}

随后在VO类中在取得日期数据的时候可以使用此适配器的操作定义:

@XmlJavaTypeAdapter(JaxbDateAdapter.class

public Date getHiredate() {

return hiredate;

}

这样在以xml形式返回之后,此时将可以自动格式化:

@RequestMapping(value = "/emp/{empno:\\d+}", 

produces = "application/xml;charset=UTF-8", method = RequestMethod.GET)

如果此时不是使用XML,而使用的是JSON的数据,那么只能够将返回的内容交给JS处理;就需要进行一些格式化的处理操作;

7、 编写一个get.jsp页面,此页面将使用ajax()原生函数进行数据的读取处理;

· 将jquery开发包配置到项目之中;

<script type="text/javascript" src="js/mldn.js"></script>

<script type="text/javascript" src="js/jquery.min.js"></script>

<script type="text/javascript">
$(function() {
$.ajax({
url : 'emp/7369' ,
type : 'get' ,
dataType : 'json' ,
success : function(data) {
console.log("雇员编号:" + data.empno) ;
console.log("雇员姓名:" + data.ename) ;
console.log("基本工资:" + data.sal) ;
console.log("雇佣日期:" + new Date(data.hiredate).format("yyyy-MM-dd hh:mm:ss")) ;
} ,
error : function() {
alert("对不起,操作出现了错误!") ;
}
}) ;
}) 
</script>

当取得了日期时间数字的时候是可以将其转换为正常可以读懂的日期时间数据。

2.2.3、编辑雇员信息

在Restful风格的操作里面,对于编辑的操作应该于增加的操作的路径是一样的,也就是说此时对于编辑的路径应该设置为“/emp”才合适,可以与增加区分的唯一的方式就是在HTTP请求模式上。
8、 在EmpAction中定义edit()方法,具体的形式与add()相同;

@RequestMapping(value="/emp",method = RequestMethod.PUT)
public @ResponseBody Object edit(Emp emp) {
System.out.println("【编辑雇员】" + emp); 
JSONObject obj = new JSONObject() ;
obj.put("msg",true) ;
return obj ; 
}

PUT的提交模式必须通过jQuery进行操作,但是你也需要知道,这个和浏览器有关系。

9、 编写一个edit.jsp页面;

<script type="text/javascript">
$(function() {
$.ajax({
url : 'emp' ,
type : 'post' , // 在请求模式只能够设置GET、POST、DELETE,而对于PUT需要设置“_method”参数
dataType : 'json' ,
data : {
_method : 'PUT' ,
empno : 7566 ,
ename : '王五' ,
sal : 8999.00 ,
hiredate : '2004-07-07'
} ,
success : function(data) {
console.log(data) ;
} ,
error : function() {
alert("对不起,操作出现了错误!") ;
}
}) ;
}) 
</script>

对于PUT的模式需要根据你的浏览器来决定。

2.2.4、删除雇员信息

如果要进行雇员信息的删除处理,实际上路径和根据ID查询没有区别,唯一的区别在于,删除操作中的HTTP模式应该设置为DELETE模式。

10、 在EmpAction中定义删除的操作:

@RequestMapping(value = "/emp/{empno:\\d+}", produces = "application/json;charset=UTF-8", method = RequestMethod.DELETE)
public @ResponseBody Object rm(@PathVariable("empno") int empno) {
System.out.println("【删除雇员】" + empno);
JSONObject obj = new JSONObject() ;
obj.put("msg",true) ;
return obj ; 
}

11、 由于此时使用的是DELETE处理模式,所以依然要使用ajax()原生函数实现操作的调用;

<script type="text/javascript">
$(function() {
$.ajax({
url : 'emp/7369' ,
type : 'delete' ,
dataType : 'json' ,
success : function(data) {
console.log(data) ;
} ,
error : function() {
alert("对不起,操作出现了错误!") ;
}
}) ;
}) 
</script>

这个DELETE模式如果在不同的浏览器中也可能有问题。

2.2.5、数据完整列表

所谓的数据完整列表指的就是查询表中的全部数据内容,那么很明显,这个时候返回的一定是List集合内容。幸运的是JAXB默认支持集合的处理模式。

12、 在EmpAction中建立一个列表的处理方法:

@RequestMapping(value = "/emp", produces = "application/json;charset=UTF-8", method = RequestMethod.PATCH)
public @ResponseBody Object list() {
List<Emp> allEmps = new ArrayList<Emp>();
for (int x = 0; x < 10; x++) {
Emp vo = new Emp();
vo.setEmpno(x);
vo.setEname("雇员  - " + x);
vo.setSal(8000.0 + x);
vo.setHiredate(new java.util.Date());
allEmps.add(vo) ;
}
return allEmps;
}

13、 建立一个list.jsp页面,现在使用的是PATCH的处理模式,所以这个时候可以发送一个PATCH请求。

<script type="text/javascript">
$(function() {
$.ajax({
url : 'emp' ,
type : 'patch' ,
dataType : 'json' ,
success : function(data) {
for (var x = 0 ; x < data.length ; x ++) { 
console.log("雇员编号:" + data[x].empno + 
"雇员姓名:" + data[x].ename + 
"基本工资:" + data[x].sal +
"雇佣日期:" + new Date(data[x].hiredate).format("yyyy-MM-dd hh:mm:ss")) ;
}
} ,
error : function() {
alert("对不起,操作出现了错误!") ;
}
}) ;
}) 
</script>

考虑到浏览器的问题,对于PATCH的传递可以以PATCH请求模式的方式进行设置。

2.2.6、分页显示

在实际的开发之中分页的列表处理操作才是核心的关键所在。

14、 考虑到需要进行分页等参数的接收处理,建议专门准备出一个分页的工具类;

package cn.mldn.util;
public class SplitPageUtil {
private Integer cp = 1 ;
private Integer ls = 10 ;
private String col ;
private String kw ;
public void setCp(String cp) {
try {
this.cp = Integer.parseInt(cp);
} catch (Exception e) {}
}
public void setLs(String ls) {
try {
this.ls = Integer.parseInt(ls);
} catch (Exception e) {}
}
public void setCol(String col) {
this.col = col;
}
public String getCol() {
return col;
}
public Integer getCurrentPage() {
return this.cp ;
}
public Integer getLineSize() {
return this.ls ;
}
public String getColumn() {
return this.col ;
}
public String getKeyword () {
return this.kw ; 
}
}

15、 在EmpAction之中进行分页的控制;

@RequestMapping(value = "/emp/{condition}", produces = "application/json;charset=UTF-8", method = RequestMethod.PATCH)
public @ResponseBody Object listSplit(
@PathVariable("condition") String condition, SplitPageUtil spu) {
Map<String, Object> map = new HashMap<String, Object>();
List<Emp> allEmps = new ArrayList<Emp>();
for (int x = (spu.getCurrentPage() - 1) * spu.getLineSize(); x < spu
.getCurrentPage() * spu.getLineSize(); x++) {
Emp vo = new Emp();
vo.setEmpno(x);
vo.setEname("雇员  - " + x);
vo.setSal(8000.0 + x);
vo.setHiredate(new java.util.Date());
allEmps.add(vo);
}
map.put("condition", condition) ;
map.put("empCount", 8000) ;
map.put("allEmps", allEmps) ; 
return map;
}

16、 此时编写一个split.jsp页面;

<script type="text/javascript">
$(function() {
$.ajax({
url : 'emp/split' ,
type : 'patch' ,
dataType : 'json' ,
data : {
cp : 2 ,
ls : 30 ,
col : 'ename' ,
kw : 'smith'
} ,
success : function(data) {
console.log("condition = " + data.condition) ;
console.log("empCount = " + data.empCount) ;
for (var x = 0 ; x < data.allEmps.length ; x ++) { 
console.log("雇员编号:" + data.allEmps[x].empno + 
"雇员姓名:" + data.allEmps[x].ename + 
"基本工资:" + data.allEmps[x].sal +
"雇佣日期:" + new Date(data.allEmps[x].hiredate).format("yyyy-MM-dd hh:mm:ss")) ;
}
} ,
error : function() {
alert("对不起,操作出现了错误!") ;
}
}) ;
}) 
</script>

此时Restful风格的CRUD操作已经彻底完成了。