1. 程式人生 > 實用技巧 >SpringMVC無xml檔案之靜態資源,攔截器配置和@EnableWebMvc

SpringMVC無xml檔案之靜態資源,攔截器配置和@EnableWebMvc

目錄

1 SpringMVC配置

1.1 原專案參考

一下變更大都是在此無xml基礎上整合的
先看原無xml專案地

1.2 靜態資源對映

程式的靜態檔案(js、css等)需要直接訪問,這時我們可以在配置裡重寫addResourceHandler方法,類似於在springmvc的地方配置靜態資源放行

由於優雅REST風格的資源URL不希望帶.html.do 等字尾.由於早期的Spring MVC不能很好地處理靜態資源,所以在web.xml中配置DispatcherServlet的請求對映,往往使用 *.do 、 *.xhtml等方式。這就決定了請求URL必須是一個帶字尾的URL,而無法採用真正的REST風格的URL
如果將DispatcherServlet請求對映配置為/,則Spring MVC將捕獲Web容器所有的請求,包括靜態資源的請求,Spring MVC會將它們當成一個普通請求處理,因此找不到對應處理器將導致錯誤,所以需要放行靜態資源

@Configuration
@EnableWebMvc
@ComponentScan("cn.jzh")
public class MyMvcConfig implements WebMvcConfigurer {

    /**
     * 此處相當於web專案中 springmvc.xml檔案
     * @return
     */
    @Bean
    public InternalResourceViewResolver viewResolver (){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return viewResolver;
    }

    /**
     * 靜態資源放行
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //ResourceHandler對外暴露路徑  ResourceLocations:資源位置
        registry.addResourceHandler("/jquery/**").addResourceLocations("classpath*:/WEB-INF/jquery/");
    }
}

注意:
classpathclasspath*spring載入資源的時候是不同的:

  • classpath:只能載入找到的第一個資原始檔
  • classpath*:能載入多個路徑下的資原始檔

1.3 攔截器配置

攔截器實現一個請求處理前和處理後進行相關業務處理,類似ServletFilter

可以是實現HandlerInterceptor介面或者繼承HandlerInterceptorAdapter來實現自定義的攔截器

package cn.jzh.config;

import cn.jzh.controller.JspController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * SpringMVC攔截器配置
 */
@Component
public class MyMvcInterceptor extends HandlerInterceptorAdapter {
    private  Logger log = LoggerFactory.getLogger(getClass());

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("請求處理器前開始執行。。。。。。");
        long startTimes = System.currentTimeMillis();
        request.setAttribute("startTimes",startTimes);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("請求處理器後開始執行。。。。。。");
        long endTimes = System.currentTimeMillis();
        long startTimes = (Long)request.getAttribute("startTimes");
        request.removeAttribute("startTimes");
        long handTimes = endTimes - startTimes;
        log.info("本次請求處理時間:{}",handTimes);
    }
}

配置到springmvc相關配置中去

package cn.jzh.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@Configuration
@EnableWebMvc
@ComponentScan("cn.jzh")
public class MyMvcConfig implements WebMvcConfigurer {

    @Autowired
    private MyMvcInterceptor interceptor;

    /**
     * 此處相當於web專案中 springmvc.xml檔案
     * @return
     */
    @Bean
    public InternalResourceViewResolver viewResolver (){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setViewClass(JstlView.class);
        return viewResolver;
    }
    /**
     * 攔截器部分
     */

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor);
    }
}

1.4 其他配置

檢視層配置ViewController
在專案開發過程中,經常會涉及頁面跳轉問題,而且這個頁面跳轉沒有任何業務邏輯過程,只是單純的路由過程 ( 點選一個按鈕跳轉到一個頁面 )
常規寫法如下:

@RequestMapping("/toview")
 public String view(){
    return "view";
 }

如果專案中有很多類似的無業務邏輯跳轉過程,那樣會有很多類似的程式碼

那麼如何可以簡單編寫,這種程式碼?
Spring MVC中提供了一個方法,可以把類似程式碼統一管理,減少類似程式碼的書寫(根據專案要求,或者程式碼規範,不一定非要統一管理頁面跳轉,有時會把相同業務邏輯的程式碼放在一個類中)
在實現WebMvcConfigurer的MyMvcConfig類中過載addViewControllers

  @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/toview").setViewName("/view");
        //新增更多
    }

以上程式碼等效於第一種寫法

1.5 @EnableWebMvc

1.5.1 對spring boot專案影響

假如是pringboot專案,pringboot預設可以訪問以下路徑檔案:

  • classpath:/static
  • classpath:/public
  • classpath:/resources
  • classpath:/META-INF/resources
    當使用了@EnableWebMvc時,預設的靜態資源訪問無效了因為預設情況下mvc使用的配置是WebMvcAutoConfiguration,加入該配置變成了WebMvcConfigurationSupport

1.5.2 @EnableWebMvc、WebMvcConfigurationSupport、WebMvcConfigurationAdapter

@EnableWebMvc=WebMvcConfigurationSupport,使用了@EnableWebMvc註解等於擴充套件了WebMvcConfigurationSupport但是沒有重寫任何方法
看原始碼如下:
同時可以看下@EnableWebMvc原始碼

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

其中@Import(DelegatingWebMvcConfiguration.class)為該註解的核心,

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
 
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
       this.configurers.addWebMvcConfigurers(configurers);
    }
}

可以看到,該類DelegatingWebMvcConfiguration也是WebMvcConfigurationSupport的子類,但是相對而言,添加了自己的擴充套件配置,同時從setConfigurers可以看到,所有WebMvcConfigurer的子類也會被新增到配置中

各個組合搭配情況:

  • @EnableWebMvc+extends WebMvcConfigurationAdapter,在擴充套件的類中重寫父類的方法即可,這種方式會遮蔽springbootWebMvcAutoConfiguration中的設定
  • @EnableWebMvc+extends WebMvcConfigurationSupport 只會使用@EnableWebMvc
  • extends WebMvcConfigurationSupport,在擴充套件的類中重寫父類的方法即可,這種方式會遮蔽springboot@WebMvcAutoConfiguration中的設定
  • extends WebMvcConfigurationAdapter,在擴充套件的類中重寫父類的方法即可,這種方式依舊使用springbootWebMvcAutoConfiguration中的設定
  • @EnableWebMvc+implements WebMvcConfigurer的方式可以實現springbootWebMvcAutoConfiguration中的設定的配置加上自己的配置

springboot2.x中,WebMvcConfigurationAdapter已經過時,通過實現介面WebMvcConfigurer可以替代原有規則
在預設情況下,springboot是啟用WebMvcAutoConfiguration,這點可以在spring-boot-autoconfigure.jar/META-INF/spring.factories中看到

org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration
但是開啟WebMvcAutoConfiguration可以看到

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration{
}

其中@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)說明,當沒有WebMvcConfigurationSupport對應的bean時,才會使用該配置,所以當我們使用繼承WebMvcConfigurationSupport的方式類擴充套件mvc時,原有的配置則無效。

其中WebMvcConfigurerAdapter,也是WebMvcConfigurer的子類,
這就是為什麼我們使用@EnableWebMvc+WebMvcConfigurer的方式可以實現EnableWebMvc的配置加上自己的配置了