素材巴巴 > 程序开发 >

SpringBoot整合SpringMVC

程序开发 2023-09-09 06:39:07

一、初识SpringMVC

1.1 SpringMVC框架的设计

  目前前后端都流行使用MVC框架,他松散的结构使得开发高效、简便,下面看一下SpringMVC的流程图:
在这里插入图片描述
  对于上述的流程,MVC先通过控制器处理请求,然后通过模型层处理逻辑,因为通常是在nosql或关系数据库进行数据的处理,因此模型层分为业务层和数据访问层,最后将数据渲染到视图并返回给控制器,最后交给客户端。

1.2 SpringMVC流程

  我们需要了解一下SpringMVC的组件,否则我们不知道SpringBoot为我们做了哪些默认的初始化操作,在SpringMVC的流程中,不一定要走完全部的流程,下面我们看一下流程图:
在这里插入图片描述
  在SpringBoot机制下,启动SpringMVC会做一些组件的初始化,在spring-webmvc-xxx.jar包有一个DispatcherServlet.properties文件,里面有一些对象的初始化定义,启动时读取信息并初始化到IOC容器。可以帮助我们简化SpringMVC的配置。
  下面来开发一个控制器:

@Controller
 @RequestMapping("/user")
 public class UserController{//注入服务类private UserService userService = null;//展示用户详情@ReqeustMapping("details")public ModelAndView details(long id){//访问模型层获取数据User user = userService.getUser(id);//模型和视图ModelAndView mv = new ModelAndView;//定义模型视图mv.setViewName("user/details");//加入数据模型mv.addObject("user",user);//返回模型和视图return mv;}
 }
 

  @Controller代表这是一个控制器,@RequestMapping表示的路径和控制器会被扫描到HandlerMapping机制中存储,当用户请求到来会根据HandlerMapping找到相关的控制器,只不过HandlerMapping返回一个HandlerExecutionChain对象,这是处理器链,这个对象是对控制器的包装,包含控制和他的拦截器等。
  不过上述的处理器链执行还需要一个适配器来运行,因为有的请求是http,有的是websocket等,运行时先通过模型层拿到数据,在将数据放入模型,最后返回模型和视图,最后交给视图解析器去解析视图。
  为了定制视图解析器,可以在application.properties文件中配置如下:

spring.mvc.view.prefix=/WEB-INF/jsp/
 spring.mvc.view.suffix=.jsp
 

  通过上述的配置,即可解析视图了,上述配置了视图的前缀和后缀,所以返回逻辑视图会找/WEB-INF/jsp/user/details.jsp这个视图。但目前的前后端分离的趋势,有时候我们只需要返回json数据即可,即返回非逻辑视图,这在后边再介绍。

1.3 定制SpringMVC的初始化

  在SpringBoot中回去尽可能的配置SpringMVC相关的组件等,但是我们可以通过自定义的方式来实现不同场景下的SpringMVC,为此spring提供了一个接口WebMvcConfigurer,这是一个java8的接口,因此里面的方法都提供了default实现,如果需要自定义,只需要实现接口并重写特定的方法即可。
  在SpringBoot中是通过WebMvcAutoConfigurer进行自动配置的,他有一个静态的内部类WebMvcAutoConfigurationAdapter,通过它实现了自动配置SpringMVC,关于SpringBoot对于SpringMVC的可配置项,可以参考官方文档,这里不赘述。

二、深入SpringMVC的开发

2.1 处理器映射

  在启动时会将@RequestMapping注解配置的内容保存到HandlerMapping中,然后等待拦截请求,并与HandlerMapping进行匹配找到对应的处理器,并将控制器和处理器保存到HandlerExecutionChain对象交给DispatcherServlet,就可以运行了。
  @RequestMapping配置项主要有value和method,就是拦截路径和http请求的方法,之后为了简化,提供了很多注解如:@GetMapping、@PostMapping等。

2.2 获取控制器参数

  之前介绍过,处理器是对控制器的包装,请求到来之前会先将http请求的参数解析之后在进行控制器的方法,下面介绍一下如何获取参数。

2.2.1 无注解下获取参数

  无注解下可以允许参数为空,但参数名必须和http请求的参数名一致,如:

@GetMapping("/user")
 @ResponseBody
 public User noAnnotation(int id,String userName){....
 }
 

  注意,@ResponseBody注解表示返回的是json格式的数据。

2.2.2 使用@RequestParam获取参数

  在前后端分离的环境下,有可能前端参数名和后端不对应,因此可以使用注解@RequestParam来进行映射:

@GetMapping("/user")
 @responseBody
 public User reqeustParam(@RequestParam(value="id",required=false) int id,@RequestParam(value="user_name",requried=false) String userName){....
 }
 

  上述代码中,参数前的注解@RequestParam表示匹配前端传递的参数id和user_name,而且都可以为null。

2.2.3 传递数组

  SpringMVC支持使用逗号分隔来传递数组

@GetMapping("/user")
 @RespnseBody
 public User requestArray(int [] intArr,String [] strArr){....
 }
 

  上述可以接受数组传递,因此前端的参数可以如下:
? intArr=1,2,3 & strArr=str1,str2,str3

2.2.4 传递Json

  在目前前后端分离下,可以使用json体传递参数,使用@RequestBody注解,且需要提供一个对象,对象的属性与json体的属性一一对应:

@PostMapping("/insert")
 @ResponseBody
 public User insert(@RequstBody User user){....
 }
 

  这样可以提交类似表单的json数据。

2.2.5 传递url参数

  对于当下的restful风格的开发,使用url传递参数会更加简洁,使用{…}并结合@PathVariable注解使用:

@GetMapping("/{id}")
 @ResponseBody
 public User get(@PathVariable("id") int id){....
 }
 

  通过{ … }来表明参数的名称和位置,注解@PathVariable知道如何从url获取参数,前端可以传递如:http://locahost/user/1,这个参数1就会被获取到。

2.2.6 获取格式化参数

  SpringMVC提供了对日期和数字类型的格式化参数处理,分别是注解@DateTimeFormat和@NumberFormat,使用如下:

@GetMapping("/format")
 @ResponseBody
 public Map format(@DateTimeFormat(iso=ISO.Date) Date date,@NumberFormat(pattern="#,###.##") Double number){....
 }
 

  在SpringBoot中,也可以不用@DateTimeFormat注解,在application.properties配置文件中配置如下即可:

spring.mvc.date-format=yyyy-MM-dd
 

2.3 自定义参数转换规则

  对于上述的参数接收,之所以使用简单的注解甚至不用注解就可获取到参数,这是因为SpringMVC会默认提供一套机制来获取这些参数,但是有的参数传递,SpringMVC默认并不支持,因此就需要我们自定义转换规则来适应这种情况。下面我们介绍一下SpringMVC是如何从http请求获取参数的。

2.3.1 处理器获取参数逻辑

  在处理器执行过程中会先从http请求和上下文环境得到参数,对于简单的参数会直接用简单的转换器转换,这些转换器是SpringMVC提供的,但如果转换请求体,则会调用HttpMessageConverter接口,看一下该接口:

public interface HttpMessageConverter{//是否可读,clazz是java类型,mediaType是http请求类型boolean canRead(Class clazz,MediaType mediaType);//判断clazz类型能否转换为mediaType媒体类型boolean canWrite(Class clazz,Mediatype mediaType);//可支持的媒体类型列表List getSupportedMeidaTypes();//当canRead验证通过后,读入http请求信息T read(Class clazz,HttpInputMessage inputMessage) throws IOException,HttpMessageNotReadableException;//当canWrite方法验证通过后写入响应void write(T t,Mediatype contentType,HttpOutputMessage outPutMessage) throws IOException,HttpMessageNotWritableExecption;
 }
 

  下面对@ResponseBody注解进行解析,这个注解会使处理器采用请求体的内容进行参数转换,而前端的请求体是json,因此会先调用canRead方法判断可读,之后调用read方法将前端的参数转换为控制器User类型的参数,这样控制器就能得到参数了。

  下面讨论一下处理器转换参数的过程,在SpringMVC中是用WebDataBinder机制,作用是解析http请求的上下文,然后在控制器调用之前转换参数。

  处理器会从http请求中读取数据,然后通过三种接口来进行各类参数转换,Converter、Formatter、GenericConverter,这三个接口的实现类都是采用注册机机制,默认MVC注册多个转换器。

  下面是http请求体的转换流程:
在这里插入图片描述
  上述的流程并不一定完全走完,对于三个接口Converter处理普通简单的参数,如前端传递字符串,而后端用int接收,那么他就负责将前端的string转换为int,而Formatter是负责格式化处理参数,最后GenericConverter负责将字符转换为数组的。

  对于数据类型转换,SpringMVC提供一个服务机制去管理,他就是ConversionService接口,默认使用这个接口的子类DefaultFormattingConversionService对象管理这些转换类,下面是关系图:
在这里插入图片描述
  上述的三个转换接口通过注册机进行注册,在SpringBoot中还提供一个特殊的机制管理这些转换器,SpringBoot的自动配置类有一个方法可以实现注册:

@Override
 public void addFormatters(FormatterRegistry registry){//遍历IOC容器,找到Converter类型的Bean注册到服务类中for(Converter converter : getBeansOfType(Converter.class)){registry.addConverter(converter);}//遍历IOC容器,找到GenericConverter类型的Bean注册到服务类中for(GenericConverter converter : getBeansOfType(GenericConverter.class)){registry.addConverter(converter);}//遍历IOC容器,找到Formatter类型的Bean注册到服务类中for(Formatter formatter : getBeansOfType(Formatter.class)){registry.addConverter(formatter);}
 }
 

  我们可以实现三个转换器接口,SpringBoot会自动注册到服务类中。

2.3.2 一对一转换器(Converter)

  这个接口实现简单的一对一转换,定义如下:

public interface Converter{//转换方法,S代表原类型,T代表目标类型T Convert(S source);
 }
 

  可以看出通过Convert方法转换,如果是简单的类似string到int的转换,该方法会使用Spring默认的StringToNumber( T extends Number )进行转换。
  但是如果是前端传递一个{id}-{userName}格式的数据,而控制器的参数是User对象,那么只能自定义实现了:

@Component
 public class StringToUserConverter implements Converter{/***转换方法*/@Overridepublic User convert(String userStr){User user = new User();String [] strArr = userStr.split("-");Long id = Long.parseLong(strArr[0]);String userName = strArr[1];user.setId(id);user.setUserName(userName);return user;}
 }
 

  这里使用注解@Component标注,并且实现了Converter接口,SpringBoot会扫描到IOC中,并在初始化的时候自动将这个类注册到转换机制中,下面开发一个控制器:

@GetMapping
 @ResponseBody
 public User getUserByConverter(User user){return user;
 }
 

  在浏览器传入参数?user=1-userName1即可完成测试。

2.3.3 GenericConverter集合和数组的转换

  GenericConverter是数组转换器,讨论一下他的自定义实现,假设我们需要传递多个用户,那么MVC会使用 StringToCollectionConverter进行转换,他实现了GenericConverter接口,且已经是SpringMVC注册的转换器。
  他首先会将字符串以逗号分隔为一个个子串,然后根据每个子串和我们之前定义好的StringToUserConverter进行匹配,这样就可以转换为List
  下面开发一个控制器:

@GetMapping("/list")
 @ResponseBody
 public List list(List userList){return userList;
 }
 

  下面传递参数:http://localhost/list?userList=1-username1,2-username2,3-username3即可完成验证。

2.4 数据验证

  上述介绍的是参数的转换和传递,紧接着就是参数的合法性验证,SpringBoot默认引入关于Hibernate Validator机制来支持JSR-303验证规范,有时还需要自定义验证机制。

2.4.1 JSR-303验证

  主要是通过注解的方式验证,先定义一个pojo:

public class ValidatorPojo{//非空判断@NotNull(message="id不能为空")private Long id;@Future(message="需要一个将来的日期")//只能是将来日期//@Past//只能是过去的日期@DateTimeFormat(pattern="yyyy-MM-dd")//日期格式化转换@NotNull//不能为空private Date date;@NotNull//不能为空@DecimalMin(value="0.1")//最小值0.1元@DecimalMax(value="10000.00")//最大值10000.00元private Double doubleValue = null;@Min(value=1,message="最小值为1")@Max(value=88,message="最大值为88")@NotNull//不能为空private Integer integer;@Range(min=1,max=88,message="范围是1到88")private Long range;//邮箱验证@Email(message="邮箱格式错误")private String email;@Size(min=20,max=30,message="字符串长度要求在20到30之间")private String size;
 }
 

  上述就是使用JSR-303注解验证的案例,下面开发一个控制器:

/**
 *解析参数验证错误
 *@Param vp——需要验证的pojo,使用注解@Valid表示验证
 *@Param errors 他由MVC验证参数后自动填充
 *@Return 返回错误信息map
 */
 @RequestMapping("/valid/validate")
 @ResponseBody
 public Map validate(@Valid @RequestBody ValidatorPojo vp,Errors errors){Map errMap = new HashMap<>();//获取错误列表List oes = errors.getAllErrors();for(ObjectErrors oe : oes){String key = null;String msg = null;//字段错误if(oe instanceof FieldError){FieldError fr = (FieldError)oe;key = fe.getField();//获取错误验证字段名}else{//非字段错误key = oe.getObjectName()//获取验证对象名称}//错误信息msg = oe.getDefaultMessage();errMap.put(key,msg);}return errMap;
 }
 

  上述就是JSR-303验证,但有时验证逻辑较为复杂,因此也可以自定义。

2.4.2 参数验证机制

  之前的注册转换器的WebDataBinder还可以注册验证器,注解@InitBinder允许在进入控制器方法前修改WebDataBinder的机制,在那之前先了解一下SpringMVC的验证机制,在SpringMVC中定义了一个接口Validator:

public interface Validator{/***判定当前验证器是否支持该Class类型的验证*@Param clazz pojo类型*@return 当前验证器是否支持该pojo验证*/boolean supports(Class clazz);/***如果supports返回true,则这个方法执行验证逻辑*@param target 被验证pojo对象*@param errors 错误信息对象*/void validate(Object target,Errors errors);
 }
 

  自定义验证器实现这个接口即可,下面看一下案例:

public class UserValidator implements Validator{//该验证器只支持User类验证@Overridepublic boolean supports(Class clazz){return clazz.equals(User.class);}//验证逻辑@Overridepublic void validate(Object target ,Errors errors){//对象为空if(target == null){//直接在参数出报错,这样就不能进入控制器的方法errors.rejectValue("",null,"用户不能为空");return ;}//强制转换User user = (User)target;//用户名非空串if(StringUtils.isEmpty(user.getUserName())){//增加错误可以进入控制器方法errors.rejectValue("userName",null,"用户名不能为空");}}
 }
 

  有了上述的自定义验证器,但还不能运行,因为需要注册给WebDataBinder,使用注解@InitBinder将会在执行控制器之前先执行被这个注解标注的方法,将WebDataBinder对象传递进去,然后使用这个对象的setValidator方法绑定自定义的验证器,除此之外还可以进行参数的自定义,看下面的控制器

@Controller
 @RequestMapping("/user")
 public class UserController{/***调用控制器前先执行这个方法*@param binder*/@InitBinderpublic void initBinder(WebDataBinder binder){//绑定验证器binder.setValidator(new UserValidator());//定义日期参数格式,参数不在需要注解@DateTimeFormat,boolean表示允许参数为空binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpeDateFormat("yyyy-MM-dd"),false));}/***@param user 用户对象用StringToUserConverter转换*@param errors 验证器返回的错误*@param date 因为WebDataBinder已经绑定了格式,所以不再需要注解*/@GetMapping("validator")@ResponseBodypublic Map validator(@Valid User user,Errors errors,Date date){Map map = new HashMap<>();map.put("user",user);map.put("date",date);//判断是否存在错误if(errors.hasErrors()){//获取全部错误List oes = Errors.getAllErrors();for(ObjectError oe : oes){//判断是否字段错误if(oe instanceof FieldError){//字段错误FieldError fe = (FieldError)oe;map.put(fe.getField(),fe.getDefaultMessage());}else{//对象错误map.put(oe.getObjectName(),oe.getDefaultMessage());}}}return map;} ....
 }
 

  绑定了验证器和规定好日期格式后,通过注解@Valid标注,此时SpringMVC就会遍历对应的验证器,匹配验证器之后去验证User参数。

2.5 数据模型

  我们知道控制器的核心就是对数据的处理,在SpringMVC中提供了模型和视图,先来看模型,就是存放数据的地方,下图是SpringMVC使用的模型接口:
在这里插入图片描述
  SpringMVC有三种数据模型对象,ModelAndView、Model、ModelMap,这三个对象会由SpringMVC自动创建:

@RequestMapping("/data")
 @Controller
 public class DataModelContrller{//注入用户服务类@Autowiredprivate UserService userService = null;//测试Model接口@GetMapping("/model")public String userModel(Long id,Model model){User user = userService.getUser(id);model.addAttribute("user",user);//这里返回字符串,SpringMVC会自动创建ModelAndView且绑定名称return "data/user";}//测试ModelMap类@GetMapping("/modelMap")public ModelAndView useModelMap(Long id,ModelMap,modelMap){User user = userService.getUser(id);ModelAndView mv = new ModelAndView();//设置视图名称mv.setViewName("data/user");//设置数据模型,这里系统会自动将modelMap与mv绑定modelMap.put("user",user);return mv;}//测试ModelAndView@GetMapping("/mav")public ModelAndView useModelAndView(Long id,ModelandView mv){User user = userService.getUser(id);//设置数据模型mv.addObject("user",user);//设置视图名称mv.setViewName("data/user");return mv;}
 }
 

2.6 视图和视图解析器

  视图是将数据模型渲染给用户的组件,视图分为逻辑视图和非逻辑视图,对于逻辑视图需要视图解析器(常用的如:InternalResourceViewResolver)去解析,才能最终定位,而非逻辑视图仅仅将数据模型渲染出来即可,如MappingJackson2JsonView。

2.6.1 视图设计

  除了Json、Jsp等,还有其他的视图如Excel、PDF等,但都需要实现SpringMVC定义的接口View:

public interface View{//响应状态属性String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";//路径变量String PATH_VARIABLES = View.class.getName() + ".pathVaariables";//选择内容类型String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";//响应类型String getContentType();//渲染方法void render(Map model,HttpServletRequest request,HttpServletResponse response) throws Exception;
 }
 

  其中,getContentType是获取Http响应类型,而render方法是将数据模型渲染到视图的,model是从控制器返回的模型,这样就可渲染,渲染过程还是比较复杂的,因此SpringMVC提供了一些实现好的视图:
在这里插入图片描述
  对于其他的视图,可以阅读官方文档。

2.7 文件上传

2.7.1 SpringMVC对文件上传的支持

  DispatcherServlet会使用适配器模式,将HttpServletRequest接口对象转换为MultiPartHttpServletRequest对象,而他扩展了HttpServletRequest接口的所有方法:
在这里插入图片描述
  这样在文件上传时会将参数的接口对象进行转换,但在使用SpringMVc实现文件上传时,还需要配置MultiPartHttpServletRequest对象,这个任务是通过MultiPartResolver接口实现的,他存在两个实现类,StandardServletMultiPartResolver和CommonsMultiPartResolver,推荐使用前者,后者需要依赖apahce的第三方包。
  在SpringBoot机制中,如果没有自定义MultiPartResolver接口,SpringBoot会为我们自动创建对象StandardServletMultiPartResolver,并提供简单的配置:

//是否使用SpringMVC的多分部上传功能
 spring.servlet.multipart.enabled=true
 //将文件写入磁盘的阈值,值可以使用MB或KB作为单位
 spring.servlet.multipart.file-size-threshold=0
 //指定默认的上传文件夹
 spring.servlet.multipart.location=E://data
 //限制单个文件最大的大小
 spring.servlet.multipart.max-file-size=1MB
 //限制所有文件最大大小
 spring.servlet.multipart.max-request-size=10MB
 //是否延迟多部件文件请求的参数和文件的解析
 spring.servlet.multipart.resolve-lazily=false
 

  之后可以使用Servlet规范的接口Part作为参数,也可以使用SpringMVC提供的MultipartFile接口作为参数,后者需要依赖第三方包。

2.7.2 开发文件上传功能

  开发控制器前需要自定义配置一些文件大小等参数,不在赘述,下面开始开发控制器:

@Controller
 @RequestMapping("/file")
 public class FileController{//使用HttpServletRequest对象作为参数@PostMapping("/upload/request")@ResponseBodypublic Map uploadRequest(HttpServletRequest request){boolean flag = false;MultipartHttpServletRequest mreq = null;//强制转换为MultipartHttpServletRequest接口对象if(request instanceof MultpartHttpServletRequest){mreq = (MultipartHttpServletRequest) request;}else{return dealResultMap(false,"上传失败");}//获取MultipartFile文件信息MultipartFile mf = mreq.getFile("file");//获取源文件名String filename = mf.getOriginalFilename();File file = new file(filename);try{//保存文件mf.transferTo(file);}catch(Exception e){e.printStackTrace();return dealResultMap(false,"上传失败");}return dealResultMap(true,"上传成功");}//使用SpringMVC的MultipartFile类作为参数@PostMapping("/upload/multipartFile")@ResponseBodypublic Map uploadMultipartFile(MultipartFile file){String filename = file.getOriginalFilename();File dest = new File(filename);try{file.transferto(dest);}catch(Exception e){e.printStackTrace();return dealResultMap(false,"上传失败");}return dealResultMap(true,"上传成功");}//测试part接口作为参数@PostMapping("/upload/part")@ResponseBodypublic Map uploadPart(Part file){//获取提交文件名String filename = file.getSubmittedFilename();try{//写入文件file.write(filename);}catch(Exception e){e.printStackTrance();return dealResultMap(fasle,"上传失败");}return dealResultMap(true,"上传成功");}//处理上传结果private Map dealResultMap(boolean success,String msg){Map reslut = new HashMap();result.put("success",success);result.put("msg",msg);return result;}
 }
 

2.8 拦截器

  之前谈到HandlerMapping机制,当请求来到DispatcherServlet时,会通过HandlerMapping机制来匹配控制器,这样会返回一个HandlerExecutionChain对象,它包含了控制器和拦截器,而拦截器是来增强控制器功能的,有点类似SpringAOP。

2.8.1 拦截器的设计

  首先所有拦截器都要实现HandlerInterceptor接口:

public interface HandlerInterceptor{//处理器执行前方法default boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{return true;}//处理器处理后方法default void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,@Nullable ModelAndView modelAndView) throws Exception{}//处理器完成后方法default void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,@Nullable Exception ex) throws Exception{}
 }
 

  下面看一下这些拦截方法的执行流程
在这里插入图片描述

2.8.2 开发拦截器

  根据需要重写对应的方法即可,但是重写之后还不能被SpringMVC发现,需要我们注册才可以使用,为此需要实现接口WebMvcConfigurer接口,并重写addInterceptors方法进行注册拦截器即可:
开发拦截器

public class Interceptor1 extends HandlerInterceptor{@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{System.out.println("处理器前方法");//返回true来继续后面的处理return true;}@Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception{System.out.println("处理器后的方法");}
 }
 

注册拦截器

@Configuration
 public class MyInterceptors implements WebMvcConfigurer{@Overridepublic void addInterceptors(IntercepterRegistry registry){//注册拦截器到SpringMVC机制,然后会返回一个拦截器注册InterceptorRegistration ir = registry.addInterceptor(new Interceptor1());//指定拦截匹配模式,限制拦截器拦截请求ir.addPathPatterns("/interceptor/*");//会拦截所有/interceptr路径的请求}
 }
 

  下面开发一个控制器

@Controller
 @RequestMapping("/interceptor")
 public class InterceptorController{@GetMapping("/start")public String start(){System.out.pringln("执行处理器逻辑");return "/welcome";//返回欢迎页面}
 }
 

  对于多个拦截器,按照按先注册先执行的方式进行,如:
1->2->3->处理器->3>2->1,对于处理器前方法返回false的场景是,执行到返回false后,之后的所有拦截器、处理器和所有拦截器的postHandle方法都不会执行。

2.9 SpringMVC拾遗

2.9.1 @ResponseBody转换为Json的秘密

  在方法上标注@ResponseBody注解后,处理器会记录这个方法的响应类型是Json,当执行完控制器返回后,处理器会启用结果解析器去解析这个结果,他会去轮询注册给SpringMVC的HttpMessageConverter接口的实现类,因为MappingJackson2HttpMessageConverter已经注册给SpringMVC,而SpringMVC将控制器的结果类型标注为Json,因此就可匹配上,于是在处理器内部把结果转换为Json。
下面是流程图:
在这里插入图片描述

2.9.2 重定向

  重定向就是通过各种方法将各种网络请求重新转向其他位置。假设一个这样的需求,向数据库插入新用户,之后通过JSP展现给请求者,则开发一个如下的控制器:

@Controller
 @RequestMapping("/user")
 public class UserController{//显示用户@GetMapping("/show")public String showUser(Long id,Model model){User user = userService.getUser(id);model.addAttribute("user",user);return "data/user";}//使用字符串指定跳转@GetMapping("/redirect1")public String redirect1(String username,String note){User user = new User();user.setUserName(username);user.setNote(note);//插入后主键回填userService.insertUser(user);return "redirect:/user/show?id=" + user.getId();}//使用模型和视图指定跳转@GetMapping("/redirect2")public ModelAndView redirect2(String userName,String note){User user = new User();user.setUserName(username);user.setNote(note);userService.insertUser(user);ModelAndView mv = new ModelAndView();mv.setViewName("redirect:/user/show?id=" + user.getId());return mv;}
 }
 

2.9.3 操作会话对象

  操作HTTPSession十分普遍,SpringMVC提供了两个注解来操作,@SessionAttribute和@SessionAttributes,前者用于参数,将HTTPSession中的属性读出,赋予控制器的参数。后者只用于类的注解,将相关数据模型的属性保存到session中。不太常用,有兴趣可以查看官网即可。

2.10 总结

  SpringMVC的内部原理还是比较复杂的,我们需要做的就是了解各个组件之间的流程,以及可以在哪些组件加入业务代码,比如控制器,就是主要的业务逻辑,我们可以开发几个用于特定用途的拦截器,比如用户登录验证等操作都需要进行验证。还有比如淘宝买东西,当点击购买或加入购物车等涉及到用户信息的操作时,会进行拦截,判断一下用户是否登录。
  如上类似的功能组件,需要我们在工作学习中一点点的体会,当对各个组件以及他们的关系有一定的了解后,可以阅读源码来进一步的深入学习。


标签:

上一篇: 饿了么的移动APM架构实践 下一篇:
素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。