使用Java方式配置Spring MVC,以及回顾一下Spring MVC的各类用法。javascript
关于Spring MVC的介绍网上有不少,这里就再也不赘述了,只是要说一下,Spring MVC中的“MVC”是什么意思,它和三层架构是什么关系。html
可能有不少人说,在三层架构中,M就是数据访问层,V就是展示层,C就是应用层。java
听上去颇有道理,但其实MVC就是数据模型(Model)+视图(View)+控制器(Controller),在Spring MVC中,有一个专门的类叫Model,用来和V之间的数据交互、传值;V指的是视图页面,包含JSP、Thymeleaf等;C就是控制器。MVC只存在于三层架构中的展示层。jquery
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>
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>
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>
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 }
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 }
@Controller:web
@Controller注解在类上,代表这个类是Spring MVC里的Controller,将其声明为Spring的一个Bean,Dispatcher Servlet会自动扫描注解了此注解的类,并将Web请求映射到注解了@RequestMapping的方法上。ajax
@RequestMapping:spring
@RequestMapping注解是用来映射Web请求(访问路径和参数)、处理类和方法的。@RequestMapping可注解在类或方法上。注解在方法上的@RequestMapping路径会继承注解在类上的路径,@RequestMapping支持Servlet的request和response做为参数,也支持对request和response的媒体类型进行配置。apache
@ResponseBody:json
@ResponseBody支持将返回值放在response体内,而不是返回一个页面。咱们在不少编写基于Ajax的程序的时候,能够以此注解返回数据而不是页面;此注解可放置在返回值前或方法上。api
@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 }
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 }
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 }
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 }
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 }
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 }
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>
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 }
路径参数配置:
在Spring MVC中,路径参数若是带“.”的话,“.”后面的值将被忽略,经过重写configurePathMatch方法可不忽略后面的值。
1 /** 2 * 不忽略路径参数中"."后面的值 3 * @param configurer 4 */ 5 @Override 6 public void configurePathMatch(PathMatchConfigurer configurer) { 7 configurer.setUseSuffixPatternMatch(false); 8 }
文件上传配置:
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>
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 }
自定义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 }
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 }
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>
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 }
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>
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 }
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 }
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>
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 }
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 }
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 }
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>