`
IXHONG
  • 浏览: 450144 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

(5)SpringMVC-http请求处理路线

阅读更多




核心架构的具体流程步骤如下:

1、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

2、 DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;

3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);

5、 ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;

6、 View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;

7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

 

用我的话来概括如下:

    由HandlerMapping得到HandlerExecutionChain(其包含Object handler,interceptors),handler就是Controller实例,接着由handler得到HandlerAdapter(ha),最后由ha.handle(..)得到ModelAndView。注意,在执行目标方法之前,会升序调用interceptors,之后又会倒序调用interceptors。那么,HandlerAdapter是个什么东西呢,我在DispatcherServlet.properties里可以看到默认配了3种adapter,其实还有一种adapter没加进配置文件,如果你了解springmvc的发展史和解决mvc的思路,你就会明白为什么有多种实现方式了。最初springmvc采用的是实现Controller接口的方式,javase 1.5开始,springmvc采用了更方便的注解方式来标明Controller类,这两种方式对应的adapter分别是SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter,所以我们可以重点关注AnnotationMethodHandlerAdapter。


下面来好好谈谈DispatcherServlet的部分代码

    我们注意到该类中定义了一些well-known常量,呵呵,众所周知的常量

 

	public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";

	public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";

	public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";

	/**
	 * Well-known name for the HandlerMapping object in the bean factory for this namespace.
	 * Only used when "detectAllHandlerMappings" is turned off.
	 * @see #setDetectAllHandlerMappings
	 */
	public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";

	public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";

	public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";

	public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";

	public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";

	public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";

	private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
 我们在配置文件中给相关组件bean命名时,可以保持和只写常量值一致,毕竟别人的命名是相当标准的,不过,这从逻辑上不是必须的,你甚至可以不声明bean的id,因为该类提供了如下变量
/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
	private boolean detectAllHandlerMappings = true;

	/** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */
	private boolean detectAllHandlerAdapters = true;

	/** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
	private boolean detectAllHandlerExceptionResolvers = true;

	/** Detect all ViewResolvers or just expect "viewResolver" bean? */
	private boolean detectAllViewResolvers = true;

	/** Perform cleanup of request attributes after include request? */
	private boolean cleanupAfterInclude = true;

	/** MultipartResolver used by this servlet */
	private MultipartResolver multipartResolver;

	/** LocaleResolver used by this servlet */
	private LocaleResolver localeResolver;

	/** ThemeResolver used by this servlet */
	private ThemeResolver themeResolver;

	/** List of HandlerMappings used by this servlet */
	private List<HandlerMapping> handlerMappings;

	/** List of HandlerAdapters used by this servlet */
	private List<HandlerAdapter> handlerAdapters;

	/** List of HandlerExceptionResolvers used by this servlet */
	private List<HandlerExceptionResolver> handlerExceptionResolvers;

	/** RequestToViewNameTranslator used by this servlet */
	private RequestToViewNameTranslator viewNameTranslator;

	/** FlashMapManager used by this servlet */
	private FlashMapManager flashMapManager;

	/** List of ViewResolvers used by this servlet */
	private List<ViewResolver> viewResolvers;
初始化方法中,执行了组件的初始化

 

	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}
 我们拿出initHandlerMappings(context)来瞅瞅

 

/**
	 * Initialize the HandlerMappings used by this class.
	 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
	 * we default to BeanNameUrlHandlerMapping.
	 */
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				OrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}
 通过该方法我们可以知道,当detectAllHandlerMappings为true时,直接到mvc容器及其父容器中寻找所有类型为HandlerMapping的bean,而detectAllHandlerMappings默认恰为true,因此我们完全可以不声明bean的id。当detectAllHandlerMappings为false时(可通过在web.xml里配置servlet参数来改变默认值),则在mvc容器寻找名为"handlerMapping"的bean。如以上两种情况都没找到bean,将会实例化DispatcherServlet.properties中配置的bean。

    doService方法里有这么一段

 

// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}
 这是用于对页面有include的情况做的请求数据快照处理。

下面进入重点doDispatch方法

 

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
			// multipart包装处理,我们常用CommonsMultipartResolver
                                processedRequest = checkMultipart(request);
				multipartRequestParsed = processedRequest != request;

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) { // 404
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.  // 代码省略
				
                                // 调用目标方法前,顺序执行拦截器
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				try {
					// Actually invoke the handler.
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, mv);
				// 调用目标方法后逆序执行拦截器
                                mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}

 

HandlerExecutionChain 

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (getInterceptors() != null) {
			for (int i = 0; i < getInterceptors().length; i++) {
				HandlerInterceptor interceptor = getInterceptors()[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

	/**
	 * Apply postHandle methods of registered interceptors.
	 */
	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
		if (getInterceptors() == null) {
			return;
		}
		for (int i = getInterceptors().length - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = getInterceptors()[i];
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}

	/**
	 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
	 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
	 * has successfully completed and returned true.
	 */
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws Exception {

		if (getInterceptors() == null) {
			return;
		}
		for (int i = this.interceptorIndex; i >= 0; i--) {
			HandlerInterceptor interceptor = getInterceptors()[i];
			try {
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
 

HandlerInterceptor

public interface HandlerInterceptor {

	boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
	    throws Exception;

	void postHandle(
			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception;

	void afterCompletion(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception;

}

 

 自定义

public class ExceptionHandler implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        request.setAttribute("exception",ex.getMessage());
        ModelAndView mav = new ModelAndView();
        mav.setViewName("/error");
        return mav;
    }
}
  

 

  • 大小: 50.4 KB
0
1
分享到:
评论

相关推荐

    Java SpringMVC 学习路线.pdf

    综上所述,SpringMVC的学习路线涵盖了从基础到高级的多个方面,包括框架使用、代码实践、架构理解、组件整合、参数绑定、注解使用、异常处理、数据交互、Restful实现以及拦截器开发等多个知识点,为Java后台开发者...

    SpringMVC学习笔记,学习路线,详细

    本文将深入探讨 SpringMVC 的学习路线,包括基础配置、控制器定义、请求处理以及注解的使用。 首先,让我们从【标题】和【描述】中的内容开始。SpringMVC 学习笔记涵盖了从基础到高级的多个方面,如源码分析、实例...

    Maven+ssm(spring+springmvc+mybatis)+mysql

    接着,学习SpringMVC的生命周期和请求处理流程。再深入MyBatis,理解其映射文件和动态SQL的用法。最后,通过Maven管理项目,了解其生命周期和插件机制。实践中,可以创建一个简单的CRUD应用,逐步将SSM框架整合并...

    Java基于SSM(Spring+SpringMVC+MyBatis)网约车用户服务平台.zip

    SpringMVC是Spring框架的一个模块,专门用于处理Web层的请求。它通过DispatcherServlet接收HTTP请求,然后将请求分发给处理器(Controller),控制器再调用服务层方法完成业务逻辑,最后返回视图(View)给客户端。...

    毕业论文jsp1957出行路线规划系统springmvc.doc

    采用当前主流的B/S(Browser/Server)架构,即用户通过浏览器端进行操作,服务器端处理请求并返回数据,这种架构使得系统具有良好的跨平台性和易用性。JAVA编程语言作为后端开发语言,以其强类型、面向对象的特性,...

    javaWeb,Spring,springMvc,MyBatis物流系统

    5. 物流路线规划:可能使用第三方API进行物流路线的优化计算。 6. 系统监控:通过Spring Actuator等工具监控系统的性能和健康状况。 这些技术的结合使得物流系统能够高效、可靠地处理大量数据,提供实时的货物追踪...

    Java基于SSM(Spring+SpringMVC+MyBatis)环卫管理平台.zip

    SpringMVC通过DispatcherServlet接收请求,Controller处理请求,Model存储数据,View负责渲染视图,整个流程清晰明了。 MyBatis是一个优秀的持久层框架,它简化了数据库操作,通过XML或注解的方式将SQL语句与Java...

    毕业设计基于SSM框架+MySQL实现的城市公共交通路线推荐系统源码+数据库

    在城市公共交通路线推荐系统中,SpringMVC处理用户请求,调用业务逻辑,并返回响应结果。 - **MyBatis**:是一个持久层框架,简化了数据库操作。它允许开发者直接编写SQL语句,通过XML或注解的方式将SQL与Java代码...

    基于java的-133-jspSSM368的药品销售配送网站系统-LW-源码.zip

    SpringMVC提供了处理HTTP请求、路由请求到相应的控制器、数据绑定、验证、视图解析等功能。在这个药品销售配送系统中,SpringMVC将处理用户请求,调用服务层进行业务处理,然后返回响应数据。 4. **目录结构** - ...

    Java学习线路资料,包括JavaWeb、Spring、SpringMVC、Mybatis、SpringBoot......

    在这一阶段,你会学习如何处理HTTP请求,如何使用JSP进行视图层开发,以及如何通过Servlet进行业务逻辑处理。 Spring框架是JavaEE开发中的核心组件,它简化了依赖注入(Dependency Injection)和面向切面编程...

    基于SSM的旅游管理系统.zip

    Spring负责依赖注入和事务管理,SpringMVC处理HTTP请求,MyBatis则作为持久层框架,用于与数据库交互。 5. **微信小程序**:可能表示该系统与微信小程序进行了集成,允许用户通过微信小程序访问和使用旅游服务,提升...

    基于ssm+jsp的快递管理系统.zip

    在系统中,SpringMVC处理HTTP请求,调用相应的控制器方法,然后将结果传递给视图层进行渲染。 **3. MyBatis框架** MyBatis是一个持久层框架,它简化了数据库操作,使得开发者可以直接编写SQL语句,避免了传统的JDBC...

    基于SSM的游乐园智慧向导系统和微信小程序源码.zip

    首先,SSM是Java Web开发中常见的三层架构模式,由Spring框架负责依赖注入(DI)和面向切面编程(AOP),SpringMVC处理HTTP请求和响应,而MyBatis作为持久层框架,实现了数据库操作的简单化。在游乐园智慧向导系统中...

    基于springMVC的ssh简单物流系统 附报告.zip

    在Spring MVC中,控制器接收HTTP请求,处理业务逻辑,并决定如何展示结果。模型层负责业务对象的创建和管理,而视图层则负责数据的展示。 SSH框架组合则是经典的Java Web开发解决方案。Struts2作为MVC框架,处理...

    基于SSM的校园运动会管理系统的设计与实现.rar

    Spring提供了全面的事务管理、依赖注入等功能,SpringMVC则负责处理HTTP请求和响应,而MyBatis作为持久层框架,简化了数据库操作。这个系统的实现,将充分展示SSM框架在实际项目中的应用。 1. **系统架构设计**:...

    ssm+vue的物流配送管理系统(有报告) Javaee项目,ssm vue前后端分离项目

    SSM是Java Web开发中的常用组合,Spring作为核心容器管理Bean,SpringMVC处理HTTP请求,MyBatis则负责数据库交互。在这个项目中,Spring通过依赖注入实现业务对象的管理和协作,SpringMVC负责接收和转发请求,...

    物流管理系统数据库+后台ssh

    Struts2是另一个常用的Java Web框架,它基于Model-View-Controller模式,提供了丰富的插件和拦截器机制,便于实现请求处理、页面跳转和异常管理。在物流管理系统中,Struts2可以处理HTTP请求,调用后台服务,然后将...

    基于ssm+mysql的喀什古城旅游网源码数据库.doc

    - 使用SpringMVC接收前端请求,处理业务逻辑,并返回相应的数据。 - MyBatis负责数据持久化操作,包括增删改查等基本操作。 3. **安全性**: - 对敏感信息(如密码)进行加密处理,保护用户隐私。 - 实现权限控制...

    JSP基于SSM校园地图导航系统可升级SpringBoot毕业源码案例设计.zip

    - **后端Controller**:SpringMVC的Controller负责处理前端的请求,调用Service层的方法,完成业务逻辑。 - **Service层**:包含业务逻辑,可能包括路径规划算法,如Dijkstra算法或A*算法,用于计算两点间的最短或...

Global site tag (gtag.js) - Google Analytics