spring中的攔截器和配置的演變

Interceptor類

SpringMVC 中的Interceptor 攔截請求是通過HandlerInterceptor 來實現的。在SpringMVC 中定義一個Interceptor 非常簡單,主要有兩種方式,第一種方式是要定義的Interceptor類要實現了Spring 的HandlerInterceptor 接口,或者是這個類繼承實現了HandlerInterceptor 接口的類,比如Spring 已經提供的實現了HandlerInterceptor 接口的抽象類HandlerInterceptorAdapter ;第二種方式是實現Spring的WebRequestInterceptor接口,或者是繼承實現了WebRequestInterceptor的類。

(1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顧名思義,該方法將在請求處理之前進行調用。SpringMVC 中的Interceptor 是鏈式的調用的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續進行下去。該方法的返回值是布爾值Boolean類型的,當它返回爲false 時,表示請求結束,後續的Interceptor 和Controller 都不會再執行;當返回值爲true 時就會繼續調用下一個Interceptor 的preHandle 方法,如果已經是最後一個Interceptor 的時候就會是調用當前請求的Controller 方法。

(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解釋我們知道這個方法包括後面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值爲true 時才能被調用。postHandle 方法,顧名思義就是在當前請求進行處理之後,也就是Controller 方法調用之後執行,但是它會在DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對Controller 處理之後的ModelAndView 對象進行操作。postHandle 方法被調用的方向跟preHandle 是相反的,也就是說先聲明的Interceptor 的postHandle 方法反而會後執行,這和Struts2 裏面的Interceptor 的執行過程有點類型。Struts2 裏面的Interceptor 的執行過程也是鏈式的,只是在Struts2 裏面需要手動調用ActionInvocation 的invoke 方法來觸發對下一個Interceptor 或者是Action 的調用,然後每一個Interceptor 中在invoke 方法調用之前的內容都是按照聲明順序執行的,而invoke 方法之後的內容就是反向的。

(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值爲true 時纔會執行。顧名思義,該方法將在整個請求結束之後,也就是在DispatcherServlet 渲染了對應的視圖之後執行。這個方法的主要作用是用於進行資源清理工作的。

攔截器是一個Spring的組件,歸Spring管理,配置在Spring文件中,因此能使用Spring裏的任何資源、對象,例如 Service對象、數據源、事務管理等,通過IoC注入到攔截器即可 而 Filter 是被 Server(like Tomcat) 調用 Filter 不能夠使用 Spring 容器資源,Filter 是在 Servlet 規範中定義的,是 Servlet 容器支持的。Filter 定義在 web.xml 中,Filter在只在 Servlet 前後起作用。Filters 通常將 請求和響應(request/response) 當做黑盒子,Filter 通常不考慮servlet 的實現。

Interceptor使用

interceptor 的執行順序大致爲:

  1. 請求到達 DispatcherServlet
  2. DispatcherServlet 發送至 Interceptor ,執行 preHandle
  3. 請求達到 Controller
  4. 請求結束後,postHandle 執行

Spring 中主要通過 HandlerInterceptor 接口來實現請求的攔截,實現 HandlerInterceptor 接口需要實現下面三個方法:

  • preHandle() – 在handler執行之前,返回 boolean 值,true 表示繼續執行,false 爲停止執行並返回。

  • postHandle() – 在handler執行之後, 可以在返回之前對返回的結果進行修改

  • afterCompletion() – 在請求完全結束後調用,可以用來統計請求耗時等等

    //登陸攔截器

    public class LoginInterceptor implements HandlerInterceptor {

    /**

    • 在控制器執行之前完成業務邏輯操作

    • 方法的返回值決定邏輯是否繼續執行, true,表示繼續執行, false, 表示不再繼續執行。
      */
      public boolean preHandle(HttpServletRequest request,
      HttpServletResponse response, Object handler) throws Exception {

      // 判斷當前用戶是否已經登陸
      HttpSession session = request.getSession();
      User loginUser = (User)session.getAttribute(“loginUser”);

      if ( loginUser == null ) {
      String path = session.getServletContext().getContextPath();
      response.sendRedirect(path + “/login”);
      return false;
      } else {
      return true;
      }
      }

    /**

    • 在Controller方法調用之後執行,但是它會在DispatcherServlet進行視圖返回渲染之前被調用
      */
      public void postHandle(HttpServletRequest request,
      HttpServletResponse response, Object handler,
      ModelAndView modelAndView) throws Exception {
      // TODO Auto-generated method stub
      }

    /**

    • 在完成視圖渲染之後,執行此方法。
      */
      public void afterCompletion(HttpServletRequest request,
      HttpServletResponse response, Object handler, Exception ex)
      throws Exception {
      }
      }

日誌攔截器

/**
* 用於記錄日誌,在每次請求之前,打印請求的地址和參數,方便調試
 */

public class LogInterceptor implements HandlerInterceptor {

	/** 記錄日誌 */
	private static Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
	
	@Override
	public boolean preHandle(HttpServletRequest request,HttpServletResponse response, 
								Object arg2) throws Exception {
		StringBuilder sb = new StringBuilder();
		String uri = request.getRequestURI();
		sb.append("---------------> demo uri:").append(uri).append(" - ");

		Enumeration<String> enums2 = request.getParameterNames();
		while (enums2.hasMoreElements()) {
			String key = enums2.nextElement();
			sb.append("\"").append(key).append("\":").append(
					request.getParameter(key)).append(", ");
		}
		logger.info(sb.toString());
		return true;
	}	

	@Override
	public void postHandle(HttpServletRequest request,HttpServletResponse response, 
								Object arg2, ModelAndView arg3)throws Exception {
	
	}

	@Override
	public void afterCompletion(HttpServletRequest request,HttpServletResponse response, 
								Object arg2, Exception arg3) throws Exception {
	
	}
}

授權訪問Url攔截器

	//此處採用繼承抽象類的方式,不是實現Interceptor方式
public class AuthInterceptor extends HandlerInterceptorAdapter {
	@Autowired
	private PermissionService permissionService;
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
										Object handler) throws Exception {
		// 獲取用戶的請求地址
		String uri = request.getRequestURI();
		String path = request.getSession().getServletContext().getContextPath();
		
		// 判斷當前路徑是否需要進行權限驗證。
		// 查詢所有需要驗證的路徑集合
		List<Permission> permissions = permissionService.queryAll();
		Set<String> uriSet = new HashSet<String>();
		for ( Permission permission : permissions ) {
			if ( permission.getUrl() != null && !"".equals(permission.getUrl()) ) {
				uriSet.add(path + permission.getUrl());
			}
		}
		
		if ( uriSet.contains(uri) ) {
			// 權限驗證
			// 判斷當前用戶是否擁有對應的權限
			Set<String> authUriSet = (Set<String>)request.getSession()
														.getAttribute("authUriSet");
			if ( authUriSet.contains(uri) ) {
				return true;
			} else {
				response.sendRedirect(path + "/error");
				return false;
			}
		} else {
			return true;
		}
	}
}

在spring中註冊使用

需要把上面的這些攔截器給註冊到spring中使用,在springMVC.xml中配置如下:

<mvc:interceptors>
<mvc:interceptor>
    <mvc:mapping path="/**" />
    <bean class="site.gaoyisheng.filter.LoginHandlerInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptor>
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <mvc:exclude-mapping path="/login" />
        <mvc:exclude-mapping path="/bootstrap/**" />
        <mvc:exclude-mapping path="/css/**" />
        <mvc:exclude-mapping path="/fonts/**" />
        <mvc:exclude-mapping path="/img/**" />
        <mvc:exclude-mapping path="/jquery/**" />
        <mvc:exclude-mapping path="/layer/**" />
        <mvc:exclude-mapping path="/script/**" />
        <mvc:exclude-mapping path="/ztree/**" />
        //該攔截器不對靜態資源進行攔截
        <bean class="com.scorpios.atcrowdfunding.web.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

在springboot中註冊使用:

/**
 * 擴展SpringMVC
 * SpringBoot2使用的Spring5,因此將WebMvcConfigurerAdapter改爲WebMvcConfigurer
 * 使用WebMvcConfigurer擴展SpringMVC好處既保留了SpringBoot的自動配置,又能用到我們自己的配置
 */
 //@EnableWebMvc //如果我們需要全面接管SpringBoot中的SpringMVC配置則開啓此註解,
                 //開啓後,SpringMVC的自動配置將會失效。
 @Configuration
 public class WebConfig implements WebMvcConfigurer {
     @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //設置對“/”的請求映射到index
         //如果沒有數據返回到頁面,沒有必要用控制器方法對請求進行映射
        registry.addViewController("/").setViewName("index");
    }
   /**
     * 跨域支持
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .maxAge(3600 * 24);
    }

  @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry) {
         // 默認訪問"/"到"index.html"
         registry.addResourceHandler("/index.html")
                .addResourceLocations("/");
          // 資源處理 添加靜態資源處理器 ,自定義靜態資源映射目錄
          registry.addResourceHandler("/**")
                  .addResourceLocations("classpath:/static/");
           // 配置映射資源,訪問這個路徑直接映射到文件 攔截器
    	registry.addResourceHandler("/image/**").addResourceLocations("file:E:/upload/image/");
      }
     //註冊攔截器
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
        //SpringMVC下,攔截器的註冊需要排除對靜態資源的攔截(*.css,*.js)
        //SpringBoot已經做好了靜態資源的映射
         registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
     }
 }

WebMvcConfigurer接口(spring 4.0時)

定義回調方法以自定義通過啓用的Spring MVC的基於Java的配置

@EnableWebMvc-帶註釋的配置類可以實現此接口以使其被回調,並有機會自定義默認配置

主要方法:

void addFormatters(FormatterRegistry var1);

void configureMessageConverters(List<HttpMessageConverter<?>> var1);

void extendMessageConverters(List<HttpMessageConverter<?>> var1);

Validator getValidator();

void configureContentNegotiation(ContentNegotiationConfigurer var1);

void configureAsyncSupport(AsyncSupportConfigurer var1);

void configurePathMatch(PathMatchConfigurer var1);

void addArgumentResolvers(List<HandlerMethodArgumentResolver> var1);

void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> var1);

void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> var1);

void addInterceptors(InterceptorRegistry var1);

MessageCodesResolver getMessageCodesResolver();

void addViewControllers(ViewControllerRegistry var1);

void configureViewResolvers(ViewResolverRegistry var1);

void addResourceHandlers(ResourceHandlerRegistry var1);

void configureDefaultServletHandling(DefaultServletHandlerConfigurer var1);

實現類:WebMvcConfigurerAdapter(5.0之後被棄用了,不推薦使用但是還可以用)

其實這個WebMvcConfigurerAdepter只是WebMvcConfigurer接口的一個實現類。是一個抽象類,裏面是和父接口一樣的方法。所以在spring5.0之後去除了WebMvcConfigurerAdapter這個類。當我們使用時直接實現WebMvcConfigurer接口即可,然後實現其中的方法。

Defines callback methods to customize the Java-based configuration for Spring MVC enabled via @EnableWebMvc.從5.0開始,WebMvcConfigurer具有默認方法(由Java 8基準實現),可以直接實現而無需此適配器

實現類 WebMvcConfigurerComposite (5.0後棄用)

主要方法如下:

 // 
   public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
          if (configurers != null) {
              this.delegates.addAll(configurers);
          }

      }
	// 註冊自定義轉化器  註冊自定義的Formatter和Convert
    public void addFormatters(FormatterRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addFormatters(registry);
        }

    }
    //// 攔截器配置,添加springmvc攔截器
    public void addInterceptors(InterceptorRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addInterceptors(registry);
        }

    }
	//// 添加自定義視圖控制器 ,視圖跳轉控制 而無需書寫controller
    public void addViewControllers(ViewControllerRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addViewControllers(registry);
        }

    }
	// 該方法的參數ViewResolverRegistry 是一個註冊器,用來註冊你想自定義的視圖解析器等。
    public void configureViewResolvers(ViewResolverRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.configureViewResolvers(registry);
        }

    }
	// // 資源處理 添加靜態資源處理器 ,自定義靜態資源映射目錄
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        Iterator var2 = this.delegates.iterator();

        while(var2.hasNext()) {
            WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
            delegate.addResourceHandlers(registry);
        }

    }

使用WebMvcConfigurer:

/**
 * Spring 5 (或者Spring Boot 2.x)版本配置Web應用程序示例
 */
@Configuration
public class MvcConfigure implements WebMvcConfigurer{
 	@Override
    public void addFormatters(FormatterRegistry formatterRegistry) {
        // 註冊自定義轉化器  註冊自定義的Formatter和Convert
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> list) {
		// 配置消息轉換器。重載會覆蓋默認註冊的HttpMessageConverter
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> list) {
		// 配置消息轉換器。僅添加一個自定義的HttpMessageConverter
    }

    @Override
    public Validator getValidator() {
    	// 
        return null;
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {
		//  內容協商配置
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {
		// 
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {
    	// 配置路由請求規則
 		configurer.setUseSuffixPatternMatch(false);
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
		// 添加自定義方法參數處理器
    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
		// 添加自定義返回結果處理器
    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
		// 配置異常轉換器
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
		// 攔截器配置,添加springmvc攔截器
      // 如果未配置了靜態資源映射可以把排除攔截中的路徑寫靜態資源路徑用.excludePathPatterns("/static/**")排除靜態資源路徑,
		registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/js/**","/css/**","/images/**","/index.html","/","/user/login","/static/**");
		// addPathPatterns("/**")對所有請求都攔截,但是排除了/user/login和/login請求的攔截
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
    	// 
        return null;
    }
	/**
	 * 以前要訪問一個頁面需要先創建個Controller控制類,再寫方法跳轉到頁面
     * 在這裏配置後就不需要那麼麻煩了,直接訪問http://localhost:8080/toLogin就跳轉到login.jsp頁面了
     **/
    @Override
    public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
		// 添加自定義視圖控制器 ,視圖跳轉控制 而無需書寫controller
		viewControllerRegistry.addViewController("/toLogin").setViewName("login")
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
		// 從方法名稱我們就能看出這個方法是用來配置視圖解析器的,該方法的參數ViewResolverRegistry 是一個註冊器,用來註冊你想自定義的視圖解析器等。
        	
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    	// 資源處理 添加靜態資源處理器 ,自定義靜態資源映射目錄
      	// 自定義後在註冊攔截器時不需要使用 排除路徑的方法
		registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("classpath:/public/")
                .addResourceLocations("classpath:/resources/");
         // 配置映射資源,訪問這個路徑直接映射到文件 攔截器
         registry.addResourceHandler("/image/**").addResourceLocations("file:E:/upload/image/");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
		// 默認靜態資源處理器 默認seevlet處理
    }
  
}

使用 WebMvcConfigurationSupport

WebMvcConfigurationSupport是一個提供了以Java編程方式來配置Web應用程序的配置主類,

public class WebMvcConfigurationSupport implements ApplicationContextAware,ServletContextAware{}



1、springboot默認可以訪問以下路徑文件(見ResourceProperties):
    classpath:/static
    classpath:/public
    classpath:/resources
    classpath:/META-INF/resources
   當使用了@EnableWebMvc時,默認的靜態資源訪問無效了因爲默認情況下mvc使用的配置是WebMvcAutoConfiguration,加入該配置變成了WebMvcConfigurationSupport
2、@EnableWebMvc、WebMvcConfigurationSupport、WebMvcConfigurationAdapter
    @EnableWebMvc=WebMvcConfigurationSupport,使用了@EnableWebMvc註解等於擴展了WebMvcConfigurationSupport但是沒有重寫任何方法
    @EnableWebMvc+extends WebMvcConfigurationAdapter,在擴展的類中重寫父類的方法即可,這種方式會屏蔽springboot的WebMvcAutoConfiguration中的設置
    @EnableWebMvc+extends WebMvcConfigurationSupport 只會使用@EnableWebMvc
    extends WebMvcConfigurationSupport,在擴展的類中重寫父類的方法即可,這種方式會屏蔽springboot的@WebMvcAutoConfiguration中的設置
    extends WebMvcConfigurationAdapter,在擴展的類中重寫父類的方法即可,這種方式依舊使用springboot的WebMvcAutoConfiguration中的設置
    在springboot2.x中,WebMvcConfigurationAdapter已經過時,通過實現接口WebMvcConfigurer可以替代原有規則


public class MyWebMvcConfiguer extends WebMvcConfigurationSupport {
    public MyWebMvcConfiguer() {
        super();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        super.setApplicationContext(applicationContext);
        //  設置ApplicationContext
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        super.setServletContext(servletContext);
        // 設置域
    }
	/**
     * 攔截器配置
     *
     * @param registry 註冊類
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
    	registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/js/**","/css/**","/images/**","/index.html","/","/user/login","/static/**");
		// addPathPatterns("/**")對所有請求都攔截,但是排除了/user/login和/login請求的攔截
        super.addInterceptors(registry);
        
    }
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        super.configurePathMatch(configurer);
        //配置路徑匹配選項。
    }

    @Override
    protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        super.configureContentNegotiation(configurer);
        // 配置內容協商
    }

    @Override
    protected void addViewControllers(ViewControllerRegistry registry) {
        super.addViewControllers(registry);
        // 添加視圖控制器
    }

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        super.addResourceHandlers(registry);
        // 添加用於處理靜態資源的資源處理程序
    }

    @Override
    protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        super.configureDefaultServletHandling(configurer);
        // 配置“默認” Servlet處理
    }

    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        super.addArgumentResolvers(argumentResolvers);
        // 除了默認註冊的方法外,還添加要使用的方法
    }

    @Override
    protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        super.addReturnValueHandlers(returnValueHandlers);
        // 除了默認註冊的自定義內容外,還添加自定義內容
    }

    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        // 
    }

    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.extendMessageConverters(converters);
        // 以添加HttpMessageConverters 要與RequestMappingHandlerAdapter和 一起使用的自定義ExceptionHandlerExceptionResolver
    }

    @Override
    protected void addFormatters(FormatterRegistry registry) {
    	// 註冊自定義轉化器  註冊自定義的Formatter和Convert
        super.addFormatters(registry);
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        super.configureAsyncSupport(configurer);
        // 配置異步請求處理選項
    }


    @Override
    protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        super.configureHandlerExceptionResolvers(exceptionResolvers);
        // 配置HandlerExceptionResolvers要使用的列表
    }

    @Override
    protected void configureViewResolvers(ViewResolverRegistry registry) {
        super.configureViewResolvers(registry);
        // 配置視圖分辨率
    }
}

使用這些時仍需謹慎,按需使用。比如有些不必要的配置就不要去配置了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章