GVKun编程网logo

【原创】遨游springmvc之HandlerAdapter(遨游客户端)

22

如果您对【原创】遨游springmvc之HandlerAdapter和遨游客户端感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解【原创】遨游springmvc之HandlerAdapter的各种

如果您对【原创】遨游springmvc之HandlerAdapter遨游客户端感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解【原创】遨游springmvc之HandlerAdapter的各种细节,并对遨游客户端进行深入的分析,此外还有关于HandlerAdapter与适配器模式以及SpringMVC核心接口、spring boot使用HandlerInterceptorAdapter+WebMvcConfigurerAdapter拦截、Spring MVC 中 HandlerInterceptorAdapter的使用、Spring MVC 中 HandlerInterceptorAdapter过滤器的使用的实用技巧。

本文目录一览:

【原创】遨游springmvc之HandlerAdapter(遨游客户端)

【原创】遨游springmvc之HandlerAdapter(遨游客户端)

1.前言

前一篇我们讲述了HandlerMapping,它返回了请求对应的controller实例和对应的处理方法,很多读者认为就完事了,其实不然。所以我们需要这一片来讲述一下HandlerAdapter的作用。HandlerAdapter是用来无线扩展DistapcherServlet的一个接口,通过适配器模式实现的,举个例子:读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。HandlerAdapter作用其实就跟这个读卡器差不多:它定义了如何处理请求的策略,将请求对应的参数和返回结果进行了一翻修饰,然后返回对应的视图ModelAndView。

HandlerAdapter定义了三个方法,如源码1.1:support方法的作用是判断处理适配器是不是支持该Handler。hanle方法,调用对应的Handler中适配到的方法,并返回一个ModelView,getLastModified返回请求最后被修改的时间,如果未知可以简单地返回-1

源码1.1

public interface HandlerAdapter {

	/**
	 * Given a handler instance, return whether or not this {@code HandlerAdapter}
	 * can support it. Typical HandlerAdapters will base the decision on the handler
	 * type. HandlerAdapters will usually only support one handler type each.
	 * <p>A typical implementation:
	 * <p>{@code
	 * return (handler instanceof MyHandler);
	 * }
	 * @param handler handler object to check
	 * @return whether or not this object can use the given handler
	 */
	boolean supports(Object handler);

	/**
	 * Use the given handler to handle this request.
	 * The workflow that is required may vary widely.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler handler to use. This object must have previously been passed
	 * to the {@code supports} method of this interface, which must have
	 * returned {@code true}.
	 * @throws Exception in case of errors
	 * @return ModelAndView object with the name of the view and the required
	 * model data, or {@code null} if the request has been handled directly
	 */
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	/**
	 * Same contract as for HttpServlet''s {@code getLastModified} method.
	 * Can simply return -1 if there''s no support in the handler class.
	 * @param request current HTTP request
	 * @param handler handler to use
	 * @return the lastModified value for the given handler
	 * @see javax.servlet.http.HttpServlet#getLastModified
	 * @see org.springframework.web.servlet.mvc.LastModified#getLastModified
	 */
	long getLastModified(HttpServletRequest request, Object handler);

}

spring3.2之前会默认给我们提供3个适配器:

HttpRequestHandlerAdapter

SimpleControllerHandlerAdapter  

AnnotationMethodHandlerAdaptor

缺省使用DefaultAnnotationHandlerMapping 来注册handler method和request的mapping关系。 AnnotationMethodHandlerAdapter来在实际调用handlermethod前对其参数进行处理。 

其中3.2版本之后变成了

RequestMappingHandlerAdapter

通常在我们的spring-mvc的配置文件中我们会看到一行配置<mvc:annotation-driven/>,它提供了RequestMappingHandlerAdapter(还有RequestMappingHandlerMapping),RequestMappingHandlerAdapter适配了RequestMappingInfoHandlerMapping

 

2.源代码解刨

RequestMappingHandlerAdapter在springmvc中其实算是一个非常重要的类

2.1.RequestMappingHandlerAdapter依赖图

 

2.2 源码解刨

初始化了一系列的参数解析器(参数解析器我们将在专门的一篇介绍,测出省略这些参数解析器的作用)

源码2.2.1

	private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

		// Annotation-based argument resolution
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ServletModelAttributeMethodProcessor(false));
		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new RequestHeaderMapMethodArgumentResolver());
		resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		// Type-based argument resolution
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());
		resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RedirectAttributesMethodArgumentResolver());
		resolvers.add(new ModelMethodProcessor());
		resolvers.add(new MapMethodProcessor());
		resolvers.add(new ErrorsMethodArgumentResolver());
		resolvers.add(new SessionStatusMethodArgumentResolver());
		resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

		// Custom arguments
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		// Catch-all
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		resolvers.add(new ServletModelAttributeMethodProcessor(true));

		return resolvers;
	}

 

初始化了一系列的返回值处理器(和参数解析器一样将在专门一篇介绍)

源码2.2.2

	private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();

		// Single-purpose return value types
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		handlers.add(new HttpHeadersReturnValueHandler());
		handlers.add(new CallableMethodReturnValueHandler());
		handlers.add(new DeferredResultMethodReturnValueHandler());
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		// Annotation-based return value types
		handlers.add(new ModelAttributeMethodProcessor(false));
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		handlers.add(new ViewNameMethodReturnValueHandler());
		handlers.add(new MapMethodProcessor());

		// Custom return value types
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

 

最终处理请求的执行方法

源码2.2.3

	/**
	 * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
	 * if view resolution is required.
	 * @since 4.2
	 * @see #createInvocableHandlerMethod(HandlerMethod)
	 */
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
            //获取WebDataBinder,实现类型绑定转换,相当于一个转化工具,将参数转换成服务端真正需要的类型
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

			if (asyncManager.hasConcurrentResult()) {
				Object result = asyncManager.getConcurrentResult();
				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
				asyncManager.clearConcurrentResult();
				if (logger.isDebugEnabled()) {
					logger.debug("Found concurrent result value [" + result + "]");
				}
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}
            //最终执行方法(包含参数解析,返回值包装)
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}

 

处理返回值,并将之包装

源码2.2.4

	/**
	 * Invokes the method and handles the return value through one of the
	 * configured {@link HandlerMethodReturnValueHandler}s.
	 * @param webRequest the current request
	 * @param mavContainer the ModelAndViewContainer for this request
	 * @param providedArgs "given" arguments matched by type (not resolved)
	 */
	public void invokeAndHandle(ServletWebRequest webRequest,
			ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(this.responseReason)) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		try {
            //包装结果
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
			}
			throw ex;
		}
	}
	public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        //获取包装过的参数,已经经过参数解析器处理
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			StringBuilder sb = new StringBuilder("Invoking [");
			sb.append(getBeanType().getSimpleName()).append(".");
			sb.append(getMethod().getName()).append("] method with arguments ");
			sb.append(Arrays.asList(args));
			logger.trace(sb.toString());
		}
        //doInvoke执行方法返回结果
		Object returnValue = doInvoke(args);
		if (logger.isTraceEnabled()) {
			logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
		}
		return returnValue;
	}

 

 

解析参数

源码2.2.6

	/**
	 * Get the method argument values for the current request.
	 */
	private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
        //循环springmvc初始化的参数解析器
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
			args[i] = resolveProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
            //获得匹配的参数解析器,得到处理后的参数结果
			if (this.argumentResolvers.supportsParameter(parameter)) {
				try {
					args[i] = this.argumentResolvers.resolveArgument(
							parameter, mavContainer, request, this.dataBinderFactory);
					continue;
				}
				catch (Exception ex) {
					if (logger.isDebugEnabled()) {
						logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
					}
					throw ex;
				}
			}
			if (args[i] == null) {
				String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
				throw new IllegalStateException(msg);
			}
		}
		return args;
	}

 

结果包装

源码2.2.7

@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
        //根据指定的HandlerMethodReturnValueHandler处理结果
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

	private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}

 

3.小结

HandlerAdapter是在DispatcherServlet正真处理事情的家伙,这一步处理了原理图中的4、5、6、7部分

期待下一章WebDataBinder

 

 

发现一个机智的导航

HandlerAdapter与适配器模式以及SpringMVC核心接口

HandlerAdapter与适配器模式以及SpringMVC核心接口

HandlerAdapter,顾名思义,是Handler的适配器,为了适配各种Handler,从而可以以统一的方式获取ModelAndView。

// request => Handler
public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request);
}

//常用:
// - BeanNameUrlHandlerMapping
// - RequestMappingHandlerMapping
// Handler => ModelAndView
public interface HandlerAdapter {
    boolean support(Object handler);
    ModelAndView handler(HttpServletRequest request, HttpServletResponse response, Object handler);
    long getLastModified(HttpServletRequest request, Object handler);
}

//常用:
// - HttpRequestHandlerAdapter
// - SimpleControllerHandlerAdapter
// - RequestMappingHandlerAdapter
// viewName => View
public interface ViewResolver {
    View resolveViewName(String viewName, Locale locale);
}
//常用:
// - InternalResourceViewResolver
// - FreeMarkerViewResolver
// - ThymeleafViewResolver
// 渲染
public interface View {
    void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response);
}

//常用:
// - InternalResourceView
// - FreeMarkerView
// - ThymeleafView

这段话说的很好:

springmvc通过HandlerMapping获取到可以处理的handler,这些handler的类型各不相同,对请求的预处理,参数获取都不相同,最简单的做法是根据不同的handler类型,做一个分支处理,不同的handler编写不同的代码。

这样的问题是很明显的,分支判断复杂,代码庞大,不符合单一职责原则。如果要增加一种handler类型,需要修改代码增加分支处理,违反了开闭原则。DispatcherServelt与多个handler发生了交互,违反迪米特法则。

而使用适配器模式,就可以很好的解决这个问题:

不直接对handler进行处理,而是将handler交给适配器HandlerAdapter去处理,这样DispatcherServlet交互的类就只剩下一个接口,HandlerAdapter,符合迪米特法则,尽可能少的与其他类发生交互;

将handler交给HandlerAdapter处理后,不同类型的handler被对应类型的HandlerAdapter处理,每个HandlerAdapter都只完成单一的handler处理,符合单一职责原则;

如果需要新增一个类型的handler,只需要新增对应类型的HandlerAdapter就可以处理,无需修改原有代码,符合开闭原则。

这样,不同的handler的不同处理方式,就在HandlerAdapter中得到了适配,对于DispatcherServlet来讲,只需要统一的调用HandlerAdapter的handle()方法就可以了,无需关注不同handler的处理细节。

设计模式的原则 1.单一职责原则 一个类只完成单一的功能,粒度越小越好

2.开闭原则 对扩展开放,对修改关闭。 一个类开发完成后,应当可以通过扩展的方式实现新的功能,而不是通过修改原有类来达到目的。

3.里氏替换原则 所有引用父类的地方都可以透明的使用其子类

4.依赖倒置原则 抽象不应该依赖于细节,细节应该依赖于抽象。 应该针对接口或抽象类编程,而不是针对具体的实现编程

5.接口隔离原则 接口的划分应该更细致,使用多个功能单一的接口,而不是将所有的功能放到一个接口中实现,客户端不应该去依赖它所不需要的接口方法。

6.迪米特法则 一个类应尽可能少的与其他类进行交互。降低耦合。

参考链接:http://blog.sina.com.cn/s/blog_e660c25b01030kbj.html

spring boot使用HandlerInterceptorAdapter+WebMvcConfigurerAdapter拦截

spring boot使用HandlerInterceptorAdapter+WebMvcConfigurerAdapter拦截

  最近在做考试系统,是个微信小程序,页面都是前端那里做好的,数据都是调的后端接口,自然后端就使用到了拦截器。时间有点紧,这篇只是大概写个结构,以后再补。

1.HandlerInterceptorAdapter

preHandle 业务处理器在处理业务之前被调用,预处理。
postHandle 在业务处理之后调用,后处理,
afterCompletion 在DispatcherServlet完全处理完请求后被调用,可用于清理资源。也可以根据ex是否为null来判断是否发生了异常。
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * @author suwan
 */
public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String url = request.getRequestURI().toString();
            System.out.println("preHandle:" + url);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        String url = request.getRequestURI().toString();
        System.out.println("postHandle:" + url);
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion:" + ex);
        super.afterCompletion(request, response, handler, ex);
    }
}

 

2.WebMvcConfigurerAdapter添加拦截器

  addInterceptors 需要一个实现HandlerInterceptor接口的拦截器实例

 addPathPatterns 用于设置拦截器的过滤路径规则

 excludePathPatterns:用于设置不需要拦截的过滤规则

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @author suwan
 */
@Configuration
    public class InterceptorConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor()).addPathPatterns("/login/**").excludePathPatterns("/hello/**");
    }
    @Bean
    public LoginInterceptor loginInterceptor(){return new LoginInterceptor();}
}

3.测试设置拦截器的路径

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author suwan
 */
@RestController
@RequestMapping("/login")
public class UserController {

    @GetMapping("/test")
    public void test(){
        System.out.println("loginTest");
    }
}

浏览器请求:http://localhost:8080/login/test

输出:
preHandle:/login/test
loginTest
postHandle:/login/test
afterCompletion:null

4.测试未设置拦截的路径

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author suwan
 * @date 2019/6/13
 */
@RestController
@RequestMapping("/hello")
public class HelloController {
    @GetMapping("/test")
    public  void  test(){
        System.out.println("helloTest");
    }
}

浏览器请求:http://localhost:8080/hello/test

输出:
helloTest

 

Spring MVC 中 HandlerInterceptorAdapter的使用

Spring MVC 中 HandlerInterceptorAdapter的使用

一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可以实现Bean预处理、后处理。 
Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度。 

Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:


public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
        throws Exception {  
        return true;  
    }  
    public void postHandle(  
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)  
            throws Exception {  
    }  
    public void afterCompletion(  
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  
            throws Exception {  
            
    }


分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面) 
在preHandle中,可以进行编码、安全控制等处理; 
在postHandle中,有机会修改ModelAndView; 
在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。 

如果基于xml配置使用Spring MVC,
可以利用SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping进行Url映射(相当于struts的path映射)和拦截请求(注入interceptors),
如果基于注解使用Spring MVC,可以使用DefaultAnnotationHandlerMapping注入interceptors。
注意无论基于xml还是基于注解,HandlerMapping bean都是需要在xml中配置的。 

一个demo: 
在这个例子中,我们假设UserController中的注册操作只在9:00-12:00开放,那么就可以使用拦截器实现这个功能。 


public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {  
    private int openingTime;  
    private int closingTime;  
    private String mappingURL;//利用正则映射到需要拦截的路径  
    public void setOpeningTime(int openingTime) {  
        this.openingTime = openingTime;  
    }  
    public void setClosingTime(int closingTime) {  
        this.closingTime = closingTime;  
    }  
    public void setMappingURL(String mappingURL) {  
        this.mappingURL = mappingURL;  
    }  
    @Override  
    public boolean preHandle(HttpServletRequest request,  
            HttpServletResponse response, Object handler) throws Exception {  
        String url=request.getRequestURL().toString();  
        if(mappingURL==null || url.matches(mappingURL)){  
            Calendar c=Calendar.getInstance();  
            c.setTime(new Date());  
            int now=c.get(Calendar.HOUR_OF_DAY);  
            if(now<openingTime || now>closingTime){  
                request.setAttribute("msg", "注册开放时间:9:00-12:00");  
                request.getRequestDispatcher("/msg.jsp").forward(request, response);  
                return false;  
            }  
            return true;  
        }  
        return true;  
    }  
}


xml配置: 


<bean id="timeBasedAccessInterceptor" >  
    <property name="openingTime" value="9" />  
    <property name="closingTime" value="12" />  
    <property name="mappingURL" value=".*/user\.do\?action=reg.*" />  
</bean>  
<bean >  
    <property name="interceptors">  
        <list>  
            <ref bean="timeBasedAccessInterceptor"/>  
        </list>  
    </property>  
</bean>


这里我们定义了一个mappingURL属性,实现利用正则表达式对url进行匹配,从而更细粒度的进行拦截。当然如果不定义mappingURL,则默认拦截所有对Controller的请求。 

UserController: 

@Controller  
@RequestMapping("/user.do")  
public class UserController{  
    @Autowired  
    private UserService userService;  
    @RequestMapping(params="action=reg")  
    public ModelAndView reg(Users user) throws Exception {  
        userService.addUser(user);  
        return new ModelAndView("profile","user",user);  
    }  
    // other option ...  
}


这个Controller相当于Struts的DispatchAction 



Spring MVC 中 HandlerInterceptorAdapter过滤器的使用

Spring MVC 中 HandlerInterceptorAdapter过滤器的使用

一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可以实现Bean预处理、后处理。 
Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度。 

Spring为我们提供了org.springframework.web.servlet.handler.handlerinterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:

 

一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可以实现Bean预处理、后处理。 
Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度。 

Spring为我们提供了org.springframework.web.servlet.handler.handlerinterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:

 

 

public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)    
        throws Exception {    
        return true;    
    }    
    public void postHandle(    
            HttpServletRequest request,Object handler,ModelAndView modelAndView)    
            throws Exception {    
    }    
    public void afterCompletion(    
            HttpServletRequest request,Exception ex)    
            throws Exception {    
    }  

 

 

 

分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面) 
在preHandle中,可以进行编码、安全控制等处理; 
在postHandle中,有机会修改ModelAndView; 
在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。 

如果基于xml配置使用Spring MVC,
可以利用SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping进行Url映射(相当于struts的path映射)和拦截请求(注入interceptors),
如果基于注解使用Spring MVC,可以使用DefaultAnnotationHandlerMapping注入interceptors。
注意无论基于xml还是基于注解,HandlerMapping bean都是需要在xml中配置的。 

一个demo: 
在这个例子中,我们假设UserController中的注册操作只在9:00-12:00开放,那么就可以使用拦截器实现这个功能。 

 

 

 

public class TimeBasedAccessInterceptor extends handlerinterceptorAdapter {    
    private int openingTime;    
    private int closingTime;    
    private String mappingURL;//利用正则映射到需要拦截的路径    
    public void setopeningTime(int openingTime) {    
        this.openingTime = openingTime;    
    }    
    public void setClosingTime(int closingTime) {    
        this.closingTime = closingTime;    
    }    
    public void setMappingURL(String mappingURL) {    
        this.mappingURL = mappingURL;    
    }    
    @Override    
    public boolean preHandle(HttpServletRequest request,Object handler) throws Exception {    
        String url=request.getRequestURL().toString();    
        if(mappingURL==null || url.matches(mappingURL)){    
            Calendar c=Calendar.getInstance();    
            c.setTime(new Date());    
            int Now=c.get(Calendar.HOUR_OF_DAY);    
            if(Now<openingTime || Now>closingTime){    
                request.setAttribute("msg","注册开放时间:9:00-12:00");    
                request.getRequestdispatcher("/msg.jsp").forward(request,response);    
                return false;    
            }    
            return true;    
        }    
        return true;    
    }    
}    

xml配置:

 

<bean id="timeBasedAccessInterceptor">    
    <property name="openingTime" value="9" />    
    <property name="closingTime" value="12" />    
    <property name="mappingURL" value=".*/user\.do\?action=reg.*" />    
</bean>    
<bean>    
    <property name="interceptors">    
        <list>    
            <ref bean="timeBasedAccessInterceptor"/>    
        </list>    
    </property>    
</bean>  


这里我们定义了一个mappingURL属性,实现利用正则表达式对url进行匹配,从而更细粒度的进行拦截。当然如果不定义mappingURL,则默认拦截所有对Controller的请求。 

 

UserController:

 

@Controller    
@RequestMapping("/user.do")    
public class UserController{    
    @Autowired    
    private UserService userService;    
    @RequestMapping(params="action=reg")    
    public ModelAndView reg(Users user) throws Exception {    
        userService.addUser(user);    
        return new ModelAndView("profile","user",user);    
    }    
    // other option ...    
}    

这个Controller相当于Struts的dispatchAction 

你也可以配置多个拦截器,每个拦截器进行不同的分工. 

 

 

我们项目里面的例子

 


 

 

package com.shishuo.studio.filter;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handlerinterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.shishuo.studio.entity.User;
import com.shishuo.studio.exception.CookieNotFoundException;
import com.shishuo.studio.service.UserService;
import com.shishuo.studio.util.CookieUtils;

/**
 * @author Herbert 拦截器的实现
 */
@Component
public class AuthInterceptor implements handlerinterceptor {

	@Autowired
	private UserService userService;

	public void afterCompletion(HttpServletRequest request,HttpServletResponse respone,Object object,Exception exception)
			throws Exception {
		// Todo Auto-generated method stub

	}

	public void postHandle(HttpServletRequest request,ModelAndView modelAndView) throws Exception {
	}

	public boolean preHandle(HttpServletRequest request,Object handler) throws Exception {
		try {
			User user = userService.getUserById(request);
			if (userService.checkAuth(user,request,response)) {
				return true;
			} else {
				response.sendRedirect("/user/login.htm");
				return false;
			}
		} catch (Exception e) {
			e.printstacktrace();
		}
		response.sendRedirect("/user/login.htm");
		return false;
	}
}

 

package com.shishuo.studio.filter;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handlerinterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.shishuo.studio.service.CategoryService;

/**
 * @author Herbert
 * 
 */
@Component
public class GlobalInterceptor implements handlerinterceptor {

	@Autowired
	private CategoryService categoryService;

	@Override
	public boolean preHandle(HttpServletRequest request,Object handler) throws Exception {

		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request,ModelAndView modelAndView) throws Exception {
		if (null == modelAndView) {
			return;
		}

		// 系统配置参数
		modelAndView.addobject("categoryList",categoryService.getCategorylistofdisplayByFatherId(0));
	}

	@Override
	public void afterCompletion(HttpServletRequest request,Exception ex)
			throws Exception {
		// Todo Auto-generated method stub

	}

}

 

package com.shishuo.studio.filter;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handlerinterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.shishuo.studio.entity.User;
import com.shishuo.studio.entity.vo.StudioVo;
import com.shishuo.studio.exception.StudioNotFoundException;
import com.shishuo.studio.service.StudioService;
import com.shishuo.studio.service.UserService;

/**
 * @author Herbert
 * 
 */
@Component
public class StudioInterceptor implements handlerinterceptor {

	@Autowired
	private StudioService studioService;

	@Autowired
	private UserService userService;

	@Override
	public boolean preHandle(HttpServletRequest request,Object handler) throws Exception {
		User user = userService.getUserById(request);
		try {
			StudioVo studio = studioService.getStudioById(user.getUserId());
			return true;
		} catch (StudioNotFoundException e) {
			response.sendRedirect("/auth/teacher/studio.htm");
			return false;
		}
	}

	@Override
	public void postHandle(HttpServletRequest request,ModelAndView modelAndView) throws Exception {

	}

	@Override
	public void afterCompletion(HttpServletRequest request,Exception ex)
			throws Exception {
	}
}

 

 

 

 

 

今天的关于【原创】遨游springmvc之HandlerAdapter遨游客户端的分享已经结束,谢谢您的关注,如果想了解更多关于HandlerAdapter与适配器模式以及SpringMVC核心接口、spring boot使用HandlerInterceptorAdapter+WebMvcConfigurerAdapter拦截、Spring MVC 中 HandlerInterceptorAdapter的使用、Spring MVC 中 HandlerInterceptorAdapter过滤器的使用的相关知识,请在本站进行查询。

本文标签: