Java方式配置Spring MVC

概述

  使用Java方式配置Spring MVC,以及回顾一下Spring MVC的各种用法。


Spring MVC简述

  关于Spring MVC的介绍网上有很多,这里就不再赘述了,只是要说一下,Spring MVC中的“MVC”是什么意思,它和三层架构是什么关系。

  可能有很多人说,在三层架构中,M就是数据访问层,V就是展现层,C就是应用层。

  听上去很有道理,但其实MVC就是数据模型(Model)+视图(View)+控制器(Controller),在Spring MVC中,有一个专门的类叫Model,用来和V之间的数据交互、传值;V指的是视图页面,包含JSP、Thymeleaf等;C就是控制器。MVC只存在于三层架构中的展现层。

搭建一个Spring MVC项目

  1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3     <modelVersion>4.0.0</modelVersion>
  4     <groupId>com.wisely</groupId>
  5     <artifactId>highlight_springmvc4</artifactId>
  6     <version>0.0.1-SNAPSHOT</version>
  7     <packaging>war</packaging>
  8 
  9     <properties>
 10         <!-- Generic properties -->
 11         <java.version>1.7</java.version>
 12         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 13         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 14         <!-- Web -->
 15         <jsp.version>2.2</jsp.version>
 16         <jstl.version>1.2</jstl.version>
 17         <servlet.version>3.1.0</servlet.version>
 18         <!-- Spring -->
 19         <spring-framework.version>4.1.5.RELEASE</spring-framework.version>
 20         <!-- Logging -->
 21         <logback.version>1.0.13</logback.version>
 22         <slf4j.version>1.7.5</slf4j.version>
 23     </properties>
 24 
 25     <dependencies>
 26         <dependency>
 27             <groupId>javax</groupId>
 28             <artifactId>javaee-web-api</artifactId>
 29             <version>7.0</version>
 30             <scope>provided</scope>
 31         </dependency>
 32 
 33         <!-- Spring MVC -->
 34         <dependency>
 35             <groupId>org.springframework</groupId>
 36             <artifactId>spring-webmvc</artifactId>
 37             <version>${spring-framework.version}</version>
 38         </dependency>
 39 
 40         <!-- 其他web依赖 -->
 41         <dependency>
 42             <groupId>javax.servlet</groupId>
 43             <artifactId>jstl</artifactId>
 44             <version>${jstl.version}</version>
 45         </dependency>
 46         
 47         <dependency>
 48             <groupId>javax.servlet</groupId>
 49             <artifactId>javax.servlet-api</artifactId>
 50             <version>${servlet.version}</version>
 51             <scope>provided</scope>
 52         </dependency>
 53         
 54         <dependency>
 55             <groupId>javax.servlet.jsp</groupId>
 56             <artifactId>jsp-api</artifactId>
 57             <version>${jsp.version}</version>
 58             <scope>provided</scope>
 59         </dependency>
 60 
 61         <!-- Spring and Transactions -->
 62         <dependency>
 63             <groupId>org.springframework</groupId>
 64             <artifactId>spring-tx</artifactId>
 65             <version>${spring-framework.version}</version>
 66         </dependency>
 67 
 68         <!-- 使用SLF4J和LogBack作为日志 -->
 69         <dependency>
 70             <groupId>org.slf4j</groupId>
 71             <artifactId>slf4j-api</artifactId>
 72             <version>${slf4j.version}</version>
 73         </dependency>
 74         <dependency>
 75             <groupId>log4j</groupId>
 76             <artifactId>log4j</artifactId>
 77             <version>1.2.16</version>
 78         </dependency>
 79         <dependency>
 80             <groupId>org.slf4j</groupId>
 81             <artifactId>jcl-over-slf4j</artifactId>
 82             <version>${slf4j.version}</version>
 83         </dependency>
 84         <dependency>
 85             <groupId>ch.qos.logback</groupId>
 86             <artifactId>logback-classic</artifactId>
 87             <version>${logback.version}</version>
 88         </dependency>
 89         <dependency>
 90             <groupId>ch.qos.logback</groupId>
 91             <artifactId>logback-core</artifactId>
 92             <version>${logback.version}</version>
 93         </dependency>
 94         <dependency>
 95             <groupId>ch.qos.logback</groupId>
 96             <artifactId>logback-access</artifactId>
 97             <version>${logback.version}</version>
 98         </dependency>
 99 
100         <!--对json和xml格式的支持 -->
101         <dependency>
102             <groupId>com.fasterxml.jackson.dataformat</groupId>
103             <artifactId>jackson-dataformat-xml</artifactId>
104             <version>2.5.3</version>
105         </dependency>
106 
107         <!-- file upload -->
108         <dependency>
109             <groupId>commons-fileupload</groupId>
110             <artifactId>commons-fileupload</artifactId>
111             <version>1.3.1</version>
112         </dependency>
113         <!-- 非必需,可简化IO操作 -->
114         <dependency>
115             <groupId>commons-io</groupId>
116             <artifactId>commons-io</artifactId>
117             <version>2.3</version>
118         </dependency>
119 
120         <dependency>
121             <groupId>org.springframework</groupId>
122             <artifactId>spring-test</artifactId>
123             <version>${spring-framework.version}</version>
124             <scope>test</scope>
125         </dependency>
126         
127     
128         <dependency>
129             <groupId>junit</groupId>
130             <artifactId>junit</artifactId>
131             <version>4.11</version>
132             <scope>test</scope>
133         </dependency>
134 
135     </dependencies>
136 
137     <build>
138         <plugins>
139             <plugin>
140                 <groupId>org.apache.maven.plugins</groupId>
141                 <artifactId>maven-compiler-plugin</artifactId>
142                 <version>2.3.2</version>
143                 <configuration>
144                     <source>${java.version}</source>
145                     <target>${java.version}</target>
146                 </configuration>
147             </plugin>
148             <plugin>
149                 <groupId>org.apache.maven.plugins</groupId>
150                 <artifactId>maven-war-plugin</artifactId>
151                 <version>2.3</version>
152                 <configuration>
153                     <failOnMissingWebXml>false</failOnMissingWebXml>
154                 </configuration>
155             </plugin>
156         </plugins>
157     </build>
158 </project>
pom.xml(Maven)
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <configuration scan="true" scanPeriod="1 seconds">
 3     <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
 4         <resetJUL>true</resetJUL>
 5     </contextListener>
 6     <jmxConfigurator/>
 7     <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
 8         <encoder>
 9             <pattern>logbak: %d{HH:mm:ss.SSS} %logger{36} - %msg%n</pattern>
10         </encoder>
11     </appender>
12     <!--将org.springframework.web包下的类的日志级别设置为DEBUG,在开发Spring MVC时经常出现和参数类型相关的4XX错误,设置-->
13     <!--此项可以看到更详细的错误信息-->
14     <logger name="org.springframework.web" level="DEBUG"/>
15     <root level="info">
16         <appender-ref ref="console"/>
17     </root>
18 </configuration>
logback.xml(日志配置)
 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>Insert title here</title>
 8 </head>
 9 <body>
10     <pre>
11         Welcome to Spring MVC world
12     </pre>
13 </body>
14 </html>
index.jsp(演示页面)
  1 package com.wisely.highlight_springmvc4;
  2 
  3 import com.wisely.highlight_springmvc4.interceptor.DemoInterceptor;
  4 import com.wisely.highlight_springmvc4.messageconverter.MyMessageConverter;
  5 import org.springframework.context.annotation.Bean;
  6 import org.springframework.context.annotation.ComponentScan;
  7 import org.springframework.context.annotation.Configuration;
  8 import org.springframework.http.converter.HttpMessageConverter;
  9 import org.springframework.scheduling.annotation.EnableScheduling;
 10 import org.springframework.web.multipart.MultipartResolver;
 11 import org.springframework.web.multipart.commons.CommonsMultipartResolver;
 12 import org.springframework.web.servlet.config.annotation.*;
 13 import org.springframework.web.servlet.view.InternalResourceViewResolver;
 14 import org.springframework.web.servlet.view.JstlView;
 15 
 16 import java.util.List;
 17 
 18 /**
 19  * Spring MVC配置
 20  */
 21 @Configuration
 22 @EnableWebMvc//开启一些默认配置,如一些ViewResolver(视图解析器)或者MessageConverter(消息转换器)等。
 23 @EnableScheduling
 24 @ComponentScan("com.wisely.highlight_springmvc4")
 25 public class MyMvcConfig extends WebMvcConfigurerAdapter {// 2
 26 
 27     /**
 28      * 映射路径和实际页面的位置
 29      *
 30      * @return
 31      */
 32     @Bean
 33     public InternalResourceViewResolver viewResolver() {
 34         InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
 35         viewResolver.setPrefix("/WEB-INF/classes/views/");
 36         viewResolver.setSuffix(".jsp");
 37         viewResolver.setViewClass(JstlView.class);
 38         return viewResolver;
 39     }
 40 
 41     /**
 42      * 静态资源映射
 43      * @param registry
 44      */
 45     @Override
 46     public void addResourceHandlers(ResourceHandlerRegistry registry) {
 47         //addResourceHandler是对外暴露的访问路径,addResourceLocations是文件放置的目录。
 48         registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
 49     }
 50 
 51     /**
 52      * 配置拦截器的Bean。
 53      * @return
 54      */
 55     @Bean
 56     public DemoInterceptor demoInterceptor() {
 57         return new DemoInterceptor();
 58     }
 59 
 60     /**
 61      * 重写addInterceptors方法,注册拦截器。
 62      * @param registry
 63      */
 64     @Override
 65     public void addInterceptors(InterceptorRegistry registry) {// 2
 66         registry.addInterceptor(demoInterceptor());
 67     }
 68 
 69     /**
 70      * 集中设置页面转向
 71      * 等同下列代码:
 72      *      @RequestMapping("/index")
 73      *      public String hello() {
 74      *          return "index";
 75      *      }
 76      * @param registry
 77      */
 78     @Override
 79     public void addViewControllers(ViewControllerRegistry registry) {
 80         registry.addViewController("/index").setViewName("/index");
 81         registry.addViewController("/toUpload").setViewName("/upload");
 82         registry.addViewController("/converter").setViewName("/converter");
 83         registry.addViewController("/sse").setViewName("/sse");
 84         registry.addViewController("/async").setViewName("/async");
 85     }
 86 
 87     /**
 88      * 不忽略路径参数中"."后面的值
 89      * @param configurer
 90      */
 91     @Override
 92     public void configurePathMatch(PathMatchConfigurer configurer) {
 93         configurer.setUseSuffixPatternMatch(false);
 94     }
 95 
 96     /**
 97      * 配置文件上传
 98      * @return
 99      */
100     @Bean
101     public MultipartResolver multipartResolver() {
102         CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
103         multipartResolver.setMaxUploadSize(1000000);
104         return multipartResolver;
105     }
106 
107     @Override
108     public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
109         converters.add(converter());
110     }
111 
112     @Bean
113     public MyMessageConverter converter() {
114         return new MyMessageConverter();
115     }
116 }
MyMvcConfig.java(Spring MVC配置)
 1 package com.wisely.highlight_springmvc4;
 2 
 3 import org.springframework.web.WebApplicationInitializer;
 4 import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
 5 import org.springframework.web.servlet.DispatcherServlet;
 6 
 7 import javax.servlet.ServletContext;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRegistration.Dynamic;
10 
11 /**
12  * Web配置
13  * <p>
14  * WebApplicationInitializer是Spring提供用来配置Servlet 3.0+配置的接口,从而实现了替代web.xml的位置。
15  * 实现此接口将会自动被SpringServletContainerInitializer(用来启动Servlet 3.0容器)获取到。
16  */
17 public class WebInitializer implements WebApplicationInitializer {
18 
19     @Override
20     public void onStartup(ServletContext servletContext)
21             throws ServletException {
22         //新建WebApplicationContext
23         AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
24         //注册配置类
25         ctx.register(MyMvcConfig.class);
26         //将其和当前servletContext关联。
27         ctx.setServletContext(servletContext);
28 
29         //注册Spring MVC的DispatcherServlet
30         Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
31         servlet.addMapping("/");
32         servlet.setLoadOnStartup(1);
33         servlet.setAsyncSupported(true);//开启异步方法支持
34     }
35 }
WebInitializer.java(Web配置)

 Spring MVC的常用注解

  @Controller:

      @Controller注解在类上,表明这个类是Spring MVC里的Controller,将其声明为Spring的一个Bean,Dispatcher Servlet会自动扫描注解了此注解的类,并将Web请求映射到注解了@RequestMapping的方法上。

  @RequestMapping:

      @RequestMapping注解是用来映射Web请求(访问路径和参数)、处理类和方法的。@RequestMapping可注解在类或方法上。注解在方法上的@RequestMapping路径会继承注解在类上的路径,@RequestMapping支持Servlet的request和response作为参数,也支持对request和response的媒体类型进行配置。

  @ResponseBody:

      @ResponseBody支持将返回值放在response体内,而不是返回一个页面。我们在很多编写基于Ajax的程序的时候,可以以此注解返回数据而不是页面;此注解可放置在返回值前或方法上。

  @RequestBody:

      @RequestBody允许request的参数在request体中,而不是直接链接在地址后面。此注解放置在参数前。

  @PathVariable:

      @PathVariable用来接收路径参数,如/news/001,可接收001作为参数,此注解放置在参数前。

  @RestController:

      @RestController是一个组合注解,组合了@Controller和@@ResponseBody,这就意味着当你只开发一个和页面交互数据的控制器的适口,需要使用此注解。如果不用此注解的话,则需要使用@Controller和@ResponseBody两个注解。

  下面是一些例子。

 1 package com.wisely.highlight_springmvc4.domain;
 2 
 3 /**
 4  * 此类用来获取request对象参数和返回此对象到response。
 5  */
 6 public class DemoObj {
 7     private Long id;
 8     private String name;
 9 
10     /**
11      * jackson对对象和json做转换时一定需要此空构造。
12      */
13     public DemoObj() {
14         super();
15     }
16 
17     public DemoObj(Long id, String name) {
18         super();
19         this.id = id;
20         this.name = name;
21     }
22 
23     public Long getId() {
24         return id;
25     }
26 
27     public void setId(Long id) {
28         this.id = id;
29     }
30 
31     public String getName() {
32         return name;
33     }
34 
35     public void setName(String name) {
36         this.name = name;
37     }
38 }
DemoObj.java
 1 package com.wisely.highlight_springmvc4.web.ch4_3;
 2 
 3 import com.wisely.highlight_springmvc4.domain.DemoObj;
 4 import org.springframework.stereotype.Controller;
 5 import org.springframework.web.bind.annotation.PathVariable;
 6 import org.springframework.web.bind.annotation.RequestMapping;
 7 import org.springframework.web.bind.annotation.ResponseBody;
 8 
 9 import javax.servlet.http.HttpServletRequest;
10 
11 /**
12  * 注解演示控制器
13  */
14 @Controller //此注解声明此类是一个控制器。
15 @RequestMapping("/anno") //此注解映射此类的访问路径时/anno
16 public class DemoAnnoController {
17     /**
18      * 此方法为标注路径,因此使用类级别的路径/anno。
19      * produces可定制返回的response的媒体类型和字符集,若返回值是json对象,则设置produces = "application/json;charset=UTF-8"。
20      *
21      * @param request
22      * @return
23      */
24     @RequestMapping(produces = "text/plain;charset=UTF-8")
25     //此处的@ResponseBody用在方法返回值前面
26     public @ResponseBody
27     String index(HttpServletRequest request) { //可以直接使用HttpServletRequest作为参数,也可以直接使用HttpServletResponse作为参数。
28         return "url:" + request.getRequestURL() + " can access";
29     }
30 
31     @RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8")//设置路径中的参数
32     public @ResponseBody
33     String demoPathVar(@PathVariable String str, //结合@PathVariable注解,接收路径中的参数,访问路径为/anno/pathvar/xx
34                        HttpServletRequest request) {
35         return "url:" + request.getRequestURL() + " can access,str: " + str;
36     }
37 
38     @RequestMapping(value = "/requestParam", produces = "text/plain;charset=UTF-8") //常规的request获取参数,访问路径为/anno/requestParam?id=1
39     public @ResponseBody
40     String passRequestParam(Long id, HttpServletRequest request) {
41         return "url:" + request.getRequestURL() + " can access,id: " + id;
42     }
43 
44     @RequestMapping(value = "/obj", produces = "application/json;charset=UTF-8")//映射参数到对象,访问路径为/anno/obj?id=1&name=xx
45     @ResponseBody //@ResponseBody也可以用在方法上面
46     public String passObj(DemoObj obj, HttpServletRequest request) {
47         return "url:" + request.getRequestURL() + " can access, obj id: " + obj.getId() + " obj name:" + obj.getName();
48     }
49 
50     @RequestMapping(value = {"/name1", "/name2"}, produces = "text/plain;charset=UTF-8")//将不同的路径映射到相同的方法上,访问路径为/anno/name1或者/anno/name2
51     public @ResponseBody
52     String remove(HttpServletRequest request) {
53         return "url:" + request.getRequestURL() + " can access";
54     }
55 }
DemoAnnoController.java
 1 package com.wisely.highlight_springmvc4.web.ch4_3;
 2 
 3 import com.wisely.highlight_springmvc4.domain.DemoObj;
 4 import org.springframework.web.bind.annotation.RequestMapping;
 5 import org.springframework.web.bind.annotation.RestController;
 6 
 7 /**
 8  * '@RestController'注解演示
 9  */
10 @RestController //使用@RestController注解声明此类是控制器,并且返回数据时不需要@ResponseBody。
11 @RequestMapping("/rest")
12 public class DemoRestController {
13 
14     @RequestMapping(value = "/getjson",
15             produces = {"application/json;charset=UTF-8"}) //设置返回数据的媒体类型为json。
16     public DemoObj getjson(DemoObj obj) {
17         return new DemoObj(obj.getId() + 1, obj.getName() + "yy");//直接返回对象,对象会自动转换成json。
18     }
19 
20     @RequestMapping(value = "/getxml",
21             produces = {"application/xml;charset=UTF-8"})//设置返回数据的媒体类型为xml
22     public DemoObj getxml(DemoObj obj) {
23         return new DemoObj(obj.getId() + 1, obj.getName() + "yy");//直接返回对象,对象会自动转换为xml。
24     }
25 }
DemoRestController.java

Spring MVC基本配置

  Spring MVC的定制配置需要无门的配置类继承一个WebMvcConfigurerAdapter类,并在此类使用@EnableWebMvc注解,来开启对Spring MVC的配置支持,这样我们就可以重写这个类的方法,完成我们的常用配置。

  静态资源映射:

 1 /**
 2      * 映射路径和实际页面的位置
 3      *
 4      * @return
 5      */
 6     @Bean
 7     public InternalResourceViewResolver viewResolver() {
 8         InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
 9         viewResolver.setPrefix("/WEB-INF/classes/views/");
10         viewResolver.setSuffix(".jsp");
11         viewResolver.setViewClass(JstlView.class);
12         return viewResolver;
13     }
14 
15     /**
16      * 静态资源映射
17      * @param registry
18      */
19     @Override
20     public void addResourceHandlers(ResourceHandlerRegistry registry) {
21         //addResourceHandler是对外暴露的访问路径,addResourceLocations是文件放置的目录。
22         registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
23     }
静态资源映射

  拦截器配置:

      拦截器实现对每一个请求处理前后进行相关的业务处理,类似于Servlet的Filter。

      可以让普通的Bean实现HanlderInterceptor接口或者继承HandlerInterceptorAdapter类来实现自定义拦截器。

      通过重写WebMvcConfigurerAdapter的addInterceptors方法来注册自定义的拦截器。

 1 package com.wisely.highlight_springmvc4.interceptor;
 2 
 3 import org.springframework.web.servlet.ModelAndView;
 4 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 5 
 6 import javax.servlet.http.HttpServletRequest;
 7 import javax.servlet.http.HttpServletResponse;
 8 
 9 /**
10  * 拦截器
11  */
12 public class DemoInterceptor extends HandlerInterceptorAdapter {//集成HandlerInterceptorAdapter类来实现自定义拦截器
13 
14     @Override
15     public boolean preHandle(HttpServletRequest request, //重写preHandle方法,在请求发生前执行
16             HttpServletResponse response, Object handler) throws Exception {
17         long startTime = System.currentTimeMillis();
18         request.setAttribute("startTime", startTime);
19         return true;
20     }
21 
22     @Override
23     public void postHandle(HttpServletRequest request, //重写postHandle方法,在请求完成后执行
24             HttpServletResponse response, Object handler,
25             ModelAndView modelAndView) throws Exception {
26         long startTime = (Long) request.getAttribute("startTime");
27         request.removeAttribute("startTime");
28         long endTime = System.currentTimeMillis();
29         System.out.println("本次请求处理时间为:" + new Long(endTime - startTime)+"ms");
30         request.setAttribute("handlingTime", endTime - startTime);
31     }
32 
33 }
DemoInterceptor.java(拦截器)
 1     /**
 2      * 配置拦截器的Bean。
 3      * @return
 4      */
 5     @Bean
 6     public DemoInterceptor demoInterceptor() {
 7         return new DemoInterceptor();
 8     }
 9 
10     /**
11      * 重写addInterceptors方法,注册拦截器。
12      * @param registry
13      */
14     @Override
15     public void addInterceptors(InterceptorRegistry registry) {// 2
16         registry.addInterceptor(demoInterceptor());
17     }
配置拦截器

  @ControllerAdvice:

      通过@ControllerAdvice,我们可以将对于控制器的全局配置放在同一个位置,注解了@Controller的类的方法可使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内的方法都有效。

      @ExceptionHandler:用于全局处理控制器里的异常。

      @InitBinder:用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。

      @ModelAttribute:这个注解本来的作用是绑定键值对到Model里,此处是让全局的@RequestMapping都能获得在此处设置的键值对。

 1 package com.wisely.highlight_springmvc4.advice;
 2 
 3 import org.springframework.ui.Model;
 4 import org.springframework.web.bind.WebDataBinder;
 5 import org.springframework.web.bind.annotation.ControllerAdvice;
 6 import org.springframework.web.bind.annotation.ExceptionHandler;
 7 import org.springframework.web.bind.annotation.InitBinder;
 8 import org.springframework.web.bind.annotation.ModelAttribute;
 9 import org.springframework.web.context.request.WebRequest;
10 import org.springframework.web.servlet.ModelAndView;
11 
12 /**
13  * 定制ControllerAdvice,处理全局异常
14  */
15 @ControllerAdvice //@ControllerAdvice声明一个控制器建言,@ControllerAdvice组合了@Component注解,所以自动注册为Spring的Bean。
16 public class ExceptionHandlerAdvice { 
17 
18     @ExceptionHandler(value = Exception.class)//@ExceptionHandler在此处定义全局处理,通过@ExceptionHandler的value属性可过滤拦截的条件,此处拦截所有的Exception。
19     public ModelAndView exception(Exception exception, WebRequest request) {
20         ModelAndView modelAndView = new ModelAndView("error");// error页面
21         modelAndView.addObject("errorMessage", exception.getMessage());
22         return modelAndView;
23     }
24 
25     @ModelAttribute //使用@ModelAttribute注解将键值对添加到全局,所有注解的@RequestMapping的方法可获得此键值对。
26     public void addAttributes(Model model) {
27         model.addAttribute("msg", "额外信息"); //3
28     }
29 
30     @InitBinder //通过@InitBinder注解定制WebDataBinder
31     public void initBinder(WebDataBinder webDataBinder) {
32         webDataBinder.setDisallowedFields("id");//忽略掉request参数中的id参数
33     }
34 }
ExceptionHandlerAdvice.java
 1 package com.wisely.highlight_springmvc4.web.ch4_4;
 2 
 3 import org.springframework.stereotype.Controller;
 4 import org.springframework.web.bind.annotation.ModelAttribute;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 
 7 import com.wisely.highlight_springmvc4.domain.DemoObj;
 8 
 9 /**
10  * 异常控制器
11  */
12 @Controller
13 public class AdviceController {
14     @RequestMapping("/advice")
15     public String getSomething(@ModelAttribute("msg") String msg,DemoObj obj){//1
16         
17         throw new IllegalArgumentException("非常抱歉,参数有误/"+"来自@ModelAttribute:"+ msg);
18     }
19 
20 }
AdviceController.java
 1 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
 2 <%@ page language="java" contentType="text/html; charset=UTF-8"
 3     pageEncoding="UTF-8"%>
 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 5 <html>
 6 <head>
 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 8 <title>@ControllerAdvice Demo</title>
 9 </head>
10 <body>
11     ${errorMessage}
12 </body>
13 </html>
error.jsp

Spring MVC其他配置

  ViewController:

      快捷的页面转向,方便统一管理。

 1 /**
 2      * 集中设置页面转向
 3      * 等同下列代码:
 4      *      @RequestMapping("/index")
 5      *      public String hello() {
 6      *          return "index";
 7      *      }
 8      * @param registry
 9      */
10     @Override
11     public void addViewControllers(ViewControllerRegistry registry) {
12         registry.addViewController("/index").setViewName("/index");
13         registry.addViewController("/toUpload").setViewName("/upload");
14         registry.addViewController("/converter").setViewName("/converter");
15         registry.addViewController("/sse").setViewName("/sse");
16         registry.addViewController("/async").setViewName("/async");
17     }
ViewController

  路径参数配置:

      在Spring MVC中,路径参数如果带“.”的话,“.”后面的值将被忽略,通过重写configurePathMatch方法可不忽略后面的值。

1 /**
2      * 不忽略路径参数中"."后面的值
3      * @param configurer
4      */
5     @Override
6     public void configurePathMatch(PathMatchConfigurer configurer) {
7         configurer.setUseSuffixPatternMatch(false);
8     }
configurePathMatch

 Spring MVC高级配置

  文件上传配置:

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>upload page</title>
 8 
 9 </head>
10 <body>
11 
12 
13 <div class="upload">
14     <form action="upload" enctype="multipart/form-data" method="post">
15         <input type="file" name="file"/><br/>
16         <input type="submit" value="上传">
17     </form>
18 </div>
19 
20 
21 </body>
22 </html>
upload.jsp
 1     /**
 2      * 配置文件上传
 3      * @return
 4      */
 5     @Bean
 6     public MultipartResolver multipartResolver() {
 7         CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
 8         multipartResolver.setMaxUploadSize(1000000);
 9         return multipartResolver;
10     }
配置
 1 package com.wisely.highlight_springmvc4.web.ch4_5;
 2 
 3 import org.apache.commons.io.FileUtils;
 4 import org.springframework.stereotype.Controller;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 import org.springframework.web.bind.annotation.RequestMethod;
 7 import org.springframework.web.bind.annotation.ResponseBody;
 8 import org.springframework.web.multipart.MultipartFile;
 9 
10 import java.io.File;
11 import java.io.IOException;
12 
13 @Controller
14 public class UploadController {
15     
16     @RequestMapping(value = "/upload",method = RequestMethod.POST)
17     public @ResponseBody String upload(MultipartFile file) {//使用MultipartFile file接收上传的文件
18         
19             try {
20                 //使用FileUtils.writeByteArrayToFile快速写文件到磁盘
21                 FileUtils.writeByteArrayToFile(new File("e:/upload/"+file.getOriginalFilename()), file.getBytes());
22                 return "ok";
23             } catch (IOException e) {
24                 e.printStackTrace();
25                 return "wrong";
26             }
27             
28         
29     }
30 
31 }
UploadController

  自定义HttpMessageConverter:

       HttpMessageConverter是用来处理request和response里的数据的。Spring为我们内置了大量的HttpMessageConverter,例如MappingJackson2HttpMessageConverter、StringHttpMessageConverter等。除此之外,我们也可以自己定义。

 1 package com.wisely.highlight_springmvc4.messageconverter;
 2 
 3 import com.wisely.highlight_springmvc4.domain.DemoObj;
 4 import org.springframework.http.HttpInputMessage;
 5 import org.springframework.http.HttpOutputMessage;
 6 import org.springframework.http.MediaType;
 7 import org.springframework.http.converter.AbstractHttpMessageConverter;
 8 import org.springframework.http.converter.HttpMessageNotReadableException;
 9 import org.springframework.http.converter.HttpMessageNotWritableException;
10 import org.springframework.util.StreamUtils;
11 
12 import java.io.IOException;
13 import java.nio.charset.Charset;
14 
15 public class MyMessageConverter extends AbstractHttpMessageConverter<DemoObj> {//继承AbstractHttpMessageConverter接口来实现自定义的HttpMessageConverter
16 
17     public MyMessageConverter() {
18         super(new MediaType("application", "x-wisely",Charset.forName("UTF-8")));//新建一个自定义的媒体类型application/x-wisely
19     }
20     
21     /**
22      * 重写readInternal方法,处理请求的数据。
23      * 代码表明我们处理由“-”隔开的数据,并转成DemoObj的对象。
24      */
25     @Override
26     protected DemoObj readInternal(Class<? extends DemoObj> clazz,
27             HttpInputMessage inputMessage) throws IOException,
28             HttpMessageNotReadableException {
29         String temp = StreamUtils.copyToString(inputMessage.getBody(),
30 
31         Charset.forName("UTF-8"));
32         String[] tempArr = temp.split("-");
33         return new DemoObj(new Long(tempArr[0]), tempArr[1]);
34     }
35     
36     /**
37      * 表明本HttpMessageConverter只处理DemoObj这个类。
38      */
39     @Override
40     protected boolean supports(Class<?> clazz) {
41         return DemoObj.class.isAssignableFrom(clazz);
42     }
43     
44     /**
45      * 重写writeInternal,处理如何输出数据到response。
46      * 此例中,我们在原样输出前面加上“hello:”。
47      */
48     @Override
49     protected void writeInternal(DemoObj obj, HttpOutputMessage outputMessage)
50             throws IOException, HttpMessageNotWritableException {
51         String out = "hello:" + obj.getId() + "-"
52                 + obj.getName();
53         outputMessage.getBody().write(out.getBytes());
54     }
55 
56 }
MyMessageConverter.java
 1 /**
 2      * 在Spring MVC里注册HttpMessageConverter有两种方法:
 3      *      1:configureMessageConverters:重载会覆盖掉Spring MVC默认注册的多个HttpMessageConverter。
 4      *      2:extendMessageConverters:仅添加一个自定义的HttpMessageConverter,不覆盖默认注册的HttpMessageConverter。
 5      * @param converters
 6      */
 7     @Override
 8     public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
 9         converters.add(converter());
10     }
11 
12     @Bean
13     public MyMessageConverter converter() {
14         return new MyMessageConverter();
15     }
配置
 1 package com.wisely.highlight_springmvc4.web.ch4_5;
 2 
 3 import org.springframework.stereotype.Controller;
 4 import org.springframework.web.bind.annotation.RequestBody;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 import org.springframework.web.bind.annotation.ResponseBody;
 7 
 8 import com.wisely.highlight_springmvc4.domain.DemoObj;
 9 
10 @Controller
11 public class ConverterController {
12     
13     @RequestMapping(value = "/convert", produces = { "application/x-wisely" }) //指定返回的媒体类型为我们自定义的媒体类型application/x-wisely。
14     public @ResponseBody DemoObj convert(@RequestBody DemoObj demoObj) {
15         return demoObj;
16     }
17 }
ConverterController.java
 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>HttpMessageConverter Demo</title>
 8 </head>
 9 <body>
10     <div id="resp"></div><input type="button" onclick="req();" value="请求"/>
11 <script src="assets/js/jquery.js" type="text/javascript"></script>
12 <script>
13     function req(){
14         $.ajax({
15             url: "convert",
16             data: "1-gaofei", //注意数据格式,使用“-”隔开
17             type:"POST",
18             contentType:"application/x-wisely", //contentType设置的媒体类型是我们自定义的application/x-wisely。
19             success: function(data){
20                 $("#resp").html(data);
21             }
22         });
23     }
24 
25 </script>
26 </body>
27 </html>
converter.jsp

 服务器端推送

  SSE(需新式浏览器支持):

 1 package com.wisely.highlight_springmvc4.web.ch4_5;
 2 
 3 import org.springframework.stereotype.Controller;
 4 import org.springframework.web.bind.annotation.RequestMapping;
 5 import org.springframework.web.bind.annotation.ResponseBody;
 6 
 7 import java.util.Random;
 8 
 9 @Controller
10 public class SseController {
11     /**
12      * 这里使用输出的媒体类型为text/event-stream,这是服务器端SSE的支持,本例演示每5秒钟像浏览器推送随机消息。
13      *
14      * @return
15      */
16     @RequestMapping(value = "/push", produces = "text/event-stream")
17     public @ResponseBody
18     String push() {
19         Random r = new Random();
20         try {
21             Thread.sleep(5000);
22         } catch (InterruptedException e) {
23             e.printStackTrace();
24         }
25         return "data:Testing 1,2,3" + r.nextInt() + "\n\n";
26     }
27 
28 }
SseController.java
 1 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
 2 <%@ page language="java" contentType="text/html; charset=UTF-8"
 3     pageEncoding="UTF-8"%>
 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 5 <html>
 6 <head>
 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 8 <title>SSE Demo</title>
 9 
10 </head>
11 <body>
12 
13 
14 <div id="msgFrompPush"></div>
15 <script type="text/javascript" src="<c:url value="assets/js/jquery.js" />"></script>
16 <script type="text/javascript">
17 
18 
19  if (!!window.EventSource) { //EventSource对象只有新式的浏览器才有(Chrome、Firefox等),EventSource是SSE的客户端。
20        var source = new EventSource('push'); 
21        s='';
22        source.addEventListener('message', function(e) {//添加SSE客户端监听,在此获得服务器端推送的消息
23            s+=e.data+"<br/>";
24            $("#msgFrompPush").html(s);
25          
26        });
27 
28        source.addEventListener('open', function(e) {
29             console.log("连接打开.");
30        }, false);
31 
32        source.addEventListener('error', function(e) {
33             if (e.readyState == EventSource.CLOSED) {
34                console.log("连接关闭");
35             } else {
36                 console.log(e.readyState);    
37             }
38        }, false);
39     } else {
40             console.log("你的浏览器不支持SSE");
41     } 
42 </script>
43 </body>
44 </html>
sse.jsp

  Servlet3.0+的异步方法(跨浏览器):

1 //注册Spring MVC的DispatcherServlet
2         Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
3         servlet.addMapping("/");
4         servlet.setLoadOnStartup(1);
5         servlet.setAsyncSupported(true);//开启异步方法支持
配置
 1 package com.wisely.highlight_springmvc4.web.ch4_5;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.stereotype.Controller;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 import org.springframework.web.bind.annotation.ResponseBody;
 7 import org.springframework.web.context.request.async.DeferredResult;
 8 
 9 import com.wisely.highlight_springmvc4.service.PushService;
10 
11 @Controller
12 public class AysncController {
13     @Autowired
14     PushService pushService; //定时任务,定时更新DeferredResult
15 
16     /**
17      * 返回给客户端DeferredResult
18      * @return
19      */
20     @RequestMapping("/defer")
21     @ResponseBody
22     public DeferredResult<String> deferredCall() { //2
23         return pushService.getAsyncUpdate();
24     }
25 
26 }
AysncController.java
 1 package com.wisely.highlight_springmvc4.service;
 2 
 3 import org.springframework.scheduling.annotation.Scheduled;
 4 import org.springframework.stereotype.Service;
 5 import org.springframework.web.context.request.async.DeferredResult;
 6 
 7 @Service
 8 public class PushService {
 9     private DeferredResult<String> deferredResult; //在PushService里产生DeferredResult给控制器使用,通过@Scheduled注解的方式定时更新DeferredResult。
10 
11     public DeferredResult<String> getAsyncUpdate() {
12         deferredResult = new DeferredResult<String>();
13         return deferredResult;
14     }
15 
16     @Scheduled(fixedDelay = 5000)
17     public void refresh() {
18         if (deferredResult != null) {
19             deferredResult.setResult(new Long(System.currentTimeMillis())
20                     .toString());
21         }
22     }
23 }
PushService.java
 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>servlet async support</title>
 8 
 9 </head>
10 <body>
11 
12 
13 
14 <script type="text/javascript" src="assets/js/jquery.js"></script>
15 <script type="text/javascript">
16 
17     deferred();//页面打开就向后台发送请求。
18     
19     function deferred(){
20         $.get('defer',function(data){
21             console.log(data); //在控制台输出服务端推送的数据。
22             deferred(); //一次请求完成后再向后台发送请求。
23         });
24     }
25 
26 
27 </script>
28 </body>
29 </html>
async.jsp

 Spring MVC的测试

 1 package com.wisely.highlight_springmvc4.service;
 2 
 3 import org.springframework.stereotype.Service;
 4 
 5 @Service
 6 public class DemoService {
 7     
 8     public String saySomething(){
 9         return "hello";
10     }
11 
12 }
DemoService.java
 1 package com.wisely.highlight_springmvc4.web.ch4_6;
 2 
 3 
 4 import com.wisely.highlight_springmvc4.MyMvcConfig;
 5 import com.wisely.highlight_springmvc4.service.DemoService;
 6 import org.junit.Before;
 7 import org.junit.Test;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.mock.web.MockHttpServletRequest;
11 import org.springframework.mock.web.MockHttpSession;
12 import org.springframework.test.context.ContextConfiguration;
13 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14 import org.springframework.test.context.web.WebAppConfiguration;
15 import org.springframework.test.web.servlet.MockMvc;
16 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
17 import org.springframework.web.context.WebApplicationContext;
18 
19 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
20 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
21 
22 @RunWith(SpringJUnit4ClassRunner.class)
23 @ContextConfiguration(classes = {MyMvcConfig.class})
24 //@WebAppConfiguration注解在类上,用来声明加载的ApplicationContext是一个WebApplicationContext。
25 //它的属性指定的是Web资源的位置,默认为src/main/webapp,本例修改为src/main/resource。
26 @WebAppConfiguration("src/main/resources")
27 public class TestControllerIntegrationTests {
28     private MockMvc mockMvc; //MockMvc模拟MVC对象。
29     
30     @Autowired
31     private DemoService demoService;//在测试用例中注入Spring的Bean。
32     
33     @Autowired 
34     WebApplicationContext wac; //注入WebApplicationContext。
35     
36     @Autowired 
37     MockHttpSession session; //注入模拟的Http Session。
38     
39     @Autowired 
40     MockHttpServletRequest request; //注入模拟的Http Request。
41     
42     @Before //@Before注解表示在测试开始前进行的初始化工作。
43     public void setup() {
44         mockMvc =
45                 MockMvcBuilders.webAppContextSetup(this.wac).build(); //通过MockMvcBuilders.webAppContextSetup(this.wac).build()初始化MockMvc。
46         }
47     
48     @Test
49     public void testNormalController() throws Exception{
50         mockMvc.perform(get("/normal")) //模拟向/normal进行get请求。
51                 .andExpect(status().isOk())//预期控制返回状态为200。
52                 .andExpect(view().name("page"))//预期view的名称为page。
53                 .andExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp"))//预期页面转向的真正路径为/WEB-INF/classes/views/page.jsp。
54                 .andExpect(model().attribute("msg", demoService.saySomething()));//预期model里的值是demoService.saySomething()的返回值hello。
55                 
56     }
57     
58     @Test
59     public void testRestController() throws Exception{
60         mockMvc.perform(get("/testRest")) //模拟向/testRest进行get请求。
61         .andExpect(status().isOk())
62          .andExpect(content().contentType("text/plain;charset=UTF-8"))//预期返回值的媒体类型为text/plain;charset=UTF-8。
63         .andExpect(content().string(demoService.saySomething()));//预期返回值的内容是demoService.saySomething()的返回值hello。
64     }
65 
66 }
TestControllerIntegrationTests.java
 1 package com.wisely.highlight_springmvc4.web.ch4_6;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.stereotype.Controller;
 5 import org.springframework.ui.Model;
 6 import org.springframework.web.bind.annotation.RequestMapping;
 7 
 8 import com.wisely.highlight_springmvc4.service.DemoService;
 9 
10 //@Controller
11 public class NormalController {
12     @Autowired
13     DemoService demoService;
14     
15 
16     
17     @RequestMapping("/normal")
18     public  String testPage(Model model){
19         model.addAttribute("msg", demoService.saySomething());
20         return "page";
21     }
22 
23 }
NormalController.java
 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>Test page</title>
 8 </head>
 9 <body>
10     <pre>
11         Welcome to Spring MVC world
12     </pre>
13 </body>
14 </html>
page.jsp

猜你喜欢

转载自www.cnblogs.com/gaofei-1/p/8971749.html