博学,切问,近思--詹子知(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");
}}
我们需要为每一种不同的Handler类型指定一种HandlerAdapter,但这不是必须的。
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。
相关文章:
- MVC架构探究及其源码实现(1)-理论基础
- MVC架构探究及其源码实现(2)-核心组件定义
- MVC架构探究及其源码实现(3)-WebApplicationContext
- MVC架构探究及其源码实现(4)-前端控制器
- MVC架构探究及其源码实现(6)-简单示例