spring MVC工做机制与设计模式(《深刻分析java Web》技术内幕-14)-读后小结(一/二/三)

读《深刻分析java web技术内幕》 spring MVC一章,发现一篇好的读后总结,转载与此作记录:

重点:java

1. Spring MVC中的dispatcherServletweb

2. 8个组件(handleMapping & handleAdaper & viewResolver)spring

3. MVC的关系,理解URL与handler的对应关系存放,handlerExecutionChain对象,modelAndMap对象数组

 

---------------------------- 原文 -----------------------------spring-mvc

地址:https://my.oschina.net/bosscheng/blog/129282session

Spring MVC的整体设计

在一个工程中若是想要使用 spring MVC的话,只须要两个步骤mvc

  • 在web.xml中配置一个DispatcherServlet。

须要配置一个org.springframework.web.servlet.DispatcherServlet的servlet。app

  • 再定义一个dispatcherServlet-servlet.xml配置文件。(通常配置spring-mvc.xml)

在这个配置文件里面咱们只须要扩展一个路径映射关系,定义一个视图解析器,再定义一个业务逻辑的处理流程规则。框架

这样就能够搞定一个最基本的Spring MVC的应用了。jsp

 

对于DispatcherServlet初始化的时候初始了哪些东西,这些能够在initStrategies中看到。

 

/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		//初始化MultipartResolver,主要是处理文件上传服务。
                initMultipartResolver(context);
                //用于处理应用的国际化问题
		initLocaleResolver(context);
                //用于定义一个主题
		initThemeResolver(context);
                //用于定义用户设置的请求映射关系
		initHandlerMappings(context);
                //用于根据Handler的类型定义不一样的处理规则
		initHandlerAdapters(context);
                //当Handler处理错误的时候,经过这个handler来作统一的处理
		initHandlerExceptionResolvers(context);
                //将指定的ViewName按照定义的RequestToViewNameTranslator替换成想要的格式。
		initRequestToViewNameTranslator(context);
                //用于将view解析成页面
		initViewResolvers(context);
                //用于映射flash管理的。
		initFlashMapManager(context);
	}

 

 

小结: 对于spring MVC框架中,有三个组件是用户必须定义和扩展的:

  • 定义URL映射规则:handlerMapping
  • 实现业务逻辑的handler实例对象:handlerAdapter
  • 渲染模版资源:ViewResolver

 

看看DispatcherServlet启用的时候作了哪些工做?

  • 调用HttpServletBean的init()方法。在init()方法中,建立了BeanWrapper对象,而且执行了initServletBean()方法;
/**
	 * Map config parameters onto bean properties of this servlet, and
	 * invoke subclass initialization.
	 * @throws ServletException if bean properties are invalid (or required
	 * properties are missing), or if subclass initialization fails.
	 */
	@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		try {
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			throw ex;
		}

		// Let subclasses do whatever initialization they like.
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}
  • 调用FrameworkServlet的createWebApplicationContext方法,初始化spring容器。
  • 调用FrameworkServlet的onRefresh方法完成配置文件的加载,配置文件的加载一样是先查找servlet的init-param参数中设置的路径,若是没有,就会根据namespace+Servlet的名称来查找XML文件。
  • Spring容器加载的时候会调用DispatcherServlet的initStrategies方法来初始化springMVC框架所须要的八个组件,这八个组件对应的八个bean对应都保存在DispatcherServlet类中。

 

这样:spring MVC的初始化工做就完成了。这样就能够接受你的http请求了。

 

Control设计

Spring MVC 的Control主要是由HandlerMapping(interface)和HandlerAdapter(interface)两个组件提供。

  • org.springframework.web.servlet.HandlerMapping
  • org.springframework.web.servlet.HandlerAdapter

对于HandlerMapping:主要负责映射用户的URL和对应的处理类,HandlerMapping并无规定这个URL与应用的处理类如何映射,HandlerMapping接口中只定义了根据一个URL必须返回一个由HandlerExceptionChain表明的处理链,咱们能够在这个处理链中添加任意的HandlerAdapter实例来处理这个URL对应的请求,这个设计思路和Servlet规范中的Filter处理是相似的。

 

/**
	 * Return a handler and any interceptors for this request. The choice may be made
	 * on request URL, session state, or any factor the implementing class chooses.
	 * <p>The returned HandlerExecutionChain contains a handler Object, rather than
	 * even a tag interface, so that handlers are not constrained in any way.
	 * For example, a HandlerAdapter could be written to allow another framework's
	 * handler objects to be used.
	 * <p>Returns <code>null</code> if no match was found. This is not an error.
	 * The DispatcherServlet will query all registered HandlerMapping beans to find
	 * a match, and only decide there is an error if none can find a handler.
	 * @param request current HTTP request
	 * @return a HandlerExecutionChain instance containing handler object and
	 * any interceptors, or <code>null</code> if no mapping found
	 * @throws Exception if there is an internal error
	 */
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

------------------------------------邪恶的分割线-------------------------------------------

HandlerMapping初始化

Spring MVC自己也提供了不少HandlerMapping 的实现,默认使用的是BeanNameUrlHandlerMapping,也能够根据Bean的name属性映射到URL中。

 

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

	/**
	 * Checks name and aliases of the given bean for URLs, starting with "/".
	 */
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<String>();
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		String[] aliases = getApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}

}

 

 

对于HandlerMapping,能够帮助咱们管理URL和处理类的映射关系,简单的说就是能够帮助咱们将一个或者多个URL映射到一个或者多个spring Bean中。

下面总结下spring MVC是如何将请求的URL映射到咱们定义的bean中的。

    对于HandlerMapping是如何初始化的。spring MVC提供了一个HandlerMapping的抽象类 AbstractHandlerMapping。

 

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
		implements HandlerMapping, Ordered {
}

能够经过让HandlerMapping设置setOrder方法提升优先级和经过覆盖initApplicationContext方法实现初始化的工做。

 

@Override
	protected void initApplicationContext() throws BeansException {
		extendInterceptors(this.interceptors);
		detectMappedInterceptors(this.mappedInterceptors);
		initInterceptors();
	}

 

对于SimpleUrlHandlerMapping类是如何初始化的:

  • 首先调用ApplicationObjectSupport的setApplicationContext()方法。
public final void setApplicationContext(ApplicationContext context) throws BeansException {
		if (context == null && !isContextRequired()) {
			// Reset internal context state.
			this.applicationContext = null;
			this.messageSourceAccessor = null;
		}
		else if (this.applicationContext == null) {
			// Initialize with passed-in context.
			if (!requiredContextClass().isInstance(context)) {
				throw new ApplicationContextException(
						"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
			}
			this.applicationContext = context;
			this.messageSourceAccessor = new MessageSourceAccessor(context);
			initApplicationContext(context);
		}
		else {
			// Ignore reinitialization if same context passed in.
			if (this.applicationContext != context) {
				throw new ApplicationContextException(
						"Cannot reinitialize with different application context: current one is [" +
						this.applicationContext + "], passed-in one is [" + context + "]");
			}
		}
	}

 

  • 调用SimpleUrlHandlerMapping的initApplicationContext()方法

 

/**
	 * Calls the {@link #registerHandlers} method in addition to the
	 * superclass's initialization.
	 */
	@Override
	public void initApplicationContext() throws BeansException {
		super.initApplicationContext();
		registerHandlers(this.urlMap);
	}

 

  • 调用super.initApplicationContext(),调用了AbstractHandlerMapping的initApplicationContext()方法

 

/**
	 * Initializes the interceptors.
	 * @see #extendInterceptors(java.util.List)
	 * @see #initInterceptors()
	 */
	@Override
	protected void initApplicationContext() throws BeansException {
		extendInterceptors(this.interceptors);
		detectMappedInterceptors(this.mappedInterceptors);
		initInterceptors();
	}

初始化initInterceptors()方法:将SimpleUrlHandlerMapping中定义的interceptors包装成handlerInterceptor对象保存在adaptedInterceptors数组中。

 

  • SimpleUrlHandlerMapping继续执行registerHandlers(this.urlMap)方法;

在方法registerHandlers中,调用了AbstractUrlHandlerMapping的registerHandler(url, handler)方法;

在方法registerHandler(url, handler)中,将在SimpleUrlHandlerMapping中定义的mappings注册到handlerMap集合中。

 

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
		if (urlMap.isEmpty()) {
			logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
		}
		else {
			for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
				String url = entry.getKey();
				Object handler = entry.getValue();
				// Prepend with slash if not already present.
				if (!url.startsWith("/")) {
					url = "/" + url;
				}
				// Remove whitespace from handler bean name.
				if (handler instanceof String) {
					handler = ((String) handler).trim();
				}
				registerHandler(url, handler);
			}
		}
	}

在registerHandler方法中; 

 

/**
	 * Register the specified handler for the given URL path.
	 * @param urlPath the URL the bean should be mapped to
	 * @param handler the handler instance or handler bean name String
	 * (a bean name will automatically be resolved into the corresponding handler bean)
	 * @throws BeansException if the handler couldn't be registered
	 * @throws IllegalStateException if there is a conflicting handler registered
	 */

----------------------------------邪恶的分割线-----------------------------------

小结: HandlerMapping初始化工做完成的两个最重要的工做就是:

  1. 将URL与Handler的对应关系保存在HandlerMapping集合中,并将全部的interceptors对象保存在adaptedInterceptors数组中,等请求到来的时候执行全部的adaptedIntercoptors数组中的interceptor对象。
  2. 全部的interceptor必须实现HandlerInterceptor接口。

 

HandlerAdapter初始化

HandlerMapping 能够完成URL与Handler的映射关系,那么HandlerAdapter就能够帮助自定义各类handler了。由于SpringMVC首先帮助咱们把特别的URL对应到一个Handler,那么这个Handler一定要符合某种规则,最多见的方法就是咱们的全部handler都继承某个接口,而后SpringMVC 天然就调用这个接口中定义的特性方法。

对于spring MVC提供了三种典型的handlerAdapter实现类。

 

  1. SimpleServletHandlerAdapter:能够直接继承Servlet接口。
  2. SimpleControllerHandlerAdapter:能够继承Controller接口。
  3. SimpleRequestHandlerAdapter:能够继承HttpRequsetHandler接口。

对于handlerAdapter的初始化没有什么特别之处,只是简单的建立一个handlerAdapter对象,将这个对象保存在DispatcherServlet的HandlerAdapters集合中。当Spring MVC将某个URL对应到某个Handler时候,在handlerAdapters集合中查询那个handlerAdapter对象supports这个Handler,handlerAdapter对象将会被返回,用了调用这个handlerAdapter接口对应的方法。

 

------------------------------------邪恶的分割线-------------------------------------------

Control的调用逻辑

整个Spring MVC的调用是从DispatcherServlet的doService方法开始的,在doService方法中会将ApplicationContext、localeResolver、themeResolver等对象添加到request中便于在后面使用,接着就调用doDispatch方法,这个方法是主要的处理用户请求的地方。

 

对于control的处理关键就是:DispatcherServlet的handlerMappings集合中根据请求的URL匹配每个handlerMapping对象中的某个handler,匹配成功以后将会返回这个handler的处理链接handlerExecutionChain对象。而这个handlerExecutionChain对象中将会包含用户自定义的多个handlerInterceptor对象。

 

/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found
	 */
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

而对于handlerInterceptor接口中定义的三个方法中,preHandler和postHandler分别在handler的执行前和执行后执行,afterCompletion在view渲染完成、在DispatcherServlet返回以前执行。

 

PS:这么咱们须要注意的是:当preHandler返回false时,当前的请求将在执行完afterCompletion后直接返回,handler也将不会执行。

在类HandlerExecutionChain中的getHandler()方法是返回object对象的;

 

/**
	 * Return the handler object to execute.
	 * @return the handler object
	 */
	public Object getHandler() {
		return this.handler;
	}

这里的handler是没有类型的,handler的类型是由handlerAdapter决定的。dispatcherServlet会根据handler对象在其handlerAdapters集合中匹配哪一个HandlerAdapter实例支持该对象。接下来去执行handler对象的相应方法了,若是该handler对象的相应方法返回一个ModelAndView对象接下来就是去执行View渲染了。

 

/**
	 * Return the handler object to execute.
	 * @return the handler object
	 */
	public Object getHandler() {
		return this.handler;
	}

 

Model设计

若是handler兑现返回了ModelAndView对象,那么说明Handler须要传一个Model实例给view去渲染模版。除了渲染页面须要model实例,在业务逻辑层一般也有Model实例。

 

ModelAndView对象是链接业务逻辑层与view展现层的桥梁,对spring MVC来讲它也是链接Handler与view的桥梁。ModelAndView对象顾名思义会持有一个ModelMap对象和一个View对象或者View的名称。ModelMap对象就是执行模版渲染时候所须要的变量对应的实例,如jsp的经过request.getAttribute(String)获取的JSTL标签名对应的对象。velocity中context.get(String)获取$foo对应的变量实例。

 

public class ModelAndView {

/** View instance or view name String */
	private Object view;

	/** Model Map */
	private ModelMap model;

	/** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */
	private boolean cleared = false;

.....

}

 

ModelMap其实也是一个Map,Handler中将模版中须要的对象存在这个Map中,而后传递到view对应的ViewResolver中。

 

public interface ViewResolver {
	View resolveViewName(String viewName, Locale locale) throws Exception;

}

 

不一样的ViewResolver会对这个Map中的对象有不一样的处理方式;

  • velocity中将这个Map保存到VelocityContext中。
  • JSP中将每个ModelMap中的元素分别设置到request.setAttribute(modelName,modelValue);

-----------------------邪恶的分割线-----------------------------------------------

view设计

在spring MVC中,view模块须要两个组件来支持:RequestToViewNameTranslator和ViewResolver

 

public interface RequestToViewNameTranslator {

	/**
	 * Translate the given {@link HttpServletRequest} into a view name.
	 * @param request the incoming {@link HttpServletRequest} providing
	 * the context from which a view name is to be resolved
	 * @return the view name (or <code>null</code> if no default found)
	 * @throws Exception if view name translation fails
	 */
	String getViewName(HttpServletRequest request) throws Exception;

}

对于 ViewResolver,前面有写到了,就不写了;

 

RequestToViewNameTranslator:主要支持用户自定义对viewName的解析,如将请求的ViewName加上前缀或者后缀,或者替换成特定的字符串等。

ViewResolver:主要是根据用户请求的viewName建立适合的模版引擎来渲染最终的页面,ViewResolver会根据viewName建立一个view对象,调用view对象的Void render方法渲染出页面;

 

public interface View {
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

下面来总结下 Spring MVC解析View的逻辑:

  • dispatcherServlet方法调用getDefaultViewName()方法;
/**
	 * Translate the supplied request into a default view name.
	 * @param request current HTTP servlet request
	 * @return the view name (or <code>null</code> if no default found)
	 * @throws Exception if view name translation failed
	 */
	protected String getDefaultViewName(HttpServletRequest request) throws Exception {
		return this.viewNameTranslator.getViewName(request);
	}
  • 调用了RequestToViewNameTranslator的getViewName方法;

 

public interface RequestToViewNameTranslator {

	/**
	 * Translate the given {@link HttpServletRequest} into a view name.
	 * @param request the incoming {@link HttpServletRequest} providing
	 * the context from which a view name is to be resolved
	 * @return the view name (or <code>null</code> if no default found)
	 * @throws Exception if view name translation fails
	 */
	String getViewName(HttpServletRequest request) throws Exception;

}

 

  • 调用LocaleResolver接口的resolveLocale方法;

 

Locale resolveLocale(HttpServletRequest request);
  • 调用ViewResolver接口的resolveViewName方法,返回view对象

 

View resolveViewName(String viewName, Locale locale) throws Exception;
  • 调用render方法渲染出页面
相关文章
相关标签/搜索