【Spring】Spring MVC高級技術

前言

前面學習了簡單的Spring Web知識,接着學習更高階的Web技術。java

高級技術

Spring MVC配置的替換方案

自定義DispatcherServlet配置

在第五章咱們曾編寫過以下代碼。web

public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

能夠看到SpitterWebinitializer實現了AbstractAnnotationConfigDispatcherServletInitializer抽象類,並重寫了三個必須的方法,實際上還可對更多方法進行重寫,以便實現額外的配置,如對customizeRegistration方法進行重寫,該方法是AbstractDispatcherServletInitializer的方法,無實際的方法體。當AbstractAnnotationConfigDispatcherServletInitializerDispatcherServlet註冊到Servlet容器中後,就會調用customizeRegistration方法,並將Servlet註冊後獲得的Registration.Dynamic傳入。可經過重寫customizeRegistration方法設置MultipartConfigElement,以下所示。spring

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

添加其餘Servlet和Filter

AbstractAnnotationConfigDispatcherServletInitializer會建立DispatcherServletContextLoaderListener,當須要添加其餘ServletFilter時,只須要建立一個新的初始化器便可,最簡單的方式是實現WebApplicationInitializer接口。spring-mvc

import org.springframework.web.WebApplicationInitializer;

import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;

public class MyServletInitializer implements WebApplicationInitializer {

    public void onStartup(ServletContext servletContext) throws ServletException {
        Dynamic servlet = servletContext.addServlet("myServlet", MyServlet.class);
        servlet.addMapping("/custom/**");

        FilterRegistration.Dynamic filter = servletContext.addFilter("myFilter", MyFilter.class);
        filter.addMappingForUrlPatterns(null, false, "/custom/*");

    }
}

在xml文件中聲明DispatcherServlet

對基本的Spring MVC應用而言,須要配置DispatcherServletContextLoaderListenerweb.xml配置以下。安全

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</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>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  
</web-app>

能夠看到在web.xml中配置了DispatcherServletContextLoaderListener,而且定義了上下文,該上下文會被ContextLoaderListener加載,從中讀取bean。也可指定DispatcherServlet的應用上下文並完成加載,配置web.xml以下。mvc

<servlet>
    <servlet-name>appServlet</servlet-name>
    <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>

上面使用DispatcherServletContextLoaderListener加載各自的上下文,但實際狀況中,基於Java的配置更爲通用,此時只須要配置DispatcherServletContextLoaderListener使用AnnotationConfigWebApplicationContext,這樣它即可加載Java配置類,而非使用xml,可設置contextClassDispathcerServlet的初始化參數,以下所示。app

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  </context-param>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>ch7.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>
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
    
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>ch7.WebConfig</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形式數據

處理multipart數據主要用於處理文件上傳操做。須要配置multipart解析器讀取multipart請求。ide

配置multipart解析器

DispatcherServlet並未實現任何解析multipart請求數據功能,它只是將任務委託給MultipartResolver策略接口實現,經過該實現解析multipart請求內容,Spring中內置了CommonsMultipartResolverStandardServletMultipartResolver兩個解析器。學習

  • 使用StandardServletMultipartResolver

使用Java配置以下url

@Override

protected void customizeRegistration(Dynamic registration) {
    registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads", 2 * 1024 * 1024, 4 * 1024 * 1024, 0));
}

使用xml配置以下,在servlet標籤中配置multipart-config

<multipart-config>
        <location>/tmp/spittr/uploads</location>
        <max-file-size>2 * 1024 * 1024</max-file-size>
        <max-request-size>4 * 1024 * 1024</max-request-size>
      </multipart-config>
  • 使用CommonsMultipartResolver

使用Java配置以下

@Bean
    public MultipartResolver multipartResolver() throws IOException {
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        commonsMultipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));

        return commonsMultipartResolver;
    }

處理multipart請求

可在控制器的方法參數上添加@RequestPart註解,以下所示。

@RequestMapping(value="/register", method=POST)
public String processRegistration(
    @RequestPart("profilePicture") byte[] profilePicture,
    @Valid Spittr spitter,
    Errors errors) {
    profilePicture.transferTo(new File("/data/spittr" + profilePicture.getOriginalFilename()));
}

處理異常

Spring提供了多種方式將異常轉換爲響應

  • 特定的異常將會自動映射爲指定的HTTP狀態碼。
  • 異常上能夠添加@ResponseStatus註解,從而將其映射爲某個HTTP狀態碼。
  • 在方法上可添加@ExceptionHandler註解,使其用來處理異常。

將異常映射爲HTTP狀態碼

Spring異常與狀態碼對應關係以下。

編寫異常處理方法

可在請求中直接使用try/catch處理異常,其與正常Java方法中的try/catch相同,同時,也可編寫處理器來處理特定異常,當出現特定異常時,將有處理器委託方法進行處理。

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

爲控制器添加通知

控制器通知是任意帶有@ControllerAdvice註解的類,該類包含以下類型的一個或多個方法。

  • @ExceptionHandler註解標註的方法。
  • @InitBinder註解標註的方法。
  • @ModelAttribute註解標註的方法。

上面方法會運用到整個應用程序全部控制器中帶有@RequestMapping註解的方法上。

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

通過上述配置,任意控制器方法拋出了DuplicateSpittleException異常,都會調用這個duplicateSpittleHandler方法處理異常。

跨重定向請求傳遞數據

對於重定向而言,若須要從發起重定向的方法傳遞數據給處理重定向方法中,有以下兩種方法

  • 使用URL模版以路徑變量和/或查詢參數形式傳遞數據。
  • 經過flash屬性發送數據。

經過URL模版進行重定向

如前面講到的經過redirect:/spitter/{username}進行重定向,該方法會直接根據username肯定url,並不是十分安全的作法,可以使用進行以下處理。

model.addAttribute("username", spitter.getUsername());
return "redirect:/spitter/{username}";

當須要傳遞參數,如id時,可進行以下處理。

model.addAttribute("username", spitter.getUsername());
model.addAttribute("spitterId", spitter.getId());
return "redirect:/spitter/{username}";

usernameleesfid123456。這樣訪問的url/spitter/leesf?spitterId=123456。這種方法只能傳遞簡單的值,沒法發送更爲複雜的值,此時須要使用flash屬性。

使用flash屬性

經過RedirectAttributes設置flash屬性,這樣可直接傳遞對象。

@ReuqestMapping(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}";
}

這樣spitter對象也被傳遞到重定向頁面中,可直接訪問spitter對象。

總結

本篇博文講解了如何配置DispatcherServletContextLoaderListener,以及如何處理異常和控制器通知,最後分析如何在重定向時傳遞數據。

相關文章
相關標籤/搜索