使用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>