Java方式配置Spring MVC

时间:2023-03-08 18:55:02

概述

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

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wisely</groupId>
<artifactId>highlight_springmvc4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging> <properties>
<!-- Generic properties -->
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Web -->
<jsp.version>2.2</jsp.version>
<jstl.version>1.2</jstl.version>
<servlet.version>3.1.0</servlet.version>
<!-- Spring -->
<spring-framework.version>4.1.5.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties> <dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency> <!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-framework.version}</version>
</dependency> <!-- 其他web依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency> <dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency> <!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency> <!-- 使用SLF4J和LogBack作为日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>${logback.version}</version>
</dependency> <!--对json和xml格式的支持 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.5.3</version>
</dependency> <!-- file upload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 非必需,可简化IO操作 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>

pom.xml(Maven)

 <?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="1 seconds">
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<jmxConfigurator/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>logbak: %d{HH:mm:ss.SSS} %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--将org.springframework.web包下的类的日志级别设置为DEBUG,在开发Spring MVC时经常出现和参数类型相关的4XX错误,设置-->
<!--此项可以看到更详细的错误信息-->
<logger name="org.springframework.web" level="DEBUG"/>
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>

logback.xml(日志配置)

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<pre>
Welcome to Spring MVC world
</pre>
</body>
</html>

index.jsp(演示页面)

 package com.wisely.highlight_springmvc4;

 import com.wisely.highlight_springmvc4.interceptor.DemoInterceptor;
import com.wisely.highlight_springmvc4.messageconverter.MyMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView; import java.util.List; /**
* Spring MVC配置
*/
@Configuration
@EnableWebMvc//开启一些默认配置,如一些ViewResolver(视图解析器)或者MessageConverter(消息转换器)等。
@EnableScheduling
@ComponentScan("com.wisely.highlight_springmvc4")
public class MyMvcConfig extends WebMvcConfigurerAdapter {// /**
* 映射路径和实际页面的位置
*
* @return
*/
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/classes/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
} /**
* 静态资源映射
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//addResourceHandler是对外暴露的访问路径,addResourceLocations是文件放置的目录。
registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
} /**
* 配置拦截器的Bean。
* @return
*/
@Bean
public DemoInterceptor demoInterceptor() {
return new DemoInterceptor();
} /**
* 重写addInterceptors方法,注册拦截器。
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {//
registry.addInterceptor(demoInterceptor());
} /**
* 集中设置页面转向
* 等同下列代码:
* @RequestMapping("/index")
* public String hello() {
* return "index";
* }
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("/index");
registry.addViewController("/toUpload").setViewName("/upload");
registry.addViewController("/converter").setViewName("/converter");
registry.addViewController("/sse").setViewName("/sse");
registry.addViewController("/async").setViewName("/async");
} /**
* 不忽略路径参数中"."后面的值
* @param configurer
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
} /**
* 配置文件上传
* @return
*/
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
} @Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
} @Bean
public MyMessageConverter converter() {
return new MyMessageConverter();
}
}

MyMvcConfig.java(Spring MVC配置)

 package com.wisely.highlight_springmvc4;

 import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic; /**
* Web配置
* <p>
* WebApplicationInitializer是Spring提供用来配置Servlet 3.0+配置的接口,从而实现了替代web.xml的位置。
* 实现此接口将会自动被SpringServletContainerInitializer(用来启动Servlet 3.0容器)获取到。
*/
public class WebInitializer implements WebApplicationInitializer { @Override
public void onStartup(ServletContext servletContext)
throws ServletException {
//新建WebApplicationContext
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//注册配置类
ctx.register(MyMvcConfig.class);
//将其和当前servletContext关联。
ctx.setServletContext(servletContext); //注册Spring MVC的DispatcherServlet
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);//开启异步方法支持
}
}

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两个注解。

  下面是一些例子。

 package com.wisely.highlight_springmvc4.domain;

 /**
* 此类用来获取request对象参数和返回此对象到response。
*/
public class DemoObj {
private Long id;
private String name; /**
* jackson对对象和json做转换时一定需要此空构造。
*/
public DemoObj() {
super();
} public DemoObj(Long id, String name) {
super();
this.id = id;
this.name = name;
} public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

DemoObj.java

 package com.wisely.highlight_springmvc4.web.ch4_3;

 import com.wisely.highlight_springmvc4.domain.DemoObj;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; /**
* 注解演示控制器
*/
@Controller //此注解声明此类是一个控制器。
@RequestMapping("/anno") //此注解映射此类的访问路径时/anno
public class DemoAnnoController {
/**
* 此方法为标注路径,因此使用类级别的路径/anno。
* produces可定制返回的response的媒体类型和字符集,若返回值是json对象,则设置produces = "application/json;charset=UTF-8"。
*
* @param request
* @return
*/
@RequestMapping(produces = "text/plain;charset=UTF-8")
//此处的@ResponseBody用在方法返回值前面
public @ResponseBody
String index(HttpServletRequest request) { //可以直接使用HttpServletRequest作为参数,也可以直接使用HttpServletResponse作为参数。
return "url:" + request.getRequestURL() + " can access";
} @RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8")//设置路径中的参数
public @ResponseBody
String demoPathVar(@PathVariable String str, //结合@PathVariable注解,接收路径中的参数,访问路径为/anno/pathvar/xx
HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access,str: " + str;
} @RequestMapping(value = "/requestParam", produces = "text/plain;charset=UTF-8") //常规的request获取参数,访问路径为/anno/requestParam?id=1
public @ResponseBody
String passRequestParam(Long id, HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access,id: " + id;
} @RequestMapping(value = "/obj", produces = "application/json;charset=UTF-8")//映射参数到对象,访问路径为/anno/obj?id=1&name=xx
@ResponseBody //@ResponseBody也可以用在方法上面
public String passObj(DemoObj obj, HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access, obj id: " + obj.getId() + " obj name:" + obj.getName();
} @RequestMapping(value = {"/name1", "/name2"}, produces = "text/plain;charset=UTF-8")//将不同的路径映射到相同的方法上,访问路径为/anno/name1或者/anno/name2
public @ResponseBody
String remove(HttpServletRequest request) {
return "url:" + request.getRequestURL() + " can access";
}
}

DemoAnnoController.java

 package com.wisely.highlight_springmvc4.web.ch4_3;

 import com.wisely.highlight_springmvc4.domain.DemoObj;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* '@RestController'注解演示
*/
@RestController //使用@RestController注解声明此类是控制器,并且返回数据时不需要@ResponseBody。
@RequestMapping("/rest")
public class DemoRestController { @RequestMapping(value = "/getjson",
produces = {"application/json;charset=UTF-8"}) //设置返回数据的媒体类型为json。
public DemoObj getjson(DemoObj obj) {
return new DemoObj(obj.getId() + 1, obj.getName() + "yy");//直接返回对象,对象会自动转换成json。
} @RequestMapping(value = "/getxml",
produces = {"application/xml;charset=UTF-8"})//设置返回数据的媒体类型为xml
public DemoObj getxml(DemoObj obj) {
return new DemoObj(obj.getId() + 1, obj.getName() + "yy");//直接返回对象,对象会自动转换为xml。
}
}

DemoRestController.java

Spring MVC基本配置

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

  静态资源映射:

 /**
* 映射路径和实际页面的位置
*
* @return
*/
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/classes/views/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
} /**
* 静态资源映射
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//addResourceHandler是对外暴露的访问路径,addResourceLocations是文件放置的目录。
registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/assets/");
}

静态资源映射

  拦截器配置:

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

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

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

 package com.wisely.highlight_springmvc4.interceptor;

 import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* 拦截器
*/
public class DemoInterceptor extends HandlerInterceptorAdapter {//集成HandlerInterceptorAdapter类来实现自定义拦截器 @Override
public boolean preHandle(HttpServletRequest request, //重写preHandle方法,在请求发生前执行
HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
} @Override
public void postHandle(HttpServletRequest request, //重写postHandle方法,在请求完成后执行
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
request.removeAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("本次请求处理时间为:" + new Long(endTime - startTime)+"ms");
request.setAttribute("handlingTime", endTime - startTime);
} }

DemoInterceptor.java(拦截器)

     /**
* 配置拦截器的Bean。
* @return
*/
@Bean
public DemoInterceptor demoInterceptor() {
return new DemoInterceptor();
} /**
* 重写addInterceptors方法,注册拦截器。
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {//
registry.addInterceptor(demoInterceptor());
}

配置拦截器

  @ControllerAdvice:

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

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

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

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

 package com.wisely.highlight_springmvc4.advice;

 import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView; /**
* 定制ControllerAdvice,处理全局异常
*/
@ControllerAdvice //@ControllerAdvice声明一个控制器建言,@ControllerAdvice组合了@Component注解,所以自动注册为Spring的Bean。
public class ExceptionHandlerAdvice { @ExceptionHandler(value = Exception.class)//@ExceptionHandler在此处定义全局处理,通过@ExceptionHandler的value属性可过滤拦截的条件,此处拦截所有的Exception。
public ModelAndView exception(Exception exception, WebRequest request) {
ModelAndView modelAndView = new ModelAndView("error");// error页面
modelAndView.addObject("errorMessage", exception.getMessage());
return modelAndView;
} @ModelAttribute //使用@ModelAttribute注解将键值对添加到全局,所有注解的@RequestMapping的方法可获得此键值对。
public void addAttributes(Model model) {
model.addAttribute("msg", "额外信息"); //
} @InitBinder //通过@InitBinder注解定制WebDataBinder
public void initBinder(WebDataBinder webDataBinder) {
webDataBinder.setDisallowedFields("id");//忽略掉request参数中的id参数
}
}

ExceptionHandlerAdvice.java

 package com.wisely.highlight_springmvc4.web.ch4_4;

 import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping; import com.wisely.highlight_springmvc4.domain.DemoObj; /**
* 异常控制器
*/
@Controller
public class AdviceController {
@RequestMapping("/advice")
public String getSomething(@ModelAttribute("msg") String msg,DemoObj obj){// throw new IllegalArgumentException("非常抱歉,参数有误/"+"来自@ModelAttribute:"+ msg);
} }

AdviceController.java

 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>@ControllerAdvice Demo</title>
</head>
<body>
${errorMessage}
</body>
</html>

error.jsp

Spring MVC其他配置

  ViewController:

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

 /**
* 集中设置页面转向
* 等同下列代码:
* @RequestMapping("/index")
* public String hello() {
* return "index";
* }
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("/index");
registry.addViewController("/toUpload").setViewName("/upload");
registry.addViewController("/converter").setViewName("/converter");
registry.addViewController("/sse").setViewName("/sse");
registry.addViewController("/async").setViewName("/async");
}

ViewController

  路径参数配置:

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

 /**
* 不忽略路径参数中"."后面的值
* @param configurer
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}

configurePathMatch

Spring MVC高级配置

  文件上传配置:

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>upload page</title> </head>
<body> <div class="upload">
<form action="upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/><br/>
<input type="submit" value="上传">
</form>
</div> </body>
</html>

upload.jsp

     /**
* 配置文件上传
* @return
*/
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}

配置

 package com.wisely.highlight_springmvc4.web.ch4_5;

 import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile; import java.io.File;
import java.io.IOException; @Controller
public class UploadController { @RequestMapping(value = "/upload",method = RequestMethod.POST)
public @ResponseBody String upload(MultipartFile file) {//使用MultipartFile file接收上传的文件 try {
//使用FileUtils.writeByteArrayToFile快速写文件到磁盘
FileUtils.writeByteArrayToFile(new File("e:/upload/"+file.getOriginalFilename()), file.getBytes());
return "ok";
} catch (IOException e) {
e.printStackTrace();
return "wrong";
} } }

UploadController

  自定义HttpMessageConverter:

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

 package com.wisely.highlight_springmvc4.messageconverter;

 import com.wisely.highlight_springmvc4.domain.DemoObj;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.StreamUtils; import java.io.IOException;
import java.nio.charset.Charset; public class MyMessageConverter extends AbstractHttpMessageConverter<DemoObj> {//继承AbstractHttpMessageConverter接口来实现自定义的HttpMessageConverter public MyMessageConverter() {
super(new MediaType("application", "x-wisely",Charset.forName("UTF-8")));//新建一个自定义的媒体类型application/x-wisely
} /**
* 重写readInternal方法,处理请求的数据。
* 代码表明我们处理由“-”隔开的数据,并转成DemoObj的对象。
*/
@Override
protected DemoObj readInternal(Class<? extends DemoObj> clazz,
HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
String temp = StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));
String[] tempArr = temp.split("-");
return new DemoObj(new Long(tempArr[0]), tempArr[1]);
} /**
* 表明本HttpMessageConverter只处理DemoObj这个类。
*/
@Override
protected boolean supports(Class<?> clazz) {
return DemoObj.class.isAssignableFrom(clazz);
} /**
* 重写writeInternal,处理如何输出数据到response。
* 此例中,我们在原样输出前面加上“hello:”。
*/
@Override
protected void writeInternal(DemoObj obj, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
String out = "hello:" + obj.getId() + "-"
+ obj.getName();
outputMessage.getBody().write(out.getBytes());
} }

MyMessageConverter.java

 /**
* 在Spring MVC里注册HttpMessageConverter有两种方法:
* 1:configureMessageConverters:重载会覆盖掉Spring MVC默认注册的多个HttpMessageConverter。
* 2:extendMessageConverters:仅添加一个自定义的HttpMessageConverter,不覆盖默认注册的HttpMessageConverter。
* @param converters
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
} @Bean
public MyMessageConverter converter() {
return new MyMessageConverter();
}

配置

 package com.wisely.highlight_springmvc4.web.ch4_5;

 import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import com.wisely.highlight_springmvc4.domain.DemoObj; @Controller
public class ConverterController { @RequestMapping(value = "/convert", produces = { "application/x-wisely" }) //指定返回的媒体类型为我们自定义的媒体类型application/x-wisely。
public @ResponseBody DemoObj convert(@RequestBody DemoObj demoObj) {
return demoObj;
}
}

ConverterController.java

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>HttpMessageConverter Demo</title>
</head>
<body>
<div id="resp"></div><input type="button" onclick="req();" value="请求"/>
<script src="assets/js/jquery.js" type="text/javascript"></script>
<script>
function req(){
$.ajax({
url: "convert",
data: "1-gaofei", //注意数据格式,使用“-”隔开
type:"POST",
contentType:"application/x-wisely", //contentType设置的媒体类型是我们自定义的application/x-wisely。
success: function(data){
$("#resp").html(data);
}
});
} </script>
</body>
</html>

converter.jsp

服务器端推送

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

 package com.wisely.highlight_springmvc4.web.ch4_5;

 import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import java.util.Random; @Controller
public class SseController {
/**
* 这里使用输出的媒体类型为text/event-stream,这是服务器端SSE的支持,本例演示每5秒钟像浏览器推送随机消息。
*
* @return
*/
@RequestMapping(value = "/push", produces = "text/event-stream")
public @ResponseBody
String push() {
Random r = new Random();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "data:Testing 1,2,3" + r.nextInt() + "\n\n";
} }

SseController.java

 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>SSE Demo</title> </head>
<body> <div id="msgFrompPush"></div>
<script type="text/javascript" src="<c:url value="assets/js/jquery.js" />"></script>
<script type="text/javascript"> if (!!window.EventSource) { //EventSource对象只有新式的浏览器才有(Chrome、Firefox等),EventSource是SSE的客户端。
var source = new EventSource('push');
s='';
source.addEventListener('message', function(e) {//添加SSE客户端监听,在此获得服务器端推送的消息
s+=e.data+"<br/>";
$("#msgFrompPush").html(s); }); source.addEventListener('open', function(e) {
console.log("连接打开.");
}, false); source.addEventListener('error', function(e) {
if (e.readyState == EventSource.CLOSED) {
console.log("连接关闭");
} else {
console.log(e.readyState);
}
}, false);
} else {
console.log("你的浏览器不支持SSE");
}
</script>
</body>
</html>

sse.jsp

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

 //注册Spring MVC的DispatcherServlet
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setAsyncSupported(true);//开启异步方法支持

配置

 package com.wisely.highlight_springmvc4.web.ch4_5;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult; import com.wisely.highlight_springmvc4.service.PushService; @Controller
public class AysncController {
@Autowired
PushService pushService; //定时任务,定时更新DeferredResult /**
* 返回给客户端DeferredResult
* @return
*/
@RequestMapping("/defer")
@ResponseBody
public DeferredResult<String> deferredCall() { //
return pushService.getAsyncUpdate();
} }

AysncController.java

 package com.wisely.highlight_springmvc4.service;

 import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.async.DeferredResult; @Service
public class PushService {
private DeferredResult<String> deferredResult; //在PushService里产生DeferredResult给控制器使用,通过@Scheduled注解的方式定时更新DeferredResult。 public DeferredResult<String> getAsyncUpdate() {
deferredResult = new DeferredResult<String>();
return deferredResult;
} @Scheduled(fixedDelay = 5000)
public void refresh() {
if (deferredResult != null) {
deferredResult.setResult(new Long(System.currentTimeMillis())
.toString());
}
}
}

PushService.java

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>servlet async support</title> </head>
<body> <script type="text/javascript" src="assets/js/jquery.js"></script>
<script type="text/javascript"> deferred();//页面打开就向后台发送请求。 function deferred(){
$.get('defer',function(data){
console.log(data); //在控制台输出服务端推送的数据。
deferred(); //一次请求完成后再向后台发送请求。
});
} </script>
</body>
</html>

async.jsp

Spring MVC的测试

 package com.wisely.highlight_springmvc4.service;

 import org.springframework.stereotype.Service;

 @Service
public class DemoService { public String saySomething(){
return "hello";
} }

DemoService.java

 package com.wisely.highlight_springmvc4.web.ch4_6;

 import com.wisely.highlight_springmvc4.MyMvcConfig;
import com.wisely.highlight_springmvc4.service.DemoService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {MyMvcConfig.class})
//@WebAppConfiguration注解在类上,用来声明加载的ApplicationContext是一个WebApplicationContext。
//它的属性指定的是Web资源的位置,默认为src/main/webapp,本例修改为src/main/resource。
@WebAppConfiguration("src/main/resources")
public class TestControllerIntegrationTests {
private MockMvc mockMvc; //MockMvc模拟MVC对象。 @Autowired
private DemoService demoService;//在测试用例中注入Spring的Bean。 @Autowired
WebApplicationContext wac; //注入WebApplicationContext。 @Autowired
MockHttpSession session; //注入模拟的Http Session。 @Autowired
MockHttpServletRequest request; //注入模拟的Http Request。 @Before //@Before注解表示在测试开始前进行的初始化工作。
public void setup() {
mockMvc =
MockMvcBuilders.webAppContextSetup(this.wac).build(); //通过MockMvcBuilders.webAppContextSetup(this.wac).build()初始化MockMvc。
} @Test
public void testNormalController() throws Exception{
mockMvc.perform(get("/normal")) //模拟向/normal进行get请求。
.andExpect(status().isOk())//预期控制返回状态为200。
.andExpect(view().name("page"))//预期view的名称为page。
.andExpect(forwardedUrl("/WEB-INF/classes/views/page.jsp"))//预期页面转向的真正路径为/WEB-INF/classes/views/page.jsp。
.andExpect(model().attribute("msg", demoService.saySomething()));//预期model里的值是demoService.saySomething()的返回值hello。 } @Test
public void testRestController() throws Exception{
mockMvc.perform(get("/testRest")) //模拟向/testRest进行get请求。
.andExpect(status().isOk())
.andExpect(content().contentType("text/plain;charset=UTF-8"))//预期返回值的媒体类型为text/plain;charset=UTF-8。
.andExpect(content().string(demoService.saySomething()));//预期返回值的内容是demoService.saySomething()的返回值hello。
} }

TestControllerIntegrationTests.java

 package com.wisely.highlight_springmvc4.web.ch4_6;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; import com.wisely.highlight_springmvc4.service.DemoService; //@Controller
public class NormalController {
@Autowired
DemoService demoService; @RequestMapping("/normal")
public String testPage(Model model){
model.addAttribute("msg", demoService.saySomething());
return "page";
} }

NormalController.java

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test page</title>
</head>
<body>
<pre>
Welcome to Spring MVC world
</pre>
</body>
</html>

page.jsp