科技
springmvc(5千字的SpringMVC总结,我觉得你会需要)

思维导图

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

SpringMVC再熟悉不过的框架了,因为现在最火的SpringBoot的内置MVC框架就是SpringMVC。我写这篇文章的动机是想通过回顾总结一下,重新认识SpringMVC,所谓温故而知新嘛。

从流程图中,我们可以看到:

  • 接收前端传过来Request请求。
  • 根据映射路径找到对应的处理器处理请求,处理完成之后返回ModelAndView。
  • 进行视图解析,视图渲染,返回响应结果。

当然这只是最基本的核心功能,除此之外还可以定义拦截器,全局异常处理,文件上传下载等等。

一、搭建项目

使用SpringMVC定义Controller处理器,总共有五种方式。

2.1 实现Controller接口

跟第一种方式差不多,也是通过实现接口的方式:

@Controller("/http/controller")public class HttpDemoController implements HttpRequestHandler{    @Override    public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {        //业务处理    }}

2.3 实现Servlet接口

因为不推荐使用这种方式,所以默认是不加载这种适配器的,需要加上:

@Configuration@EnableWebMvcpublic class WebMvcConfig extends WebMvcConfigurerAdapter {    @Bean    public SimpleServletHandlerAdapter simpleServletHandlerAdapter() {        return new SimpleServletHandlerAdapter();    }}

2.4 使用@RequestMapping

而且支持Restful风格,使用method属性定义对资源的操作方式:

 @RequestMapping(value = "/restful", method = RequestMethod.GET)    public String get() {        //查询        return "get";    }    @RequestMapping(value = "/restful", method = RequestMethod.POST)    public String post() {        //创建        return "post";    }    @RequestMapping(value = "/restful", method = RequestMethod.PUT)    public String put() {        //更新        return "put";    }    @RequestMapping(value = "/restful", method = RequestMethod.DELETE)    public String del() {        //删除        return "post";    }

2.4.2 支持Ant风格

 //匹配 /antA 或者 /antB 等URL    @RequestMapping("/ant?")    public String ant() {        return "ant";    }    //匹配 /ant/a/create 或者 /ant/b/create 等URL    @RequestMapping("/antcreate")    public String antAllCreate() {        return "antAllCreate";    }

2.5 使用HandlerFunction

有兴趣的可以网上搜索相关资料学习,这个讲起来可能要很大篇幅,这里就不赘述了。

三、接收参数

在@RequestMapping映射方法上写上接收参数名即可:

@RequestMapping(value = "/restful", method = RequestMethod.POST)public String post(Integer id, String name, int money) {    System.out.println("id:" + id + ",name:" + name + ",money:" + money);    return "post";}
5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

3.2 @RequestParam参数名绑定

通过@PathVariable将URL中的占位符{xxx}参数映射到操作方法的入参。演示代码如下:

@RequestMapping(value = "/restful/{id}", method = RequestMethod.GET)public String search(@PathVariable("id") String id) {    System.out.println("id:" + id);    return "search";}
5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

3.4 @RequestHeader绑定请求头属性

使用@RequestHeader注解,用法和@RequestParam类似:

 @RequestMapping("/head")    public String head(@RequestHeader("Accept-Language") String acceptLanguage) {        return acceptLanguage;    }
5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

3.5 @cookievalue绑定请求的cookie值

定义了一个User实体类:

public class User {    private String id;    private String name;    private Integer age;    //getter、setter方法}

只要请求参数与属性名相同自动填充到user对象中:

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

3.6.1 支持级联属性

在User中加上address属性:

public class User {    private String id;    private String name;    private Integer age;    private Address address;    //getter、setter方法}

如果有两个POJO对象拥有相同的属性名,不就产生冲突了吗?比如刚刚的user和address,其中他们都有id和name这两个属性,如果同时接收,就会冲突:

 //user和address都有id和name这两个属性  @RequestMapping(value = "/twoBody", method = RequestMethod.POST)    public String twoBody(User user, Address address) {        return user.toString() + "," + address.toString();    }

前端传入一个json字符串,自动转换成pojo对象,演示代码:

 @RequestMapping(value = "/requestBody", method = RequestMethod.POST)    public String requestBody(@RequestBody User user) {        return user.toString();    }

甚至有一些人喜欢用一个Map接收:

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

实际上,SpringMVC框架本身就内置了很多类型转换器,比如你传入字符串的数字,接收的入参定为int,long类型,都会自动帮你转换。

有的时候如果内置的类型转换器不足够满足业务需求呢,怎么扩展呢,很简单,看我操作。什么是Java技术爱好者(战术后仰)。

接着把转换器注册到Spring容器中:

@Configurationpublic class ConverterConfig extends WebMvcConfigurationSupport {    @Override    protected void addFormatters(FormatterRegistry registry) {        //添加类型转换器        registry.addConverter(new StringToDateConverter());    }}

在前后端未分离之前,页面跳转的工作都是由后端控制,采用JSP进行展示数据。虽然现在互联网项目几乎不会再使用JSP,但是我觉得还是需要学习一下,因为有些旧项目还是会用JSP,或者需要重构。

第一步,加入解析jsp的Maven配置。

<dependency>    <groupId>org.apache.tomcat.embed</groupId>    <artifactId>tomcat-embed-jasper</artifactId>    <version>7.0.59</version></dependency><dependency>    <groupId>javax.servlet</groupId>    <artifactId>jstl</artifactId></dependency>

第三步,设置IDEA的配置。

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

第五步,创建Controller控制器。

@Controller@RequestMapping("/view")public class ViewController {    @RequestMapping("/hello")    public String hello() throws Exception {        return "hello";    }}

就是这么简单,对吧

六、@ResponseBody

使用@ResponseBody注解即可,这个注解会把对象自动转成json数据返回。

演示一下:

@RequestMapping("/userList")@ResponseBodypublic List<User> userList() throws Exception {    List<User> list = new ArrayList<>();    list.add(new User("1","姚大秋",18));    list.add(new User("2","李星星",18));    list.add(new User("3","冬敏",18));    return list;}

@ModelAttribute用法比较多,下面一一讲解。

7.1 用在无返回值的方法上

index.jsp页面如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>首页</title></head><body><!-- 获取到userName属性值 --><h1>${userName}</h1></body></html>

即使在index()方法中没有放入userName属性值,jsp页面也能获取到,因为在执行index()方法之前的modelAttribute()方法已经放入了。

7.2 放在有返回值的方法上

创建一个user.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>首页</title></head><body><h1>ID:${user.id}</h1><h1>名称:${user.name}</h1><h1>年龄:${user.age}岁</h1></body></html>

放入Request域中的属性值默认是类名的首字母小写驼峰写法,如果你想自定义呢?很简单,可以这样写:

//自定义属性名为"u"@ModelAttribute("u")public User userAttribute() {    return new User("1", "Java技术爱好者", 18);}

7.3 放在RequestMapping方法上

@Controller@RequestMapping("/modelAttribute")public class ModelAttributeController {        @RequestMapping("/jojo")    @ModelAttribute("attributeName")    public String jojo() {        return "JOJO!我不做人了!";    }}

测试一下:

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

7.4 放在方法入参上

测试一下:

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

八、拦截器

很简单,实现HandlerInterceptor接口,接口有三个方法需要重写。

  • preHandle():在业务处理器处理请求之前被调用。预处理。
  • postHandle():在业务处理器处理请求执行完成后,生成视图之前执行。后处理。
  • afterCompletion():在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);

然后把拦截器添加到Spring容器中:

@Configurationpublic class ConverterConfig extends WebMvcConfigurationSupport {    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");    }}

SpringMVC本身就对一些异常进行了全局处理,所以有内置的异常处理器,在哪里呢?

从类图可以看出有四种异常处理器:

  • DefaultHandlerExceptionResolver,默认的异常处理器。根据各个不同类型的异常,返回不同的异常视图。
  • SimpleMappingExceptionResolver,简单映射异常处理器。通过配置异常类和view的关系来解析异常。
  • ResponseStatusExceptionResolver,状态码异常处理器。解析带有@ResponseStatus注释类型的异常。
  • ExceptionHandlerExceptionResolver,注解形式的异常处理器。对@ExceptionHandler注解的方法进行异常解析。

翻译过来就是简单映射异常处理器。用途是,我们可以指定某种异常,当抛出这种异常之后跳转到指定的页面。请看演示。

第二步,在启动类加载xml文件:

@SpringBootApplication@importResource("classpath:spring-config.xml")public class SpringmvcApplication {    public static void main(String[] args) {        SpringApplication.run(SpringmvcApplication.class, args);    }}

这样就完成了,写一个接口测试一下:

@Controller@RequestMapping("/exception")public class ExceptionController {    @RequestMapping("/index")    public String index(String msg) throws Exception {        if ("null".equals(msg)) {            //抛出空指针异常            throw new NullPointerException();        }        return "index";    }}

这种异常处理器,在现在前后端分离的项目中几乎已经看不到了。

9.2 ResponseStatusExceptionResolver

自定义一个异常类,并且使用@ResponseStatus注解修饰:

//HttpStatus枚举有所有的状态码,这里返回一个400的响应码@ResponseStatus(value = HttpStatus.BAD_REQUEST)public class DefinedException extends Exception{}

启动项目,测试一下,效果如下:

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

9.3 ExceptionHandlerExceptionResolver

第一步,定义自定义异常baseException:

public class baseException extends Exception {    public baseException(String message) {        super(message);    }}

第三步,定义全局异常处理类GlobalExceptionHandler:

//这里使用了RestControllerAdvice//是@ResponseBody和@ControllerAdvice的结合//会把实体类转成JSON格式的提示返回,符合前后端分离的架构@RestControllerAdvicepublic class GlobalExceptionHandler {    //这里自定义了一个baseException,当抛出baseException异常就会被此方法处理    @ExceptionHandler(baseException.class)    public ErrorInfo errorHandler(HttpServletRequest req, baseException e) throws Exception {        ErrorInfo r = new ErrorInfo();        r.setMessage(e.getMessage());        r.setCode(ErrorInfo.ERROR);        r.setUrl(req.getRequestURL().toString());        return r;    }}

启动项目,测试:

5千字的SpringMVC总结,我觉得你会需要nerror="javascript:errorimg.call(this);">

絮叨

如果要再深入一些,最好是看看SpringMVC源码,我之前写过三篇,责任链模式与SpringMVC拦截器,适配器模式与SpringMVC,全局异常处理源码分析

https://github.com/yehongzhi/mall

点个关注,不怕迷路!

能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!


顶一下()     踩一下()

热门推荐

发表评论
0评