博学,切问,近思--詹子知(http://blog.csdn.net/zhiqiangzhan)
本文将讨论HandlerMapping,HandlerAdapter,ViewResolver组件类的具体实现。
URLHandlerMapping,利用request中包含的url信息,找到对应Handler对象,URLHandlerMapping是最典型的映射方式。 package com.google.mvc.web.servlet.handler; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import com.google.mvc.web.context.WebApplicationContext; import com.google.mvc.web.context.WebApplicationContextAware; import com.google.mvc.web.servlet.HandlerMapping; import com.google.mvc.web.servlet.mvc.Controller; import com.google.mvc.web.servlet.mvc.HttpRequestHandler; public class URLHandlerMapping implements HandlerMapping, WebApplicationContextAware{ private static final Logger LOGGER = Logger.getLogger(URLHandlerMapping.class); private WebApplicationContext wac; private Map<String, Object> handlerMap = new ConcurrentHashMap<String, Object>(); private AtomicBoolean initialize = new AtomicBoolean(false); @Override public void setWebApplicationContext(WebApplicationContext wac) { this.wac = wac; } @Override public Object getHandler(HttpServletRequest request) throws Exception { if(LOGGER.isDebugEnabled()){ LOGGER.debug("Find handler for request " + request.getServletPath()); } if(initialize.compareAndSet(false, true)){ Map<String, HttpRequestHandler> map1 = wac.beansOfType(HttpRequestHandler.class); for(String key : map1.keySet()){ handlerMap.put(key, map1.get(key)); } Map<String, Controller> map2 = wac.beansOfType(Controller.class); for(String key : map2.keySet()){ handlerMap.put(key, map2.get(key)); } } Object handler = handlerMap.get(getHandlerName(request)); if(handler == null){ handler = handlerMap.get("404"); } return handler; } protected String getHandlerName(HttpServletRequest request){ String path = request.getServletPath(); int index = path.lastIndexOf('/'); String handleName = path.substring(index + 1, path.length()); return handleName; } }
HandlerAdapter用于把不同的Handler对象处理的结果封装成一个统一的对象ModelAndView,以达到逻辑上的一致处理。在这里,我们定义两种Handler类型
- Controller:用于处理用户的业务逻辑,并返回具体ModelAndView对象,具体接口定义如下 package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.mvc.web.servlet.ModelAndView; public interface Controller { ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; }
- HttpRequestHandler:用于处理简单的HTTP请求。接口定义如下 package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface HttpRequestHandler { void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; } 我们来看一下HandlerFor404的简单实现。 package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HandlerFor404 implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { response.sendRedirect("404.html"); } }
ControllerHandlerAdapter package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.mvc.web.servlet.HandlerAdapter; import com.google.mvc.web.servlet.ModelAndView; public class ControllerHandlerAdapter implements HandlerAdapter{ public boolean supports(Object handler) { return (handler instanceof Controller); } public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); } public long getLastModified(HttpServletRequest request, Object handler) { return -1L; } } HttpRequestHandlerAdapter package com.google.mvc.web.servlet.mvc; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.mvc.web.servlet.HandlerAdapter; import com.google.mvc.web.servlet.ModelAndView; public class HttpRequestHandlerAdapter implements HandlerAdapter { @Override public long getLastModified(HttpServletRequest request, Object handler) { return 0; } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; } @Override public boolean supports(Object handler) { return (handler instanceof HttpRequestHandler); } }
ViewResolver用于指定View的生成方式,我们先来看下AbstractView的定义 package com.google.mvc.web.servlet.mvc; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import com.google.mvc.web.servlet.View; public abstract class AbstractView implements View { private static final Logger LOGGER = Logger.getLogger(AbstractView.class); public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri"; public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri"; public static final String FORWARD_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.forward.context_path"; public static final String FORWARD_SERVLET_PATH_ATTRIBUTE = "javax.servlet.forward.servlet_path"; public static final String FORWARD_PATH_INFO_ATTRIBUTE = "javax.servlet.forward.path_info"; public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string"; public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=ISO-8859-1"; private String contentType = DEFAULT_CONTENT_TYPE; private boolean alwaysInclude = false; @Override public String getContentType() { return contentType; } protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request){ Iterator<Entry<String, Object>> it = model.entrySet().iterator(); while (it.hasNext()) { Entry<String, Object> entry = it.next(); String modelName = entry.getKey(); Object modelValue = entry.getValue(); if (modelValue != null) { request.setAttribute(modelName, modelValue); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + this + "'"); } } else { request.removeAttribute(modelName); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Removed model object '" + modelName + "' from request in view with name '" + this + "'"); } } } } protected boolean useInclude(HttpServletRequest request, HttpServletResponse response) { return (this.alwaysInclude || request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null || response .isCommitted()); } protected void exposeForwardRequestAttributes(HttpServletRequest request) { exposeRequestAttributeIfNotPresent(request, FORWARD_REQUEST_URI_ATTRIBUTE, request.getRequestURI()); exposeRequestAttributeIfNotPresent(request, FORWARD_CONTEXT_PATH_ATTRIBUTE, request.getContextPath()); exposeRequestAttributeIfNotPresent(request, FORWARD_SERVLET_PATH_ATTRIBUTE, request.getServletPath()); exposeRequestAttributeIfNotPresent(request, FORWARD_PATH_INFO_ATTRIBUTE, request.getPathInfo()); exposeRequestAttributeIfNotPresent(request, FORWARD_QUERY_STRING_ATTRIBUTE, request.getQueryString()); } private void exposeRequestAttributeIfNotPresent(ServletRequest request, String name, Object value) { if (request.getAttribute(name) == null) { request.setAttribute(name, value); } } public void setContentType(String contentType) { this.contentType = contentType; } public boolean isAlwaysInclude() { return alwaysInclude; } public void setAlwaysInclude(boolean alwaysInclude) { this.alwaysInclude = alwaysInclude; } } 在这里我们仅实现一种View类型,也就是对jsp页面的简单处理。 package com.google.mvc.web.servlet.mvc; import java.io.IOException; import java.util.Map; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; public class InternalResourceView extends AbstractView { private static final Logger LOGGER = Logger.getLogger(InternalResourceView.class); private String url; @Override public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Rendering view with name '" + this + "' with model " + model); } if(model != null){ exposeModelAsRequestAttributes(model, request); } RequestDispatcher rd = request.getRequestDispatcher(url); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR"); } if (useInclude(request, response)) { response.setContentType(getContentType()); rd.include(request, response); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Included resource [" + getUrl() + "] in InternalResourceView '" + url + "'"); } }else { exposeForwardRequestAttributes(request); rd.forward(request, response); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Forwarded to resource [" + getUrl() + "] in InternalResourceView '" + url + "'"); } } } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String toString(){ return this.url; } } 好了,到这里,我们再来看看ViewResolver类的实现 package com.google.mvc.web.servlet.mvc; import org.apache.log4j.Logger; import com.google.mvc.web.servlet.View; import com.google.mvc.web.servlet.ViewResolver; public class DefaultViewResolver implements ViewResolver { private static final Logger LOGGER = Logger.getLogger(DefaultViewResolver.class); private String prefix = ""; private String suffix = ""; private Class<View> viewClass; @Override public View resolveViewName(String viewName) throws Exception { View view = viewClass.newInstance(); if(view instanceof InternalResourceView){ ((InternalResourceView)view).setUrl(prefix + viewName + suffix); } return view; } public void setViewClass(String viewClass){ try { this.viewClass = (Class<View>) this.getClass().getClassLoader().loadClass(viewClass); } catch (ClassNotFoundException e) { LOGGER.error("Can't load view class " + viewClass, e); } } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } public String getSuffix() { return suffix; } public void setSuffix(String suffix) { this.suffix = suffix; } }
到这里,我们在整个MVC架构的源码实现已经完成了,下一篇,我们将介绍一个基于我们这个MVC架构的Demo。
相关文章: