Spring实战4之SpringMVC高级篇

来源:互联网 发布:sybase数据库有哪些 编辑:程序博客网 时间:2024/05/29 15:19

一、DispatcherServlet个性化配置
继承AbstractAnnotationConfigDispatcherServletInitializer时,同时还有许多其他方法可以重写从而可以实现更多的配置。
例如customizeRegistration()方法,在注册了DispatcherServlet之后,就会调用customizeRegistration()方法,并根据servlet的注册返回值传送ServletRegistration.Dynamic,通过对customizeRegistration()的重写,就可以对DispatcherServlet进行额外的配置。其中ServletRegistration.Dynamic作为入参,你可以做很多事情,比如调用setLoadOnStartup()来设置加载时优先级,调用setInitParameter()来设置初始化参数,调用setMultipartConfig()来设置Servlet3.0的多路支持

/** * Spring MVC如何处理多个请求和文件上传 * 设置了多路支持的上传文件临时存储路径为:/tmp/spittr/uploads。 */@Overrideprotected void customizeRegistration(Dynamic registration) {    registration.setMultipartConfig(        new MultipartConfigElement("/tmp/spittr/uploads"));}

二、添加额外的Servlet和Filter
1.注册额外的Servlet、Filter或者Listener
(1)最简单的方法就是实现Spring的WebApplicationInitializer接口。
例如,下面的代码展示了如何通过实现WebApplicationInitializer接口的方式来注册一个Servlet:

public  class MyServletInitializer implements WebApplicationInitializer {            @Override            public void onStartup(ServletContext servletContext) throws ServletException {                // 定义Servlet                Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);                // 映射Servlet                myServlet.addMapping("/custom/**");            }        }

(2)使用AbstractAnnotationConfigDispatcherServletInitializer注册Filter并将其映射到DispatcherServlet

@Overrideprotected Filter[] getServletFilters() {    return new Filter[] { new MyFilter() };}

三、使用web.xml声明DispatcherServlet
1.典型的web.xml文件,其中对DispatcherServlet和ContextLoaderListener进行了声明:

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">    <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>/WEB-INF/spring/root-context.xml</param-value>    </context-param>    <listener>        <!-- 注册ContextLoaderListener -->        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>    <servlet>        <servlet-name>appServlet</servlet-name>        <!-- 注册DispatcherServlet -->        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <load-on-startup>1</load-on-startup>    </servlet>    <!-- DispatcherServlet映射 -->    <servlet-mapping>        <servlet-name>appServlet</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping></web-app>

2.通过设置contextConfigLocation初始化参数的方式指定DispatcherServlet配置文件的位置,例如,下面的DispatcherServlet配置就会从/WEB-INF/spring/appServlet/servlet-context.xml文件中加载:

<servlet>    <servlet-name>appServlet</servlet-name>    <!-- 注册DispatcherServlet -->    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    <init-param>        <param-name>contextConfigLocation</param-name>        <param-value>            /WEB-INF/spring/appServlet/servlet-context.xml        </param-value>    </init-param>    <load-on-startup>1</load-on-startup></servlet>

3.基于Java的配置方式,通过设置DispatcherServlet的contextClass参数和初始化参数来实现
为了使用基于Java的配置,需要通知DispatcherServlet和ContextLoaderListener去使用AnnotationConfigWebApplicationContext,该类是WebApplicationContext接口的实现类,它可以对Java配置类进行加载。

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">    <!-- 使用Java配置 -->    <context-param>        <param-name>contextClass</param-name>        <param-value>            org.springframework.web.context.support.AnnotationConfigWebApplicationContext        </param-value>    </context-param>    <!-- 指定所使用的Java配置类 -->    <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>spittr.config.RootConfig</param-value>    </context-param>    <listener>        <listener-class>            org.springframework.web.context.ContextLoaderListener        </listener-class>    </listener>    <servlet>        <servlet-name>appServlet</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <!-- 使用Java配置 -->        <init-param>            <param-name>contextClass</param-name>            <param-value>                org.springframework.web.context.support.AnnotationConfigWebApplicationContext            </param-value>        </init-param>        <!-- 指定DispatcherServlet的配置类 -->        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value>                spittr.config.WebConfigConfig            </param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>appServlet</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping></web-app>

四、处理multipart表单数据
1.配置multipart解析器
从Spring3.1开始,Spring提供了两种MultipartResolver实现类供选择:
CommonsMultipartResolver:使用Jakarta Commons FileUpload来解析multipart请求;
StandardServletMultipartResolver:依靠Servlet 3.0支持来解析(Spring 3.1及以上,第一选择);

设置StandardServletMultipartResolver的,但是它的设置不是在Spring配置中进行的,而是在Servlet配置中。起码要配置一下存放临时文件的位置,进一步来讲,还要将multipart配置为DispatcherServlet的一部分。
(1)继承自WebMvcConfigurerAdapter的servlet初始化类中配置的DispatcherServlet,那么就可以在servlet注册时通过调用setMultipartConfig()方法来配置multipart详情。比如:

DispatcherServlet ds = new DispatcherServlet();Dynamic registration = context.addServlet("appServlet", ds);registration.addMapping("/");registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));

(2)继承自AbstractAnnotationConfigDispatcherServletInitializer或者AbstractDispatcherServletInitializer的servlet初始化类进行的配置,没有创建DispatcherServlet的实例或者使用servlet上下文对其进行注册。因此就没有直接的引用供Dynamicservlet注册来使用。重写customizeRegistration()方法来进行配置:

@Overrideprotected void customizeRegistration(Dynamic registration) {    registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));}

(3)MultipartConfigElement的唯一参数设置了上传文件时临时文件的存放位置。也可以进行其他一些设置:
文件上传的最大值(byte),默认没有限制;
所有multipart请求的文件最大值(byte),不管有多少个请求,默认无限制;
直接上传文件(不需存储到临时目录)的最大值(byte),默认是0,也就是所有的文件都要写入硬盘;
例如,你想设置文件大小不超过2MB,所有请求的总和不超过4MB,并且所有文件都要写入硬盘,那么就可以这样设置:

@Overrideprotected void customizeRegistration(Dynamic registration) {    registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads", 2097152, 4194304, 0));}

(4)传统的web.xml的方式来设置的DispatcherServlet,那么就需要使用多个元素,其默认值和MultipartConfigElement相同,并且是必填项:

<servlet>    <servlet-name>appServlet</servlet-name>    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    <load-on-startup>1</load-on-startup>    <multipart-config>        <location>/tmp/spittr/uploads</location>        <max-file-size>2097152</max-file-size>        <max-request-size>4194304</max-request-size>    </multipart-config></servlet>

(5)配置Jakarta Commons FileUpload解析器CommonsMultipartResolver
这里设置了文件的最大大小为2MB,最大的内存中大小为0,即每个上传文件都会直接写入磁盘的。但是它是无法设置multipart请求总的文件大小的。

@Beanpublic MultipartResolver multipartResolver() throws IOException {    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();    multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));    multipartResolver.setMaxUploadSize(2097152);    multipartResolver.setMaxInMemorySize(0);    return multipartResolver;}

2.处理multipart请求
页面

<form method="POST" th:object="${spitter}" *enctype="multipart/form-data"*>    <label>Profile Picture</label>:    <input type="file" name="profilePicture" accept="image/jpeg,image/png,image/gif" /><br/>    <input type="submit" value="Register" /> </form>

(1) @RequestPart注解一个控制器参数获取到上传的文件

/** * 当注册表单提交时,请求部分的数据就会赋予到profilePicture属性中, * 如果用户没有选中一个文件,那么该数组就会是一个空值(不是null) */@RequestMapping(value = "/register", method = RequestMethod.POST)public String processRegistration(@RequestPart("profilePicture") byte[] profilePicture, @Valid Spitter spitter, Errors errors) {}

(2)使用MultipartFile接口保存上传文件
MultipartFile提供获取上传文件的方法,同时提供了很多其他方法,比如原始文件名称、大小和内容类型等。另外还提供了一个InputStream可以将文件数据作为数据流读取。
另外,MultipartFile还提供了一个方便的transferTo()方法帮助你将上传文件写入到文件系统

public interface MultipartFile {    String getName();    String getOriginalFilename();    String getContentType();    boolean isEmpty();    long getSize();    byte[] getBytes() throws IOException;    InputStream getInputStream() throws IOException;    void transferTo(File dest) throws IOException;}

(3)将文件保存到Amazon S3管理文件
下面的代码可以将上传的图像保存到Amazon S3:

private void saveImage(MultipartFile image) throws ImageUploadException {    try {        //设置Amazon Web Service (AWS)认证,你需要提供S3的密钥和私钥,这些在注册S3服务时Amazon都会给你的        AWSCredentials awsCredentials = new AWSCredentials(s3AccessKey, s2SecretKey);        // 配置S3服务        S3Service s3 = new RestS3Service(awsCredentials);        // 创建S3 bucket对象        S3Bucket bucket = s3.getBucket("spittrImages");        S3Object imageObject = new S3Object(image.getOriginalFilename());        // 设置图像数据        imageObject.setDataInputStream(image.getInputStream());        imageObject.setContentLength(image.getSize());        imageObject.setContentType(image.getContentType());        AccessControlList acl = new AccessControlList();        // 设置权限        acl.setOwner(bucket.getOwner());        acl.grantPermission(GroupGrantee.ALL_USERS, Permission.PERMISSION_READ);        imageObject.setAcl(acl);        // 保存图片        s3.putObject(bucket, imageObject);    } catch (Exception e) {        throw new ImageUploadException("Unable to save image", e);    }

(4)在Servlet 3.0的容器上,Spring MVC也可以将javax.servlet.http.Part作为控制器的入参

@RequestMapping(value = "/register", method = RequestMethod.POST)    public String processRegistration(@RequestPart("profilePicture") Part profilePicture, @Valid Spitter spitter,Errors errors) {   }

Part接口

public interface Part {    public InputStream getInputStream() throws IOException;    public String getContentType();    public String getName();    public String getSubmittedFileName();    public long getSize();    public void write(String fileName) throws IOException;    public void delete() throws IOException;    public String getHeader(String name);    public Collection<String> getHeaders(String name);    public Collection<String> getHeaderNames();}

五、异常处理
(1)@ResponseStatus来将其映射到404。

@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Spittle Not Found")public class SpittleNotFoundException extends Exception {}

(2)编写异常处理方法
@ExceptionHandler注解的方法在同一个控制器里是通用的,即无论SpittleController的哪一个方法抛出DuplicateSpittleException异常,handleDuplicateSpittle()方法都可以对其进行处理,而不再需要在每一个出现异常的地方进行捕获

@ExceptionHandler(DuplicateSpittleException.class)public String handleDuplicateSpittle() {    return "error/duplicate";}

(3)@ControllerAdvice控制器增强类
使用@ControllerAdvice控制器增强类,不论哪一个controller抛出DuplicateSpittleException,都会调用handleDuplicateSpittle()方法来处理。

// 声明控制器增强@ControllerAdvicepublic class AppWideExceptionHandler {    // 定义异常处理方法    @ExceptionHandler(DuplicateSpittleException.class)    public String handleDuplicateSpittle() {        return "error/duplicate";    }    @ExceptionHandler(SpittleNotFoundException.class)    public String handleSpittleNotFound() {        return "error/duplicate";    }}

六、在redirect请求中携带数据进行传递
跳转的路径:
return “redirect:/spitter/” + spitter.getUsername();

两种方法用来从重定向的方法中获取数据:
(1)将数据转换为路径参数或者查询参数
使用URL模版重定向:使用路径参数和查询参数传递数据比较简单,它只适用于传递简单值,比如String和数字,不能传递比较复杂的东西。

由于model中的spitterId属性并没有映射到URL中的占位符,它会自动作为查询参数。
如果username是habuma,spitterId是42,那么返回的重定向路径将是/spitter/habuma?spitterId=42。

@RequestMapping(value="/register", method=POST)public String processRegistration(Spitter spitter, Model model) {    spitterRepository.save(spitter);    model.addAttribute("username", spitter.getUsername());    model.addAttribute("spitterId", spitter.getId());    return "redirect:/spitter/{username}";}

(2)在flash属性中发送数据
使用flash属性
在重定向中传送一个Spitter对象。
在重定向之前,所有的flash属性都会拷贝到session中,在重定向之后,存储在session中的flash属性会从session中移出到model中。然后处理重定向请求的方法就可以使用Spitter对象了

@RequestMapping(value="/register", method=POST)public String processRegistration(Spitter spitter, RedirectAttributes model) {    spitterRepository.save(spitter);    model.addAttribute("username", spitter.getUsername());    model.addFlashAttribute("spitter", spitter);    return "redirect:/spitter/{username}";}
原创粉丝点击