Spring、Spring Boot、Spring MVC、Spring REST如何使用

时间:2021-6-4 作者:qvyue

前言

Spring框架就像一个家族,有众多衍生产品例如boot、security、jpa等等。但他们的基础都是Spring的ioc和aop。ioc提供了依赖注入的容器,aop解决了面向横切面的编程;然后在此二者的基础上实现了其他延伸产品的高级功能。

Spring MVC是基于 Servlet 的一个 MVC 框架,主要解决 WEB 开发的问题。

后来因为 Spring 的配置非常复杂,各种XML、 JavaConfig处理起来比较繁琐。于是为了方便开发者使用,从而又推出了Spring BOOT,简化了Spring的配置流程。

说得更简便一些:Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。大家觉得挺好用,于是按照这种模式搞了一个MVC框架(一些用Spring解耦的组件),用来开发web应用,最终形成了SpringMVC。然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring BOOT。

什么是Spring?它解决了什么问题?

Spring,一般指代的是Spring Framework,它是一个开源的应用程序框架,提供了一个简易的开发方式,通过这种开发方式,将避免那些可能致使代码变得繁杂混乱的大量的业务/工具对象,说的更通俗一点就是由框架来帮你管理这些对象,包括它的创建,销毁等,比如基于Spring的项目里经常能看到的Bean,它代表的就是由Spring管辖的对象。

而在被管理对象与业务逻辑之间,Spring通过IOC(控制反转)架起使用的桥梁,IOC也可以看做Spring最核心最重要的思想,通过IOC能带来什么好处呢?首先来看一个实际开发中的典型应用场景,假设我们有一个基于MVC分层结构的应用,通过controller层对外提供接口,而通过service层提供具体的实现,在service层中有一个WelcomeService服务接口,一般情况下都是通过WelcomeService service = new WelcomeServiceImpl();创建实例并进行调用:

public class WelcomeController {
    private WelcomeService service = new WelcomeServiceImpl();

    @RequestMapping("/welcome")
    public String welcome() {
    return service.retrieveWelcomeMessage();
    }
}

调用后发现一切正常,此时,功能提交,需要进行测试,而由于实际应用环境与测试环境有所区别,需要替换WelcomeServiceImpl为一个MockWelcomeServiceImpl,以方便测试,怎么办?没有其他办法,只有改代码:

public class WelcomeController {
    private WelcomeService service = new MockWelcomeServiceImpl();

    ...
}

测试OK后再将代码改回去,这种方式太过于繁琐,且对代码的侵入性很强;
下面看通过Spring的IOC如何实现,首先将WelcomeService交由Spring管理:

然后在业务代码处通过Spring IOC拿到具体对象:

public class WelcomeController {
    @Autowired
    private WelcomeService service;

    @RequestMapping("/welcome")
    public String welcome() {
        return service.retrieveWelcomeMessage();
    }
}

测试的时候,只需要更改配置文件,将WelcomeService对应的实现改为MockWelcomeServiceImpl即可:

这种方式对业务代码没有任何侵入,它有效的实现松耦合,大家都知道紧耦合的代码是业务发展的噩梦;同时,Spring IOC提供的远不止这些,如通过单例减少创建无用的对象,通过延迟加载优化初始化成本等

当然,Spring 的核心功能远不知这些,如:

  • Spring AOP
  • Spring JDBC
  • Spring MVC
  • Spring ORM
  • Spring JMS
  • Spring Test

其实不通过Spring框架依然可以实现这些功能特定,但是Spring 提供了更优雅的抽象接口以方便对这些功能的组装,同时又给予每个具体实现以灵活的配置;另外,基于Spring,你可以方便的与其他框架进行集成,如hibernateibatis等,Spring官方的原则是绝不重复造轮子,有好的解决方案只需要通过Spring进行集成即可。纵览Spring的结构,你会发现Spring Framework 本身并未提供太多具体的功能,它主要专注于让你的项目代码组织更加优雅,使其具有极好的灵活性和扩展性,同时又能通过Spring集成业界优秀的解决方案,想了解Spring的核心实现机制可参考tiny spring 项目

为什么使用Spring ?

  1. 方便解耦,简化开发

通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。

  1. AOP编程的支持

通过Spring提供的AOP功能,方便进行面向切面的编程,如性能监测、事务管理、日志记录等。

  1. 声明式事务的支持

  2. 方便集成各种优秀框架

  3. 降低Java EE API的使用难度

如对JDBC,JavaMail,远程调用等提供了简便封装

什么是IoC,为什使用IoC ?

IoC全称Iversion of Controller,控制反转。

这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。

它能指导我们如何设计出松耦合、更优良的程序。

什么是AOP,为什么使用AOP ?

AOP全称:Aspect-Oriented Programming,面向切面编程。

AOP,面向切面编程,就是把可重用的功能提取出来,然后将这些通用功能在合适的时候织入到应用程序中,比如事务管理、权限控制、日志记录、性能统计等。

AOP并没有帮助我们解决任何新的问题,它只是提供了一种更好的办法,能够用更少的工作量来解决现有的一些问题,使得系统更加健壮,可维护性更好。

什么是Spring的事务管理?

事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。

开发中为了避免这种情况一般都会进行事务管理。

Spring的声明式事务通常是指在配置文件中对事务进行配置声明,其中包括了很多声明属性,它是通过Spring Proxy帮你做代理,自己不用额外的写代码,只要在Spring配置文件中声明即可;通常用在数据库的操作里面;

编程式事务就是指通过硬编码的方式做事务处理,这种处理方式需要写代码,事务中的逻辑可以自己定制;可以是数据库的东东,也可以是其他的操作。

Spring中也有自己的事务管理机制,一般是使用TransactionMananger进行管理,可以通过Spring的注入来完成此功能。

Spring框架支持以下五种bean的作用域:

singleton : 默认值,bean在每个Spring ioc 容器中只有一个实例。

prototype:一个bean的定义可以有多个实例。

request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。

session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

什么是Spring的MVC框架?

Spring 配备构建Web 应用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成,如Struts,Spring 的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。

spring mvc是一个基于mvc的web框架。spring mvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合。

如何启用注解:

如果使用 则上面内容可以省略

Spring MVC的请求流程:

第一步:发起请求到前端控制器(DispatcherServlet)

第二步:前端控制器请求HandlerMapping查找Handler可以根据xml配置、注解进行查找

第三步:处理器映射器HandlerMapping向前端控制器返回Handler

第四步:前端控制器调用处理器适配器去执行Handler

第五步:处理器适配器去执行Handler

第六步:Handler执行完成给适配器返回ModelAndView

第七步:处理器适配器向前端控制器返回ModelAndView。ModelAndView是springmvc框架的一个底层对象,包括 Model和view

第八步:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(jsp)

第九步:视图解析器向前端控制器返回View

第十步:前端控制器进行视图渲染。视图渲染将模型数据(在ModelAndView对象中)填充到request域

第十一步:前端控制器向用户响应结果

web.xml的配置

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

注解的处理器映射器和适配器

spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping注解映射器。

在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter注解适配器。

使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置

spring 与 mybatis整合过程

第一步:整合dao层

mybatis和spring整合,通过spring管理mapper接口。

使用mapper的扫描器自动扫描mapper接口在spring中进行注册。

第二步:整合service层

通过spring管理 service接口。

使用配置方式将service接口配置在spring配置文件中。

实现事务控制。

第三步:整合springmvc

由于springmvc是spring的模块,不需要整合。

主要配置有:

1). mybatis配置文件sqlMapConfig.xml配置别名自动扫描(实体类)

2). mapper扫描器(接口,数据库访问接口)

3). 数据库连接池配置

4). 声明式事务配置

5). 启用注解扫描:

6). 配置注解映射器和适配器:

7). 视图解析器:

8). 配置控制类: DispatcherServlet前端控制器

9). 配置spring配置文件加载类:ClassLoadListener

前端控制器从上边的文件中加载处理映射器、适配器、视图解析器等组件,如果不在springmvc.xml中配置,使用默认加载的 DispatcherSerlvet.properties。

视图解析器配置前缀和后缀:

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

sqlMapConfig.xml,mybatis自己的配置文件。

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

配置数据源:

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

事务控制(applicationContext-transaction.xml),在applicationContext-transaction.xml中使用spring声明式事务控制方法。

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

加载spring配置

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

静态资源访问不被拦截:

  

@RequestMapping的作用

1). url映射

2). 窄化请求映射

3). 限制http请求方法

controller方法的返回值

**返回ModelAndView **

需要方法结束时,定义ModelAndView,将model和view分别进行设置。

返回string

如果controller方法返回string,

1). 表示返回逻辑视图名。真正视图(jsp路径)=前缀+逻辑视图名+后缀

2). redirect重定向:返回字符串格式为:”redirect:queryItem.action”

3). forward页面转发:返回字符串格式为:“forward:queryItem.action”

返回void

在controller方法形参上可以定义request和response,使用request或response指定响应结果:

1). 使用request转向页面,如下:request.getRequestDispatcher(“页面路径”).forward(request, response);

2). 也可以通过response页面重定向:response.sendRedirect(“url”)

3). 也可以通过response指定响应结果,例如响应json数据如下:

  • response.setCharacterEncoding(“utf-8”);

  • response.setContentType(“application/json;charset=utf-8”);

  • response.getWriter().write(“json串”);

参数绑定:

默认支持的类型

直接在controller方法形参上定义下边类型的对象,就可以使用这些对象。在参数绑定过程中,如果遇到下边类型直接进行绑定。

1). HttpServletRequest:通过request对象获取请求信息

2). HttpServletResponse:通过response处理响应信息

3). HttpSession:通过session对象得到session中存放的对象

4). Model/ModelMap:model是一个接口,modelMap是一个接口实现 。作用:将model数据填充到request域。

简单类型

  • 通过@RequestParam对简单类型的参数进行绑定。

  • 如果不使用@RequestParam,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。

  • 如果使用@RequestParam,不用限制request传入参数名称和controller方法的形参名称一致。

  • 通过required属性指定参数是否必须要传入,如果设置为true,没有传入参数,会报错。

pojo绑定

页面中input的name和controller的pojo形参中的属性名称一致,将页面中数据绑定到pojo。(usename,age;不需要user.username,user.age)

自定义参数绑定实现日期类型绑定

对于controller形参中pojo对象,如果属性中有日期类型,需要自定义参数绑定。将请求日期数据串转成 日期类型,要转换的日期类型和pojo中日期属性的类型保持一致。

Spring MVC 和 Struts2 对比

1.Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC 就容易实现restful url

2.由上边原因,SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。

3.由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。

4.SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。

5.springmvc面向方法开发的(更接近service接口的开发方式),struts2面向类开发。

6.springmvc可以单例开发,struts2只能是多例开发。

乱码处理

1). post乱码

在web.xml添加post乱码filter:CharacterEncodingFilter

2). 对于get请求中文参数出现乱码解决方法有两个:

a. 修改tomcat配置文件添加编码与工程编码一致,如下:

b. 对参数进行重新编码:

String userName = new String(request.getParamter(“userName”).getBytes(“ISO8859-1″),”utf-8”)

ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码

集合类型绑定

1). 数组绑定:

controller方法参数使用:(Integer[] itemId)

页面统一使用:itemId 作为name

2). list绑定:

pojo属性名为:itemsList

页面:itemsList[index].属性名

3). map 绑定:

pojo属性名为:Map itemInfo = new HashMap();

页面:

姓名:

spring 校验

1). 项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。

2). springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。

校验思路:页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。

数据回显

1). @ModelAttribute还可以将方法的返回值传到页面:在方法上加注解@ModelAttribute

2). 使用最简单方法使用model,可以不用@ModelAttribute:model.addAttribute(“id”, id);

3). springmvc默认对pojo数据进行回显。pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)

4). public String testParam(PrintWriter out, @RequestParam(“username”) String username) { //out直接输出

异常处理

springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。

系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

上传图片

1). 在页面form中提交enctype=”multipart/form-data”的数据时,需要springmvc对multipart类型的数据进行解析。

2). 在springmvc.xml中配置multipart类型解析器。

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

3). 方法中使用:MultipartFile attach (单个文件上传) 或者 MultipartFile[] attachs (多个文件上传)

Json处理

1). 加载json转换的jar包:springmvc中使用jackson的包进行json转换(@requestBody和@responseBody使用下边的包进行json转)

2). 配置json转换器。在注解适配器RequestMappingHandlerAdapter中加入messageConverters。如果使用 则会自动加入。

3). ajax

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

4). Controller (ResponseBody、RequestBody)

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

注意ajax中contentType如果不设置为json类型,则传的参数为key/value类型。上面设置后,传的是json类型。

详见:http://www.cnblogs.com/Jtianlin/p/5224885.html

拦截器:

1). 定义拦截器,实现HandlerInterceptor接口。接口中提供三个方法。

a. preHandle :进入 Handler方法之前执行,用于身份认证、身份授权,比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行

b. postHandle:进入Handler方法之后,返回modelAndView之前执行,应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图

c. afterCompletion:执行Handler完成执行此方法,应用场景:统一异常处理,统一日志处理

2). 拦截器配置:

a. 针对HandlerMapping配置(不推荐):springmvc拦截器针对HandlerMapping进行拦截设置,如果在某个HandlerMapping中配置拦截,经过该 HandlerMapping映射成功的handler最终使用该 拦截器。 (一般不推荐使用)

b. 类似全局的拦截器:springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

如何启用注解:

如果使用 则上面内容可以省略 使用 mvc:annotation-driven代替注解映射器和注解适配器配置


什么是Spring Boot?它解决了什么问题?

初期的Spring通过代码加配置的形式为项目提供了良好的灵活性和扩展性,但随着Spring越来越庞大,其配置文件也越来越繁琐,太多复杂的xml文件也一直是Spring被人诟病的地方,特别是近些年其他简洁的WEB方案层出不穷,如基于Python或Node.Js,几行代码就能实现一个WEB服务器,对比起来,大家渐渐觉得Spring那一套太过繁琐,此时,Spring社区推出了Spring Boot,它的目的在于实现自动配置,降低项目搭建的复杂度,如需要搭建一个接口服务,通过Spring Boot,几行代码即可实现,请看代码示例:

//引入spring-boot-starter-web依赖
org.springframework.bootspring-boot-starter-web
//声明Spring Boot应用,直接写业务逻辑即可
@Controller
@SpringBootApplication
public class MockServerApplication {
    @RequestMapping("/hi")
    @ResponseBody
    String home() {
        return "how are you!";
    }

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

你甚至都不用额外的WEB容器,直接生成jar包执行即可,因为spring-boot-starter-web模块中包含有一个内置tomcat,可以直接提供容器使用;基于Spring Boot,不是说原来的配置没有了,而是Spring Boot有一套默认配置,我们可以把它看做比较通用的约定,而Spring Boot遵循的也是约定优于配置原则,同时,如果你需要使用到Spring以往提供的各种复杂但功能强大的配置功能,Spring Boot一样支持

在Spring Boot中,你会发现你引入的所有包都是

starter

形式,如:

  • spring-boot-starter-web-services,针对SOAP Web Services
  • spring-boot-starter-web,针对Web应用与网络接口
  • spring-boot-starter-jdbc,针对JDBC
  • spring-boot-starter-data-jpa,基于hibernate的持久层框架
  • spring-boot-starter-cache,针对缓存支持
  • 等等

Spring Boot对starter的解释如下:

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

这句话的译意为:

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

Spring REST如何使用?

准备工作

1、 下载jar包

spring各版本jar下载地址:http://ebr.springsource.com/repository/app/library/detail?name=org.springframework.spring

相关的依赖包也可以在这里找到:http://ebr.springsource.com/repository/app/library

2、 需要jar包如下

Spring、Spring Boot、Spring MVC、Spring REST如何使用
image.png

3、 当前工程的web.xml配置

dispatcherorg.springframework.web.servlet.DispatcherServletcontextConfigLocation/WEB-INF/dispatcher.xml1dispatcher*.doindex.jsp

4、 WEB-INF中的dispatcher.xml配置


5、 启动后,可以看到index.jsp 没有出现异常或错误。那么当前SpringMVC的配置就成功了。

二、REST控制器实现

REST控制器主要完成CRUD操作,也就是对于http中的post、get、put、delete。

还有其他的操作,如head、options、trace。

具体代码:

package com.hoo.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
 
/**
 * function:SpringMVC REST示例
 * @author hoojo
 * @createDate 2011-6-9 上午11:34:08
 * @file RESTController.java
 * @package com.hoo.controller
 * @project SpringRestWS
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
@RequestMapping("/restful")
@Controller
public class RESTController {
    
    @RequestMapping(value = "/show", method = RequestMethod.GET)
    public ModelAndView show() {
        System.out.println("show");
        ModelAndView model = new ModelAndView("xStreamMarshallingView");
        model.addObject("show method");
        return model; 
    }
    
    @RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
    public ModelAndView getUserById(@PathVariable String id) {
        System.out.println("getUserById-" + id);
        ModelAndView model = new ModelAndView("xStreamMarshallingView");
        model.addObject("getUserById method -" + id);
        return model; 
    }
    
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public ModelAndView addUser(String user) {
        System.out.println("addUser-" + user);
        ModelAndView model = new ModelAndView("xStreamMarshallingView");
        model.addObject("addUser method -" + user);
        return model; 
    }
    
    @RequestMapping(value = "/edit", method = RequestMethod.PUT)
    public ModelAndView editUser(String user) {
        System.out.println("editUser-" + user);
        ModelAndView model = new ModelAndView("xStreamMarshallingView");
        model.addObject("editUser method -" + user);
        return model;
    }
    
    @RequestMapping(value = "/remove/{id}", method = RequestMethod.DELETE)
    public ModelAndView removeUser(@PathVariable String id) {
        System.out.println("removeUser-" + id);
        ModelAndView model = new ModelAndView("xStreamMarshallingView");
        model.addObject("removeUser method -" + id);
        return model;
    }
}

上面的方法对应的http操作:

/show -> get 查询
/get/id -> get 查询
/add -> post 添加
/edit -> put 修改
/remove/id -> delete 删除

在这个方法中,就可以看到RESTful风格的url资源标识

@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
public ModelAndView getUserById(@PathVariable String id) {
    System.out.println("getUserById-" + id);
    ModelAndView model = new ModelAndView("xStreamMarshallingView");
    model.addObject("getUserById method -" + id);
    return model; 
}

value=”/get/{id}”就是url中包含get,并且带有id参数的get请求,就会执行这个方法。这个url在请求的时候,会通过Annotation的@PathVariable来将url中的id值设置到getUserById的参数中去。 ModelAndView返回的视图是xStreamMarshallingView是一个xml视图,执行当前请求后,会显示一篇xml文档。文档的内容是添加到model中的值。

三、利用RestTemplate调用REST资源

代码如下:

package com.hoo.client;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
 
/**
 * function:RestTemplate调用REST资源
 * @author hoojo
 * @createDate 2011-6-9 上午11:56:16
 * @file RESTClient.java
 * @package com.hoo.client
 * @project SpringRestWS
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
@Component
public class RESTClient {
    
    @Autowired
    private RestTemplate template;
    
    private final static String url = "http://localhost:8080/SpringRestWS/restful/";
    
    public String show() {
        return template.getForObject(url + "show.do", String.class, new String[]{});
    }
    
    public String getUserById(String id) {
        return template.getForObject(url + "get/{id}.do", String.class, id); 
    }
    
    public String addUser(String user) {
        return template.postForObject(url + "add.do?user={user}", null, String.class, user);
    }
    
    public String editUser(String user) {
        template.put(url + "edit.do?user={user}", null, user);
        return user;
    }
    
    public String removeUser(String id) {
        template.delete(url + "/remove/{id}.do", id);
        return id;
    }
}
  • RestTemplate的getForObject完成get请求、postForObject完成post请求、put对应的完成put请求、delete完成delete请求;还有execute可以执行任何请求的方法,需要你设置RequestMethod来指定当前请求类型。
  • RestTemplate.getForObject(String url, Class responseType, String… urlVariables)
  • 参数url是http请求的地址,参数Class是请求响应返回后的数据的类型,最后一个参数是请求中需要设置的参数。
  • template.getForObject(url + “get/{id}.do”, String.class, id);
  • 如上面的参数是{id},返回的是一个string类型,设置的参数是id。最后执行该方法会返回一个String类型的结果。

下面建立一个测试类,完成对RESTClient的测试。代码如下:

package com.hoo.client;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests;
 
/**
 * function:RESTClient TEST
 * @author hoojo
 * @createDate 2011-6-9 下午03:50:21
 * @file RESTClientTest.java
 * @package com.hoo.client
 * @project SpringRestWS
 * @blog http://blog.csdn.net/IBM_hoojo
 * @email hoojo_@126.com
 * @version 1.0
 */
@ContextConfiguration("classpath:applicationContext-*.xml")
public class RESTClientTest extends AbstractJUnit38SpringContextTests {
    
    @Autowired
    private RESTClient client;
    
    public void testShow() {
        System.out.println(client.show());
    }
    
    public void testGetUserById() {
        System.out.println(client.getUserById("abc"));
    }
    
    public void testAddUser() {
        System.out.println(client.addUser("jack"));
    }
    
    public void testEditUser() {
        System.out.println(client.editUser("tom"));
    }
    
    public void testRemoveUser() {
        System.out.println(client.removeUser("aabb"));
    }
}

我们需要在src目录下添加applicationContext-beans.xml完成对restTemplate的配置。restTemplate需要配置MessageConvert将返回的xml文档进行转换,解析成JavaObject。

上面配置了xStreamMarshaller是和RESTController中的ModelAndView的view对应的。因为那边是用xStreamMarshaller进行编组的,所以RestTemplate这边也需要用它来解组。RestTemplate还指出其他的MarshallingHttpMessageConverter

推荐阅读更多精彩内容

  • 阿里面试(Java岗):spring全家桶4面,Spring+boot+Cloud+MVC
    前言 相比于前几年来说,现在的面试难度提升了不少。你说说现在程序员这么多,你投递的公司可能与你一起投递的就有300…
    java高并发阅读 370评论 0赞 4
  • struts、hibernate、spring、 mybatis、 spring boot 关于
    1、谈谈你对Struts的理解。 答: 1.struts是一个按MVC模式设计的Web层框架,其实它就是一个大大的…
    慕容小伟阅读 1,660评论 0赞 13
  • spring/spring-boot
    90. 为什么要使用 spring? 简介:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器…
    CgySHFF阅读 79评论 0赞 0
  • 如何在Spring Boot应用中优雅的使用Date和LocalDateTime
    Java8已经发布很多年了,但是很多人在开发时仍然坚持使用着Date和SimpleDateFormat进行时间操作…
    杨绿寒轻阅读 2,800评论 0赞 14
  • Spring MVC的工作机制简单理解
    单独写这一篇我其实蛮忐忑的,毕竟我对于框架源码并不敢说完全“理解”,根本没到那种对原理吃透的程度,很大程度上只能算…
    加班狗老王阅读 84评论 0赞 0
评论2
赞6
抽奖

reward

6赞7赞
赞赏
更多好文

{“dataManager”:”[]”,”props”:{“isServer”:true,”initialState”:{“global”:{“done”:false,”artFromType”:null,”fontType”:”black”,”$modal”:{“ContributeModal”:false,”RewardListModal”:false,”PayModal”:false,”CollectionModal”:false,”LikeListModal”:false,”ReportModal”:false,”QRCodeShareModal”:false,”BookCatalogModal”:false,”RewardModal”:false},”$ua”:{“value”:”Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36″,”isIE11″:false,”earlyIE”:null,”chrome”:”58.0″,”firefox”:null,”safari”:null,”isMac”:false},”$diamondRate”:{“displayable”:false,”rate”:0},”readMode”:”day”,”locale”:”zh-CN”,”seoList”:[{“comments_count”:0,”public_abbr”:”前言 相比于前几年来说,现在的面试难度提升了不少。你说说现在程序员这么多,你投递的公司可能与你一起投递的就有300…”,”share_image_url”:”https://upload-images.jianshu.io/upload_images/15593451-527663ecb1d53725.png”,”slug”:”4b9c50916fe2″,”user”:{“id”:15593451,”nickname”:”java高并发”,”slug”:”e2c8c5808732″,”avatar”:”https://cdn2.jianshu.io/assets/default_avatar/6-fd30f34c8641f6f32f5494df5d6b8f3c.jpg”},”likes_count”:4,”title”:”阿里面试(Java岗):spring全家桶4面,Spring+boot+Cloud+MVC”,”id”:53639311,”views_count”:370},{“comments_count”:0,”public_abbr”:”1、谈谈你对Struts的理解。 答: 1.struts是一个按MVC模式设计的Web层框架,其实它就是一个大大的…”,”share_image_url”:””,”slug”:”a2ec21bf14f6″,”user”:{“id”:8286055,”nickname”:”慕容小伟”,”slug”:”e79074dcce23″,”avatar”:”https://upload.jianshu.io/users/upload_avatars/8286055/6b358ed2-8d45-4649-a41c-d5930355ac34.png”},”likes_count”:13,”title”:”struts、hibernate、spring、 mybatis、 spring boot 关于”,”id”:23494555,”views_count”:1660},{“comments_count”:0,”public_abbr”:”90. 为什么要使用 spring? 简介:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器…”,”share_image_url”:”https://upload-images.jianshu.io/upload_images/20515679-34319db74a3d664f.png”,”slug”:”7a8e4ff148d2″,”user”:{“id”:20515679,”nickname”:”CgySHFF”,”slug”:”e4851418a8c5″,”avatar”:”https://upload.jianshu.io/users/upload_avatars/20515679/da51acc1-bfce-4aa7-9b4d-11ab8339f9d6″},”likes_count”:0,”title”:”spring/spring-boot”,”id”:65813402,”views_count”:79},{“comments_count”:0,”public_abbr”:”Java8已经发布很多年了,但是很多人在开发时仍然坚持使用着Date和SimpleDateFormat进行时间操作…”,”share_image_url”:””,”slug”:”ccab14aef228″,”user”:{“id”:12041297,”nickname”:”杨绿寒轻”,”slug”:”60aa1a125369″,”avatar”:”https://cdn2.jianshu.io/assets/default_avatar/2-9636b13945b9ccf345bc98d0d81074eb.jpg”},”likes_count”:14,”title”:”如何在Spring Boot应用中优雅的使用Date和LocalDateTime”,”id”:72691106,”views_count”:2800},{“comments_count”:0,”public_abbr”:”单独写这一篇我其实蛮忐忑的,毕竟我对于框架源码并不敢说完全“理解”,根本没到那种对原理吃透的程度,很大程度上只能算…”,”share_image_url”:”https://upload-images.jianshu.io/upload_images/23378019-a78ece9063d2b8a8.png”,”slug”:”5eae2707f98a”,”user”:{“id”:23378019,”nickname”:”加班狗老王”,”slug”:”f3fb9d927356″,”avatar”:”https://upload.jianshu.io/users/upload_avatars/23378019/c4b2d48b-24e8-4725-a495-e22360724fff”},”likes_count”:0,”title”:”Spring MVC的工作机制简单理解”,”id”:73465246,”views_count”:84}]},”note”:{“data”:{“is_author”:false,”last_updated_at”:1617011068,”public_title”:”Spring、Spring Boot、Spring MVC、Spring REST如何使用”,”purchased”:false,”liked_note”:false,”comments_count”:2,”free_content”:”u003ch2u003e前言u003c/h2u003enu003cpu003eSpring框架就像一个家族,有众多衍生产品例如boot、security、jpa等等。但他们的基础都是Spring的ioc和aop。ioc提供了依赖注入的容器,aop解决了面向横切面的编程;然后在此二者的基础上实现了其他延伸产品的高级功能。u003c/pu003enu003cpu003eSpring MVC是基于 Servlet 的一个 MVC 框架,主要解决 WEB 开发的问题。u003c/pu003enu003cpu003e后来因为 Spring 的配置非常复杂,各种XML、 JavaConfig处理起来比较繁琐。于是为了方便开发者使用,从而又推出了Spring BOOT,简化了Spring的配置流程。u003c/pu003enu003cpu003e说得更简便一些:Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。大家觉得挺好用,于是按照这种模式搞了一个MVC框架(一些用Spring解耦的组件),用来开发web应用,最终形成了SpringMVC。然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring BOOT。u003c/pu003enu003ch2u003e什么是Spring?它解决了什么问题?u003c/h2u003enu003cpu003eSpring,一般指代的是Spring Framework,它是一个开源的应用程序框架,提供了一个简易的开发方式,通过这种开发方式,将避免那些可能致使代码变得繁杂混乱的大量的业务/工具对象,说的更通俗一点就是由框架来帮你管理这些对象,包括它的创建,销毁等,比如基于Spring的项目里经常能看到的u003ccodeu003eBeanu003c/codeu003e,它代表的就是由Spring管辖的对象。u003c/pu003enu003cpu003e而在被管理对象与业务逻辑之间,Spring通过IOC(控制反转)架起使用的桥梁,IOC也可以看做Spring最核心最重要的思想,通过IOC能带来什么好处呢?首先来看一个实际开发中的典型应用场景,假设我们有一个基于MVC分层结构的应用,通过controller层对外提供接口,而通过service层提供具体的实现,在service层中有一个u003ccodeu003eWelcomeServiceu003c/codeu003e服务接口,一般情况下都是通过u003ccodeu003eWelcomeService service = new WelcomeServiceImpl();u003c/codeu003e创建实例并进行调用:u003c/pu003enu003cpreu003eu003ccode class=”c”u003epublic class WelcomeController {n private WelcomeService service = new WelcomeServiceImpl();nn @RequestMapping(“/welcome”)n public String welcome() {n return service.retrieveWelcomeMessage();n }n}nu003c/codeu003eu003c/preu003enu003cpu003e调用后发现一切正常,此时,功能提交,需要进行测试,而由于实际应用环境与测试环境有所区别,需要替换u003ccodeu003eWelcomeServiceImplu003c/codeu003e为一个u003ccodeu003eMockWelcomeServiceImplu003c/codeu003e,以方便测试,怎么办?没有其他办法,只有改代码:u003c/pu003enu003cpreu003eu003ccode class=”c”u003epublic class WelcomeController {n private WelcomeService service = new MockWelcomeServiceImpl();nn …n}nu003c/codeu003eu003c/preu003enu003cpu003e测试OK后再将代码改回去,这种方式太过于繁琐,且对代码的侵入性很强;u003cbru003en下面看通过Spring的IOC如何实现,首先将u003ccodeu003eWelcomeServiceu003c/codeu003e交由Spring管理:u003c/pu003enu003cpreu003eu003ccode class=”c”u003eu0026lt;bean name=”WelcomeService” class=”XXX.XXX.XXX.service.impl.WelcomeServiceImpl”/u0026gt;nu003c/codeu003eu003c/preu003enu003cpu003e然后在业务代码处通过Spring IOC拿到具体对象:u003c/pu003enu003cpreu003eu003ccode class=”c”u003epublic class WelcomeController {n @Autowiredn private WelcomeService service;nn @RequestMapping(“/welcome”)n public String welcome() {n return service.retrieveWelcomeMessage();n }n}nu003c/codeu003eu003c/preu003enu003cpu003e测试的时候,只需要更改配置文件,将WelcomeService对应的实现改为MockWelcomeServiceImpl即可:u003c/pu003enu003cpreu003eu003ccode class=”c”u003eu0026lt;bean name=”WelcomeService” class=”XXX.XXX.XXX.service.impl.MockWelcomeServiceImpl”/u0026gt;nu003c/codeu003eu003c/preu003enu003cpu003e这种方式对业务代码没有任何侵入,它有效的实现松耦合,大家都知道紧耦合的代码是业务发展的噩梦;同时,Spring IOC提供的远不止这些,如通过单例减少创建无用的对象,通过延迟加载优化初始化成本等u003c/pu003enu003cpu003e当然,Spring 的核心功能远不知这些,如:u003c/pu003enu003culu003enu003cliu003eSpring AOPu003c/liu003enu003cliu003eSpring JDBCu003c/liu003enu003cliu003eSpring MVCu003c/liu003enu003cliu003eSpring ORMu003c/liu003enu003cliu003eSpring JMSu003c/liu003enu003cliu003eSpring Testu003c/liu003enu003c/ulu003enu003cpu003e其实不通过Spring框架依然可以实现这些功能特定,但是Spring 提供了更优雅的抽象接口以方便对这些功能的组装,同时又给予每个具体实现以灵活的配置;另外,基于Spring,你可以方便的与其他框架进行集成,如u003ccodeu003ehibernateu003c/codeu003e,u003ccodeu003eibatisu003c/codeu003e等,Spring官方的原则是绝不重复造轮子,有好的解决方案只需要通过Spring进行集成即可。u003cstrongu003e纵览Spring的结构,你会发现Spring Framework 本身并未提供太多具体的功能,它主要专注于让你的项目代码组织更加优雅,使其具有极好的灵活性和扩展性,同时又能通过Spring集成业界优秀的解决方案u003c/strongu003e,想了解Spring的核心实现机制可参考u003ca href=”https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Fcode4craft%2Ftiny-spring” target=”_blank”u003etiny spring 项目u003c/au003eu003c/pu003enu003ch2u003e为什么使用Spring ?u003c/h2u003enu003colu003enu003cliu003e方便解耦,简化开发u003c/liu003enu003c/olu003enu003cpu003e通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。u003c/pu003enu003col start=”2″u003enu003cliu003eAOP编程的支持u003c/liu003enu003c/olu003enu003cpu003e通过Spring提供的AOP功能,方便进行面向切面的编程,如性能监测、事务管理、日志记录等。u003c/pu003enu003col start=”3″u003enu003cliu003eu003cpu003e声明式事务的支持u003c/pu003eu003c/liu003enu003cliu003eu003cpu003e方便集成各种优秀框架u003c/pu003eu003c/liu003enu003cliu003eu003cpu003e降低Java EE API的使用难度u003c/pu003eu003c/liu003enu003c/olu003enu003cpu003e如对JDBC,JavaMail,远程调用等提供了简便封装u003c/pu003enu003ch2u003e什么是IoC,为什使用IoC ?u003c/h2u003enu003cpu003eIoC全称Iversion of Controller,控制反转。u003c/pu003enu003cpu003e这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。u003c/pu003enu003cpu003e它能指导我们如何设计出松耦合、更优良的程序。u003c/pu003enu003ch2u003e什么是AOP,为什么使用AOP ?u003c/h2u003enu003cpu003eAOP全称:Aspect-Oriented Programming,面向切面编程。u003c/pu003enu003cpu003eAOP,面向切面编程,就是把可重用的功能提取出来,然后将这些通用功能在合适的时候织入到应用程序中,比如事务管理、权限控制、日志记录、性能统计等。u003c/pu003enu003cpu003eAOP并没有帮助我们解决任何新的问题,它只是提供了一种更好的办法,能够用更少的工作量来解决现有的一些问题,使得系统更加健壮,可维护性更好。u003c/pu003enu003ch2u003e什么是Spring的事务管理?u003c/h2u003enu003cpu003e事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。u003c/pu003enu003cpu003e开发中为了避免这种情况一般都会进行事务管理。u003c/pu003enu003cpu003eSpring的声明式事务通常是指在配置文件中对事务进行配置声明,其中包括了很多声明属性,它是通过Spring Proxy帮你做代理,自己不用额外的写代码,只要在Spring配置文件中声明即可;通常用在数据库的操作里面;u003c/pu003enu003cpu003e编程式事务就是指通过硬编码的方式做事务处理,这种处理方式需要写代码,事务中的逻辑可以自己定制;可以是数据库的东东,也可以是其他的操作。u003c/pu003enu003cpu003eSpring中也有自己的事务管理机制,一般是使用TransactionMananger进行管理,可以通过Spring的注入来完成此功能。u003c/pu003enu003ch2u003eSpring框架支持以下五种bean的作用域:u003c/h2u003enu003cpu003esingleton : 默认值,bean在每个Spring ioc 容器中只有一个实例。u003c/pu003enu003cpu003eprototype:一个bean的定义可以有多个实例。u003c/pu003enu003cpu003erequest:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。u003c/pu003enu003cpu003esession:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。u003c/pu003enu003cpu003eglobal-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。u003c/pu003enu003ch2u003e什么是Spring的MVC框架?u003c/h2u003enu003cpu003eSpring 配备构建Web 应用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成,如Struts,Spring 的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。u003c/pu003enu003cpu003espring mvc是一个基于mvc的web框架。spring mvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合。u003c/pu003enu003ch3u003e如何启用注解:u003c/h3u003enu003cblockquoteu003enu003cpu003eu0026lt;context:annotation-config/u0026gt;u003c/pu003enu003c/blockquoteu003enu003cpu003e如果使用u0026lt;context:component-scan base-package=”com.tgb.web.controller.annotation”u0026lt;/context:component-scanu0026gt; 则上面内容可以省略u003c/pu003enu003ch3u003eSpring MVC的请求流程:u003c/h3u003enu003cpu003e第一步:发起请求到前端控制器(DispatcherServlet)u003c/pu003enu003cpu003e第二步:前端控制器请求HandlerMapping查找Handler可以根据xml配置、注解进行查找u003c/pu003enu003cpu003e第三步:处理器映射器HandlerMapping向前端控制器返回Handleru003c/pu003enu003cpu003e第四步:前端控制器调用处理器适配器去执行Handleru003c/pu003enu003cpu003e第五步:处理器适配器去执行Handleru003c/pu003enu003cpu003e第六步:Handler执行完成给适配器返回ModelAndViewu003c/pu003enu003cpu003e第七步:处理器适配器向前端控制器返回ModelAndView。ModelAndView是springmvc框架的一个底层对象,包括 Model和viewu003c/pu003enu003cpu003e第八步:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(jsp)u003c/pu003enu003cpu003e第九步:视图解析器向前端控制器返回Viewu003c/pu003enu003cpu003e第十步:前端控制器进行视图渲染。视图渲染将模型数据(在ModelAndView对象中)填充到request域u003c/pu003enu003cpu003e第十一步:前端控制器向用户响应结果u003c/pu003enu003ch3u003eweb.xml的配置u003c/h3u003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 700px; max-height: 479px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 56.220000000000006%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”852″ data-height=”479″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-09c679d6731f913f.png” data-original-width=”852″ data-original-height=”479″ data-original-format=”image/png” data-original-filesize=”54250″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003ch3u003e注解的处理器映射器和适配器u003c/h3u003enu003cpu003espring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping注解映射器。u003c/pu003enu003cpu003e在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter注解适配器。u003c/pu003enu003cpu003e使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置u003c/pu003enu003ch3u003espring 与 mybatis整合过程u003c/h3u003enu003cpu003e第一步:整合dao层u003c/pu003enu003cpu003emybatis和spring整合,通过spring管理mapper接口。u003c/pu003enu003cpu003e使用mapper的扫描器自动扫描mapper接口在spring中进行注册。u003c/pu003enu003cpu003e第二步:整合service层u003c/pu003enu003cpu003e通过spring管理 service接口。u003c/pu003enu003cpu003e使用配置方式将service接口配置在spring配置文件中。u003c/pu003enu003cpu003e实现事务控制。u003c/pu003enu003cpu003e第三步:整合springmvcu003c/pu003enu003cpu003e由于springmvc是spring的模块,不需要整合。u003c/pu003enu003cpu003e主要配置有:u003c/pu003enu003cpu003e1). mybatis配置文件sqlMapConfig.xml配置别名自动扫描(实体类)u003c/pu003enu003cpu003e2). mapper扫描器(接口,数据库访问接口)u003c/pu003enu003cpu003e3). 数据库连接池配置u003c/pu003enu003cpu003e4). 声明式事务配置u003c/pu003enu003cpu003e5). 启用注解扫描:u0026lt;context:component-scan base-package=”cn.itcast.ssm.controller”u0026gt;u0026lt;/context:component-scanu0026gt;u003c/pu003enu003cpu003e6). 配置注解映射器和适配器: u0026lt;mvc:annotation-drivenu0026gt;u0026lt;/mvc:annotation-drivenu0026gt;u003c/pu003enu003cpu003e7). 视图解析器:u0026lt;bean class=”org.springframework.web.servlet.view.InternalResourceViewResolver”u0026gt;u003c/pu003enu003cpu003e8). 配置控制类: DispatcherServlet前端控制器u003c/pu003enu003cpu003e9). 配置spring配置文件加载类:ClassLoadListeneru003c/pu003enu003cpu003eu003cstrongu003e前端控制器从上边的文件中加载处理映射器、适配器、视图解析器等组件,如果不在springmvc.xml中配置,使用默认加载的 DispatcherSerlvet.properties。u003c/strongu003eu003c/pu003enu003ch3u003e视图解析器配置前缀和后缀:u003c/h3u003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 700px; max-height: 196px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 27.22%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”720″ data-height=”196″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-c8676b29663bb424.png” data-original-width=”720″ data-original-height=”196″ data-original-format=”image/png” data-original-filesize=”20216″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003ch3u003esqlMapConfig.xml,mybatis自己的配置文件。u003c/h3u003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 629px; max-height: 469px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 74.56%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”629″ data-height=”469″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-8d4c7c9f12059df4.png” data-original-width=”629″ data-original-height=”469″ data-original-format=”image/png” data-original-filesize=”43700″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003ch2u003e配置数据源:u003c/h2u003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 700px; max-height: 510px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 60.28%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”846″ data-height=”510″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-470b1acdcec877ee.png” data-original-width=”846″ data-original-height=”510″ data-original-format=”image/png” data-original-filesize=”73521″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003cpu003eu003cstrongu003e事务控制(applicationContext-transaction.xml),在applicationContext-transaction.xml中使用spring声明式事务控制方法。u003c/strongu003eu003c/pu003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 700px; max-height: 520px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 54.169999999999995%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”960″ data-height=”520″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-4fd7e74748242de8.png” data-original-width=”960″ data-original-height=”520″ data-original-format=”image/png” data-original-filesize=”69785″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003ch3u003e加载spring配置u003c/h3u003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 700px; max-height: 180px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 21.3%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”845″ data-height=”180″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-bf22b9a3b17d53ae.png” data-original-width=”845″ data-original-height=”180″ data-original-format=”image/png” data-original-filesize=”18440″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003ch3u003e静态资源访问不被拦截:u003c/h3u003enu003cpreu003eu003ccode class=”c”u003e u0026lt;resources mapping=”/resources/**” location=”/resources/” /u0026gt;nn u0026lt;resources mapping=”/images/**” location=”/images/” /u0026gt;nn u0026lt;resources mapping=”/js/**” location=”/js/” /u0026gt;nu003c/codeu003eu003c/preu003enu003ch3u003e@RequestMapping的作用u003c/h3u003enu003cpu003e1). url映射u003c/pu003enu003cpu003e2). 窄化请求映射u003c/pu003enu003cpu003e3). 限制http请求方法u003c/pu003enu003ch3u003econtroller方法的返回值u003c/h3u003enu003cpu003e**返回ModelAndView **u003c/pu003enu003cpu003e需要方法结束时,定义ModelAndView,将model和view分别进行设置。u003c/pu003enu003cpu003eu003cstrongu003e返回stringu003c/strongu003eu003c/pu003enu003cpu003e如果controller方法返回string,u003c/pu003enu003cpu003e1). 表示返回逻辑视图名。真正视图(jsp路径)=前缀+逻辑视图名+后缀u003c/pu003enu003cpu003e2). redirect重定向:返回字符串格式为:”redirect:queryItem.action”u003c/pu003enu003cpu003e3). forward页面转发:返回字符串格式为:“forward:queryItem.action”u003c/pu003enu003cpu003eu003cstrongu003e返回voidu003c/strongu003eu003c/pu003enu003cpu003e在controller方法形参上可以定义request和response,使用request或response指定响应结果:u003c/pu003enu003cpu003e1). 使用request转向页面,如下:request.getRequestDispatcher(“页面路径”).forward(request, response);u003c/pu003enu003cpu003e2). 也可以通过response页面重定向:response.sendRedirect(“url”)u003c/pu003enu003cpu003e3). 也可以通过response指定响应结果,例如响应json数据如下:u003c/pu003enu003culu003enu003cliu003eu003cpu003eresponse.setCharacterEncoding(“utf-8”);u003c/pu003eu003c/liu003enu003cliu003eu003cpu003eresponse.setContentType(“application/json;charset=utf-8”);u003c/pu003eu003c/liu003enu003cliu003eu003cpu003eresponse.getWriter().write(“json串”);u003c/pu003eu003c/liu003enu003c/ulu003enu003ch3u003e参数绑定:u003c/h3u003enu003cpu003eu003cstrongu003e默认支持的类型u003c/strongu003eu003c/pu003enu003cpu003e直接在controller方法形参上定义下边类型的对象,就可以使用这些对象。在参数绑定过程中,如果遇到下边类型直接进行绑定。u003c/pu003enu003cpu003e1). HttpServletRequest:通过request对象获取请求信息u003c/pu003enu003cpu003e2). HttpServletResponse:通过response处理响应信息u003c/pu003enu003cpu003e3). HttpSession:通过session对象得到session中存放的对象u003c/pu003enu003cpu003e4). Model/ModelMap:model是一个接口,modelMap是一个接口实现 。作用:将model数据填充到request域。u003c/pu003enu003cpu003eu003cstrongu003e简单类型u003c/strongu003eu003c/pu003enu003culu003enu003cliu003eu003cpu003e通过@RequestParam对简单类型的参数进行绑定。u003c/pu003eu003c/liu003enu003cliu003eu003cpu003e如果不使用@RequestParam,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。u003c/pu003eu003c/liu003enu003cliu003eu003cpu003e如果使用@RequestParam,不用限制request传入参数名称和controller方法的形参名称一致。u003c/pu003eu003c/liu003enu003cliu003eu003cpu003e通过required属性指定参数是否必须要传入,如果设置为true,没有传入参数,会报错。u003c/pu003eu003c/liu003enu003c/ulu003enu003ch3u003epojo绑定u003c/h3u003enu003cpu003e页面中input的name和controller的pojo形参中的属性名称一致,将页面中数据绑定到pojo。(usename,age;不需要user.username,user.age)u003c/pu003enu003cpu003eu003cstrongu003e自定义参数绑定实现日期类型绑定u003c/strongu003eu003c/pu003enu003cpu003e对于controller形参中pojo对象,如果属性中有日期类型,需要自定义参数绑定。将请求日期数据串转成 日期类型,要转换的日期类型和pojo中日期属性的类型保持一致。u003c/pu003enu003ch2u003eSpring MVC 和 Struts2 对比u003c/h2u003enu003cpu003e1.Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC 就容易实现restful urlu003c/pu003enu003cpu003e2.由上边原因,SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。u003c/pu003enu003cpu003e3.由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。u003c/pu003enu003cpu003e4.SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。u003c/pu003enu003cpu003e5.springmvc面向方法开发的(更接近service接口的开发方式),struts2面向类开发。u003c/pu003enu003cpu003e6.springmvc可以单例开发,struts2只能是多例开发。u003c/pu003enu003ch2u003e乱码处理u003c/h2u003enu003cpu003e1). post乱码u003c/pu003enu003cpu003e在web.xml添加post乱码filter:CharacterEncodingFilteru003c/pu003enu003cpu003e2). 对于get请求中文参数出现乱码解决方法有两个:u003c/pu003enu003cpu003ea. 修改tomcat配置文件添加编码与工程编码一致,如下:u003c/pu003enu003cpu003eu0026lt;Connector URIEncoding=”utf-8″ connectionTimeout=”20000″ port=”8080″ protocol=”HTTP/1.1″ redirectPort=”8443″/u0026gt;u003c/pu003enu003cpu003eb. 对参数进行重新编码:u003c/pu003enu003cpu003eString userName = new String(request.getParamter(“userName”).getBytes(“ISO8859-1″),”utf-8”)u003c/pu003enu003cpu003eISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码u003c/pu003enu003ch2u003e集合类型绑定u003c/h2u003enu003cpu003e1). 数组绑定:u003c/pu003enu003cpu003econtroller方法参数使用:(Integer[] itemId)u003c/pu003enu003cpu003e页面统一使用:itemId 作为nameu003c/pu003enu003cpu003e2). list绑定:u003c/pu003enu003cpu003epojo属性名为:itemsListu003c/pu003enu003cpu003e页面:itemsList[index].属性名u003c/pu003enu003cpu003e3). map 绑定:u003c/pu003enu003cpu003epojo属性名为:Mapu0026lt;String, Objectu0026gt; itemInfo = new HashMapu0026lt;String, Objectu0026gt;();u003c/pu003enu003cpu003e页面: u0026lt;tdu0026gt;姓名:u0026lt;inputtype=”text”name=”itemInfo[‘name’]”/u0026gt;u003c/pu003enu003ch2u003espring 校验u003c/h2u003enu003cpu003e1). 项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。u003c/pu003enu003cpu003e2). springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。u003c/pu003enu003cpu003e校验思路:页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。u003c/pu003enu003ch2u003e数据回显u003c/h2u003enu003cpu003e1). @ModelAttribute还可以将方法的返回值传到页面:在方法上加注解@ModelAttributeu003c/pu003enu003cpu003e2). 使用最简单方法使用model,可以不用@ModelAttribute:model.addAttribute(“id”, id);u003c/pu003enu003cpu003e3). springmvc默认对pojo数据进行回显。pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)u003c/pu003enu003cpu003e4). public String testParam(PrintWriter out, @RequestParam(“username”) String username) { //out直接输出u003c/pu003enu003ch2u003e异常处理u003c/h2u003enu003cpu003espringmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。u003c/pu003enu003cpu003e系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。u003c/pu003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 672px; max-height: 90px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 13.389999999999999%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”672″ data-height=”90″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-099275b2ec3bb8c0.png” data-original-width=”672″ data-original-height=”90″ data-original-format=”image/png” data-original-filesize=”8601″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003ch2u003e上传图片u003c/h2u003enu003cpu003e1). 在页面form中提交enctype=”multipart/form-data”的数据时,需要springmvc对multipart类型的数据进行解析。u003c/pu003enu003cpu003e2). 在springmvc.xml中配置multipart类型解析器。u003c/pu003enu003cbru003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 700px; max-height: 173px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 23.73%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”729″ data-height=”173″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-e7d1b9a6520c8d3e.png” data-original-width=”729″ data-original-height=”173″ data-original-format=”image/png” data-original-filesize=”17484″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003cpu003e3). 方法中使用:MultipartFile attach (单个文件上传) 或者 MultipartFile[] attachs (多个文件上传)u003c/pu003enu003ch2u003eJson处理u003c/h2u003enu003cpu003e1). 加载json转换的jar包:springmvc中使用jackson的包进行json转换(@requestBody和@responseBody使用下边的包进行json转)u003c/pu003enu003cpu003e2). 配置json转换器。在注解适配器RequestMappingHandlerAdapter中加入messageConverters。如果使用u0026lt;mvc:annotation-driven /u0026gt; 则会自动加入。u003c/pu003enu003cpu003e3). ajaxu003c/pu003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 691px; max-height: 319px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 46.160000000000004%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”691″ data-height=”319″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-7f67d3805f70b4d5.png” data-original-width=”691″ data-original-height=”319″ data-original-format=”image/png” data-original-filesize=”21899″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003cpu003e4). Controller (ResponseBody、RequestBody)u003c/pu003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 700px; max-height: 200px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 26.009999999999998%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”769″ data-height=”200″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-5c1204270db4e75e.png” data-original-width=”769″ data-original-height=”200″ data-original-format=”image/png” data-original-filesize=”22084″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003cpu003e注意ajax中contentType如果不设置为json类型,则传的参数为key/value类型。u003cstrongu003e上面设置后,传的是json类型。u003c/strongu003eu003c/pu003enu003cpu003e详见:u003ca href=”https://links.jianshu.com/go?to=http%3A%2F%2Fwww.cnblogs.com%2FJtianlin%2Fp%2F5224885.html” target=”_blank”u003ehttp://www.cnblogs.com/Jtianlin/p/5224885.htmlu003c/au003eu003c/pu003enu003ch2u003e拦截器:u003c/h2u003enu003cpu003e1). 定义拦截器,实现HandlerInterceptor接口。接口中提供三个方法。u003c/pu003enu003cpu003ea. preHandle :进入 Handler方法之前执行,用于身份认证、身份授权,比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行u003c/pu003enu003cpu003eb. postHandle:进入Handler方法之后,返回modelAndView之前执行,应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图u003c/pu003enu003cpu003ec. afterCompletion:执行Handler完成执行此方法,应用场景:统一异常处理,统一日志处理u003c/pu003enu003cpu003e2). 拦截器配置:u003c/pu003enu003cpu003ea. 针对HandlerMapping配置(不推荐):springmvc拦截器针对HandlerMapping进行拦截设置,如果在某个HandlerMapping中配置拦截,经过该 HandlerMapping映射成功的handler最终使用该 拦截器。 (一般不推荐使用)u003c/pu003enu003cpu003eb. 类似全局的拦截器:springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中u003c/pu003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 700px; max-height: 273px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 37.76%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”723″ data-height=”273″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-685a96258fb81cb4.png” data-original-width=”723″ data-original-height=”273″ data-original-format=”image/png” data-original-filesize=”26506″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003ch2u003e如何启用注解:u003c/h2u003enu003cpu003eu0026lt;context:annotation-config/u0026gt;u003c/pu003enu003cpu003e如果使用u0026lt;context:component-scan base-package=”com.tgb.web.controller.annotation”u0026gt; u0026lt;/context:component-scanu0026gt; 则上面内容可以省略 使用 mvc:annotation-driven代替注解映射器和注解适配器配置u003c/pu003enu003cpu003eu003c/pu003eu003chru003eu003cpu003eu003c/pu003enu003ch2u003e什么是Spring Boot?它解决了什么问题?u003c/h2u003enu003cpu003e初期的Spring通过代码加配置的形式为项目提供了良好的灵活性和扩展性,但随着Spring越来越庞大,其配置文件也越来越繁琐,太多复杂的xml文件也一直是Spring被人诟病的地方,特别是近些年其他简洁的WEB方案层出不穷,如基于Python或Node.Js,几行代码就能实现一个WEB服务器,对比起来,大家渐渐觉得Spring那一套太过繁琐,此时,Spring社区推出了Spring Boot,它的目的在于实现自动配置,降低项目搭建的复杂度,如需要搭建一个接口服务,通过Spring Boot,几行代码即可实现,请看代码示例:u003c/pu003enu003cpreu003eu003ccode class=”c”u003e//引入spring-boot-starter-web依赖nu0026lt;dependencyu0026gt;n u0026lt;groupIdu0026gt;org.springframework.bootu0026lt;/groupIdu0026gt;n u0026lt;artifactIdu0026gt;spring-boot-starter-webu0026lt;/artifactIdu0026gt;nu0026lt;/dependencyu0026gt;nu003c/codeu003eu003c/preu003enu003cpreu003eu003ccode class=”c”u003e//声明Spring Boot应用,直接写业务逻辑即可n@Controllern@SpringBootApplicationnpublic class MockServerApplication {n @RequestMapping(“/hi”)n @ResponseBodyn String home() {n return “how are you!”;n }nn public static void main(String[] args) {n SpringApplication.run(MockServerApplication.class, args);n }n}nu003c/codeu003eu003c/preu003enu003cpu003e你甚至都不用额外的WEB容器,直接生成jar包执行即可,因为u003ccodeu003espring-boot-starter-webu003c/codeu003e模块中包含有一个内置tomcat,可以直接提供容器使用;基于Spring Boot,不是说原来的配置没有了,而是Spring Boot有一套默认配置,我们可以把它看做比较通用的约定,而Spring Boot遵循的也是约定优于配置原则,同时,如果你需要使用到Spring以往提供的各种复杂但功能强大的配置功能,Spring Boot一样支持u003c/pu003enu003cpu003e在Spring Boot中,你会发现你引入的所有包都是u003c/pu003enu003cpu003estarteru003c/pu003enu003cpu003e形式,如:u003c/pu003enu003culu003enu003cliu003espring-boot-starter-web-services,针对SOAP Web Servicesu003c/liu003enu003cliu003espring-boot-starter-web,针对Web应用与网络接口u003c/liu003enu003cliu003espring-boot-starter-jdbc,针对JDBCu003c/liu003enu003cliu003espring-boot-starter-data-jpa,基于hibernate的持久层框架u003c/liu003enu003cliu003espring-boot-starter-cache,针对缓存支持u003c/liu003enu003cliu003e等等u003c/liu003enu003c/ulu003enu003cpu003eSpring Boot对starter的解释如下:u003c/pu003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 625px; max-height: 154px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 24.64%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”625″ data-height=”154″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-a92bbcbc157d2f4f.png” data-original-width=”625″ data-original-height=”154″ data-original-format=”image/png” data-original-filesize=”22856″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003cpu003e这句话的译意为:u003c/pu003enu003cbru003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 632px; max-height: 100px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 15.82%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”632″ data-height=”100″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-2709195ec212273b.png” data-original-width=”632″ data-original-height=”100″ data-original-format=”image/png” data-original-filesize=”21460″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003ch2u003eSpring REST如何使用?u003c/h2u003enu003ch4u003e准备工作u003c/h4u003enu003cpu003e1、 下载jar包u003c/pu003enu003cpu003espring各版本jar下载地址:u003ca href=”https://links.jianshu.com/go?to=http%3A%2F%2Febr.springsource.com%2Frepository%2Fapp%2Flibrary%2Fdetail%3Fname%3Dorg.springframework.spring” target=”_blank”u003ehttp://ebr.springsource.com/repository/app/library/detail?name=org.springframework.springu003c/au003eu003c/pu003enu003cpu003e相关的依赖包也可以在这里找到:u003ca href=”https://links.jianshu.com/go?to=http%3A%2F%2Febr.springsource.com%2Frepository%2Fapp%2Flibrary” target=”_blank”u003ehttp://ebr.springsource.com/repository/app/libraryu003c/au003eu003c/pu003enu003cpu003e2、 需要jar包如下u003c/pu003enu003cdiv class=”image-package”u003enu003cdiv class=”image-container” style=”max-width: 244px; max-height: 168px;”u003enu003cdiv class=”image-container-fill” style=”padding-bottom: 68.85%;”u003eu003c/divu003enu003cdiv class=”image-view” data-width=”244″ data-height=”168″u003eu003cimg data-original-src=”//upload-images.jianshu.io/upload_images/26011540-7edd820fb71516a3.png” data-original-width=”244″ data-original-height=”168″ data-original-format=”image/png” data-original-filesize=”73496″u003eu003c/divu003enu003c/divu003enu003cdiv class=”image-caption”u003eimage.pngu003c/divu003enu003c/divu003enu003cpu003e3、 当前工程的web.xml配置u003c/pu003enu003cpreu003eu003ccode class=”c”u003eu0026lt;?xml version=”1.0″ encoding=”UTF-8″?u0026gt;nu0026lt;web-app version=”2.4″ n xmlns=”http://java.sun.com/xml/ns/j2ee” n xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” n xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee n http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd”u0026gt;n n u0026lt;!– 配置Spring核心控制器 –u0026gt;n u0026lt;servletu0026gt;n u0026lt;servlet-nameu0026gt;dispatcheru0026lt;/servlet-nameu0026gt;n u0026lt;servlet-classu0026gt;org.springframework.web.servlet.DispatcherServletu0026lt;/servlet-classu0026gt;n u0026lt;init-paramu0026gt;n u0026lt;param-nameu0026gt;contextConfigLocationu0026lt;/param-nameu0026gt;n u0026lt;param-valueu0026gt;/WEB-INF/dispatcher.xmlu0026lt;/param-valueu0026gt;n u0026lt;/init-paramu0026gt;n u0026lt;load-on-startupu0026gt;1u0026lt;/load-on-startupu0026gt;n u0026lt;/servletu0026gt;n n u0026lt;servlet-mappingu0026gt;n u0026lt;servlet-nameu0026gt;dispatcheru0026lt;/servlet-nameu0026gt;n u0026lt;url-patternu0026gt;*.dou0026lt;/url-patternu0026gt;n u0026lt;/servlet-mappingu0026gt;n n u0026lt;welcome-file-listu0026gt;n u0026lt;welcome-fileu0026gt;index.jspu0026lt;/welcome-fileu0026gt;n u0026lt;/welcome-file-listu0026gt;nu0026lt;/web-appu0026gt;nu003c/codeu003eu003c/preu003enu003cpu003e4、 WEB-INF中的dispatcher.xml配置u003c/pu003enu003cpreu003eu003ccode class=”c”u003eu0026lt;?xml version=”1.0″ encoding=”UTF-8″?u0026gt;nu0026lt;beans xmlns=”http://www.springframework.org/schema/beans”n xmlns:mvc=”http://www.springframework.org/schema/mvc”n xmlns:context=”http://www.springframework.org/schema/context”n xmlns:util=”http://www.springframework.org/schema/util”n xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”n xsi:schemaLocation=”http://www.springframework.org/schema/beans n http://www.springframework.org/schema/beans/spring-beans-3.0.xsdn http://www.springframework.org/schema/mvcn http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsdn http://www.springframework.org/schema/context n http://www.springframework.org/schema/context/spring-context-3.0.xsdn http://www.springframework.org/schema/utiln http://www.springframework.org/schema/util/spring-util-3.0.xsd”u0026gt;n n u0026lt;context:component-scan base-package=”com.hoo.*”u0026gt;n u0026lt;!– 忽略这个类 –u0026gt;n u0026lt;context:exclude-filter type=”assignable” expression=”com.hoo.client.RESTClient”/u0026gt;n u0026lt;/context:component-scanu0026gt;n n u0026lt;!– annotation的方法映射适配器 –u0026gt;n u0026lt;bean id=”handlerAdapter” class=”org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter”/u0026gt;n n u0026lt;!– xml视图,XStreamMarshaller,可以转换任何形式的java对象 –u0026gt;n u0026lt;bean name=”xStreamMarshallingView” class=”org.springframework.web.servlet.view.xml.MarshallingView”u0026gt;n u0026lt;property name=”marshaller”u0026gt;n u0026lt;bean class=”org.springframework.oxm.xstream.XStreamMarshaller”u0026gt; n u0026lt;!– 为了初始化XStreamMarshaller,这个类会把我们接口中得到结果以XML文档形式展现出来 –u0026gt;n u0026lt;property name=”autodetectAnnotations” value=”true”/u0026gt; n u0026lt;/beanu0026gt; n u0026lt;/propertyu0026gt;n u0026lt;/beanu0026gt;n n u0026lt;!– 视图解析器,根据视图的名称new ModelAndView(name),在配置文件查找对应的bean配置 –u0026gt;n u0026lt;bean class=”org.springframework.web.servlet.view.BeanNameViewResolver”u0026gt;n u0026lt;property name=”order” value=”3″/u0026gt;n u0026lt;/beanu0026gt;n n u0026lt;!– annotation默认的方法映射适配器 –u0026gt;n u0026lt;bean id=”handlerMapping” class=”org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping”u0026gt;n u0026lt;property name=”order” value=”1″ /u0026gt;n u0026lt;/beanu0026gt;n nu0026lt;/beansu0026gt;n5、 启动后,可以看到index.jsp 没有出现异常或错误。那么当前SpringMVC的配置就成功了。nu003c/codeu003eu003c/preu003enu003ch3u003e二、REST控制器实现u003c/h3u003enu003cpu003eREST控制器主要完成CRUD操作,也就是对于http中的post、get、put、delete。u003c/pu003enu003cpu003e还有其他的操作,如head、options、trace。u003c/pu003enu003cpu003e具体代码:u003c/pu003enu003cpreu003eu003ccode class=”c”u003epackage com.hoo.controller;n nimport org.springframework.stereotype.Controller;nimport org.springframework.web.bind.annotation.PathVariable;nimport org.springframework.web.bind.annotation.RequestMapping;nimport org.springframework.web.bind.annotation.RequestMethod;nimport org.springframework.web.servlet.ModelAndView;n n/**n * u0026lt;bu0026gt;function:u0026lt;/bu0026gt;SpringMVC REST示例n * @author hoojon * @createDate 2011-6-9 上午11:34:08n * @file RESTController.javan * @package com.hoo.controllern * @project SpringRestWSn * @blog http://blog.csdn.net/IBM_hoojon * @email hoojo_@126.comn * @version 1.0n */n@RequestMapping(“/restful”)n@Controllernpublic class RESTController {n n @RequestMapping(value = “/show”, method = RequestMethod.GET)n public ModelAndView show() {n System.out.println(“show”);n ModelAndView model = new ModelAndView(“xStreamMarshallingView”);n model.addObject(“show method”);n return model; n }n n @RequestMapping(value = “/get/{id}”, method = RequestMethod.GET)n public ModelAndView getUserById(@PathVariable String id) {n System.out.println(“getUserById-” + id);n ModelAndView model = new ModelAndView(“xStreamMarshallingView”);n model.addObject(“getUserById method -” + id);n return model; n }n n @RequestMapping(value = “/add”, method = RequestMethod.POST)n public ModelAndView addUser(String user) {n System.out.println(“addUser-” + user);n ModelAndView model = new ModelAndView(“xStreamMarshallingView”);n model.addObject(“addUser method -” + user);n return model; n }n n @RequestMapping(value = “/edit”, method = RequestMethod.PUT)n public ModelAndView editUser(String user) {n System.out.println(“editUser-” + user);n ModelAndView model = new ModelAndView(“xStreamMarshallingView”);n model.addObject(“editUser method -” + user);n return model;n }n n @RequestMapping(value = “/remove/{id}”, method = RequestMethod.DELETE)n public ModelAndView removeUser(@PathVariable String id) {n System.out.println(“removeUser-” + id);n ModelAndView model = new ModelAndView(“xStreamMarshallingView”);n model.addObject(“removeUser method -” + id);n return model;n }n}nu003c/codeu003eu003c/preu003enu003cpu003e上面的方法对应的http操作:u003c/pu003enu003cpreu003eu003ccode class=”c”u003e/show -u0026gt; get 查询n/get/id -u0026gt; get 查询n/add -u0026gt; post 添加n/edit -u0026gt; put 修改n/remove/id -u0026gt; delete 删除nu003c/codeu003eu003c/preu003enu003cpu003e在这个方法中,就可以看到RESTful风格的url资源标识u003c/pu003enu003cpreu003eu003ccode class=”c”u003e@RequestMapping(value = “/get/{id}”, method = RequestMethod.GET)npublic ModelAndView getUserById(@PathVariable String id) {n System.out.println(“getUserById-” + id);n ModelAndView model = new ModelAndView(“xStreamMarshallingView”);n model.addObject(“getUserById method -” + id);n return model; n}nu003c/codeu003eu003c/preu003enu003cpu003evalue=”/get/{id}”就是url中包含get,并且带有id参数的get请求,就会执行这个方法。这个url在请求的时候,会通过Annotation的@PathVariable来将url中的id值设置到getUserById的参数中去。 ModelAndView返回的视图是xStreamMarshallingView是一个xml视图,执行当前请求后,会显示一篇xml文档。文档的内容是添加到model中的值。u003c/pu003enu003ch3u003e三、利用RestTemplate调用REST资源u003c/h3u003enu003cpu003e代码如下:u003c/pu003enu003cpreu003eu003ccode class=”c”u003epackage com.hoo.client;n nimport org.springframework.beans.factory.annotation.Autowired;nimport org.springframework.stereotype.Component;nimport org.springframework.web.client.RestTemplate;n n/**n * u0026lt;bu0026gt;function:u0026lt;/bu0026gt;RestTemplate调用REST资源n * @author hoojon * @createDate 2011-6-9 上午11:56:16n * @file RESTClient.javan * @package com.hoo.clientn * @project SpringRestWSn * @blog http://blog.csdn.net/IBM_hoojon * @email hoojo_@126.comn * @version 1.0n */n@Componentnpublic class RESTClient {n n @Autowiredn private RestTemplate template;n n private final static String url = “http://localhost:8080/SpringRestWS/restful/”;n n public String show() {n return template.getForObject(url + “show.do”, String.class, new String[]{});n }n n public String getUserById(String id) {n return template.getForObject(url + “get/{id}.do”, String.class, id); n }n n public String addUser(String user) {n return template.postForObject(url + “add.do?user={user}”, null, String.class, user);n }n n public String editUser(String user) {n template.put(url + “edit.do?user={user}”, null, user);n return user;n }n n public String removeUser(String id) {n template.delete(url + “/remove/{id}.do”, id);n return id;n }n}nu003c/codeu003eu003c/preu003enu003culu003enu003cliu003eRestTemplate的getForObject完成get请求、postForObject完成post请求、put对应的完成put请求、delete完成delete请求;还有execute可以执行任何请求的方法,需要你设置RequestMethod来指定当前请求类型。u003c/liu003enu003cliu003eRestTemplate.getForObject(String url, Classu0026lt;Stringu0026gt; responseType, String… urlVariables)u003c/liu003enu003cliu003e参数url是http请求的地址,参数Class是请求响应返回后的数据的类型,最后一个参数是请求中需要设置的参数。u003c/liu003enu003cliu003etemplate.getForObject(url + “get/{id}.do”, String.class, id);u003c/liu003enu003cliu003e如上面的参数是{id},返回的是一个string类型,设置的参数是id。最后执行该方法会返回一个String类型的结果。u003c/liu003enu003c/ulu003enu003cpu003e下面建立一个测试类,完成对RESTClient的测试。代码如下:u003c/pu003enu003cpreu003eu003ccode class=”c”u003epackage com.hoo.client;n nimport org.springframework.beans.factory.annotation.Autowired;nimport org.springframework.test.context.ContextConfiguration;nimport org.springframework.test.context.junit38.AbstractJUnit38SpringContextTests;n n/**n * u0026lt;bu0026gt;function:u0026lt;/bu0026gt;RESTClient TESTn * @author hoojon * @createDate 2011-6-9 下午03:50:21n * @file RESTClientTest.javan * @package com.hoo.clientn * @project SpringRestWSn * @blog http://blog.csdn.net/IBM_hoojon * @email hoojo_@126.comn * @version 1.0n */n@ContextConfiguration(“classpath:applicationContext-*.xml”)npublic class RESTClientTest extends AbstractJUnit38SpringContextTests {n n @Autowiredn private RESTClient client;n n public void testShow() {n System.out.println(client.show());n }n n public void testGetUserById() {n System.out.println(client.getUserById(“abc”));n }n n public void testAddUser() {n System.out.println(client.addUser(“jack”));n }n n public void testEditUser() {n System.out.println(client.editUser(“tom”));n }n n public void testRemoveUser() {n System.out.println(client.removeUser(“aabb”));n }n}nu003c/codeu003eu003c/preu003enu003cpu003e我们需要在src目录下添加applicationContext-beans.xml完成对restTemplate的配置。restTemplate需要配置MessageConvert将返回的xml文档进行转换,解析成JavaObject。u003c/pu003enu003cpreu003eu003ccode class=”c”u003eu0026lt;?xml version=”1.0″ encoding=”UTF-8″?u0026gt;nu0026lt;beans xmlns=”http://www.springframework.org/schema/beans”n xmlns:context=”http://www.springframework.org/schema/context”n xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”n xsi:schemaLocation=”http://www.springframework.org/schema/beans n http://www.springframework.org/schema/beans/spring-beans-3.0.xsdn http://www.springframework.org/schema/contextn http://www.springframework.org/schema/context/spring-context-3.0.xsd”u0026gt;n n u0026lt;context:component-scan base-package=”com.hoo.*”/u0026gt;n n u0026lt;bean id=”restTemplate” class=”org.springframework.web.client.RestTemplate”u0026gt;n u0026lt;property name=”messageConverters”u0026gt;n u0026lt;listu0026gt;n u0026lt;bean class=”org.springframework.http.converter.xml.MarshallingHttpMessageConverter”u0026gt;n u0026lt;property name=”marshaller” ref=”xStreamMarshaller”/u0026gt;n u0026lt;property name=”unmarshaller” ref=”xStreamMarshaller”/u0026gt;n u0026lt;/beanu0026gt;n u0026lt;/listu0026gt;n u0026lt;/propertyu0026gt;n u0026lt;/beanu0026gt;n n u0026lt;bean id=”xStreamMarshaller” class=”org.springframework.oxm.xstream.XStreamMarshaller”u0026gt;n u0026lt;property name=”annotatedClasses”u0026gt;n u0026lt;arrayu0026gt; n u0026lt;/arrayu0026gt;n u0026lt;/propertyu0026gt;n u0026lt;/beanu0026gt;nu0026lt;/beansu0026gt;nu003c/codeu003eu003c/preu003enu003cpu003e上面配置了xStreamMarshaller是和RESTController中的ModelAndView的view对应的。因为那边是用xStreamMarshaller进行编组的,所以RestTemplate这边也需要用它来解组。RestTemplate还指出其他的MarshallingHttpMessageConverteru003c/pu003en”,”voted_down”:false,”rewardable”:true,”show_paid_comment_tips”:false,”share_image_url”:”https://upload-images.jianshu.io/upload_images/26011540-09c679d6731f913f.png”,”slug”:”dfaa6954287d”,”user”:{“liked_by_user”:false,”following_count”:9,”gender”:1,”avatar_widget”:null,”slug”:”f55db1589ea6″,”intro”:””,”likes_count”:18,”nickname”:”f55db1589ea6″,”badges”:[],”total_fp_amount”:”1497286085520975900″,”wordage”:59287,”avatar”:”https://upload.jianshu.io/users/upload_avatars/26011540/30d5ad0c-d43b-4660-bbb9-d44d3c1ca8d6.jpg”,”id”:26011540,”liked_user”:false},”likes_count”:6,”paid_type”:”free”,”show_ads”:true,”paid_content_accessible”:false,”hide_search_input”:true,”total_fp_amount”:”716000000000000000″,”trial_open”:false,”reprintable”:true,”bookmarked”:false,”wordage”:6283,”featured_comments_count”:0,”downvotes_count”:0,”wangxin_trial_open”:null,”guideShow”:{“audit_user_nickname_spliter”:0,”pc_note_bottom_btn”:1,”pc_like_author_guidance”:1,”ban_some_labels”:1,”h5_real_name_auth_link”:1,”audit_user_background_image_spliter”:0,”audit_note_spliter”:0,”new_user_no_ads”:1,”launch_tab”:0,”include_post”:0,”pc_login_guidance”:1,”audit_comment_spliter”:0,”pc_note_bottom_qrcode”:1,”audit_user_avatar_spliter”:0,”audit_collection_spliter”:0,”pc_top_lottery_guidance”:2,”subscription_guide_entry”:1,”creation_muti_function_on”:1,”explore_score_searcher”:0,”audit_user_spliter”:0,”h5_ab_test”:1,”reason_text”:1,”pc_note_popup”:2},”commentable”:true,”total_rewards_count”:0,”id”:85783421,”notebook”:{“name”:””},”activity_collection_slug”:null,”description”:”前言 Spring框架就像一个家族,有众多衍生产品例如boot、security、jpa等等。但他们的基础都是Spring的ioc和aop。ioc提供了依赖注入的容器,aop…”,”first_shared_at”:1617011068,”views_count”:378,”notebook_id”:49694532},”baseList”:{“likeList”:[],”rewardList”:[]},”status”:”success”,”statusCode”:0},”user”:{“isLogin”:false,”userInfo”:{}},”comments”:{“list”:[],”featuredList”:[]}},”initialProps”:{“pageProps”:{“query”:{“slug”:”dfaa6954287d”}},”localeData”:{“common”:{“jianshu”:”简书”,”diamond”:”简书钻”,”totalAssets”:”总资产{num}”,”diamondValue”:” (约{num}元)”,”login”:”登录”,”logout”:”注销”,”register”:”注册”,”on”:”开”,”off”:”关”,”follow”:”关注”,”followBook”:”关注连载”,”following”:”已关注”,”cancelFollow”:”取消关注”,”publish”:”发布”,”wordage”:”字数”,”audio”:”音频”,”read”:”阅读”,”reward”:”赞赏”,”zan”:”赞”,”comment”:”评论”,”expand”:”展开”,”prevPage”:”上一页”,”nextPage”:”下一页”,”floor”:”楼”,”confirm”:”确定”,”delete”:”删除”,”report”:”举报”,”fontSong”:”宋体”,”fontBlack”:”黑体”,”chs”:”简体”,”cht”:”繁体”,”jianChat”:”简信”,”postRequest”:”投稿请求”,”likeAndZan”:”喜欢和赞”,”rewardAndPay”:”赞赏和付费”,”home”:”我的主页”,”markedNotes”:”收藏的文章”,”likedNotes”:”喜欢的文章”,”paidThings”:”已购内容”,”wallet”:”我的钱包”,”setting”:”设置”,”feedback”:”帮助与反馈”,”loading”:”加载中…”,”needLogin”:”请登录后进行操作”,”trialing”:”文章正在审核中…”,”reprintTip”:”禁止转载,如需转载请通过简信或评论联系作者。”},”error”:{“rewardSelf”:”无法打赏自己的文章哟~”},”message”:{“paidNoteTip”:”付费购买后才可以参与评论哦”,”CommentDisableTip”:”作者关闭了评论功能”,”contentCanNotEmptyTip”:”回复内容不能为空”,”addComment”:”评论发布成功”,”deleteComment”:”评论删除成功”,”likeComment”:”评论点赞成功”,”setReadMode”:”阅读模式设置成功”,”setFontType”:”字体设置成功”,”setLocale”:”显示语言设置成功”,”follow”:”关注成功”,”cancelFollow”:”取消关注成功”,”copySuccess”:”复制代码成功”},”header”:{“homePage”:”首页”,”download”:”下载APP”,”discover”:”发现”,”message”:”消息”,”reward”:”赞赏支持”,”editNote”:”编辑文章”,”writeNote”:”写文章”},”note”:{},”noteMeta”:{“lastModified”:”最后编辑于 “,”wordage”:”字数 {num}”,”viewsCount”:”阅读 {num}”},”divider”:{“selfText”:”以下内容为付费内容,定价 ¥{price}”,”paidText”:”已付费,可查看以下内容”,”notPaidText”:”还有 {percent} 的精彩内容”,”modify”:”点击修改”},”paidPanel”:{“buyNote”:”支付 ¥{price} 继续阅读”,”buyBook”:”立即拿下 ¥{price}”,”freeTitle”:”该作品为付费连载”,”freeText”:”购买即可永久获取连载内的所有内容,包括将来更新的内容”,”paidTitle”:”还没看够?拿下整部连载!”,”paidText”:”永久获得连载内的所有内容, 包括将来更新的内容”},”book”:{“last”:”已是最后”,”lookCatalog”:”查看连载目录”,”header”:”文章来自以下连载”},”action”:{“like”:”{num}人点赞”,”collection”:”收入专题”,”report”:”举报文章”},”comment”:{“allComments”:”全部评论”,”featuredComments”:”精彩评论”,”closed”:”评论已关闭”,”close”:”关闭评论”,”open”:”打开评论”,”desc”:”按时间倒序”,”asc”:”按时间正序”,”disableText1″:”用户已关闭评论,”,”disableText2″:”与Ta简信交流”,”placeholder”:”写下你的评论…”,”publish”:”发表”,”create”:” 添加新评论”,”reply”:” 回复”,”restComments”:”还有{num}条评论,”,”expandImage”:”展开剩余{num}张图”,”deleteText”:”确定要删除评论么?”},”collection”:{“title”:”被以下专题收入,发现更多相似内容”,”putToMyCollection”:”收入我的专题”},”seoList”:{“title”:”推荐阅读”,”more”:”更多精彩内容”},”sideList”:{“title”:”推荐阅读”},”wxShareModal”:{“desc”:”打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮”},”bookChapterModal”:{“try”:”试读”,”toggle”:”切换顺序”},”collectionModal”:{“title”:”收入到我管理的专题”,”search”:”搜索我管理的专题”,”newCollection”:”新建专题”,”create”:”创建”,”nothingFound”:”未找到相关专题”,”loadMore”:”展开查看更多”},”contributeModal”:{“search”:”搜索专题投稿”,”newCollection”:”新建专题”,”addNewOne”:”去新建一个”,”nothingFound”:”未找到相关专题”,”loadMore”:”展开查看更多”,”managed”:”我管理的专题”,”recommend”:”推荐专题”},”QRCodeShow”:{“payTitle”:”微信扫码支付”,”payText”:”支付金额”},”rewardModal”:{“title”:”给作者送糖”,”custom”:”自定义”,”placeholder”:”给Ta留言…”,”choose”:”选择支付方式”,”balance”:”简书余额”,”tooltip”:”网站该功能暂时下线,如需使用,请到简书App操作”,”confirm”:”确认支付”,”success”:”赞赏成功”},”payModal”:{“payBook”:”购买连载”,”payNote”:”购买文章”,”promotion”:”优惠券”,”promotionFetching”:”优惠券获取中…”,”noPromotion”:”无可用优惠券”,”promotionNum”:”{num}张可用”,”noUsePromotion”:”不使用优惠券”,”validPromotion”:”可用优惠券”,”invalidPromotion”:”不可用优惠券”,”total”:”支付总额”,”tip1″:”· 你将购买的商品为虚拟内容服务,购买后不支持退订、转让、退换,请斟酌确认。”,”tip2″:”· 购买后可在“已购内容”中查看和使用。”,”success”:”购买成功”},”reportModal”:{“ad”:”广告及垃圾信息”,”plagiarism”:”抄袭或未授权转载”,”placeholder”:”写下举报的详情情况(选填)”,”success”:”举报成功”},”guidModal”:{“modalAText”:”相似文章推荐”,”subText”:”下载简书APP,浏览更多相似文章”,”btnAText”:”先不下载,下次再说”,”followOkText”:”关注作者成功!”,”followTextTip”:”下载简书APP,作者更多精彩内容更新及时提醒!”,”followBtn”:”下次再说”,”downloadTipText”:”更多精彩内容,就在简书APP”,”footerDownLoadText”:”下载简书APP”,”modabTitle”:”免费送你2次抽奖机会”,”modalbTip”:”抽取10000收益加成卡,下载简书APP概率翻倍”,”modalbFooterTip”:”下载简书APP,天天参与抽大奖”,”modalReward”:”抽奖”,”scanQrtip”:”扫码下载简书APP”,”downloadAppText”:”下载简书APP,随时随地发现和创作内容”,”redText”:”阅读”,”likesText”:”赞”,”downLoadLeft”:”更多好文”,”leftscanText”:”把文字装进口袋”}},”currentLocale”:”zh-CN”,”asPath”:”/p/dfaa6954287d”}},”page”:”/p/[slug]”,”query”:{“slug”:”dfaa6954287d”},”buildId”:”lcwHSLQvYZGBn55hiTPfZ”,”assetPrefix”:”https://cdn2.jianshu.io/shakespeare”}

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。