【Spring】Spring MVC高級技術


前言

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

高級技術

Spring MVC配置的替換方案

自定義DispatcherServlet配置

在第五章我們曾編寫過如下代碼。


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,如下所示。


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

添加其他Servlet和Filter

AbstractAnnotationConfigDispatcherServletInitializer會創建DispatcherServletContextLoaderListener,當需要添加其他ServletFilter時,只需要創建一個新的初始化器即可,最簡單的方式是實現WebApplicationInitializer接口。


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如下。


  <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的初始化參數,如下所示。


<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請求。

配置multipart解析器

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

  • 使用StandardServletMultipartResolver

使用Java配置如下


@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,以及如何處理異常和控制器通知,最后分析如何在重定向時傳遞數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM