MVC架構探究及其源碼實現(4)-前端控製器
博學,切問,近思--詹子知 (https://jameszhan.github.io)
前端控製器是整個MVC框架中最為核心的一塊,它主要用來攔截符合要求的外部請求,並把請求分發到不同的控製器去處理,根據控製器處理後的結果,生成相應的響應發送到客戶端。前端控製器既可以使用Filter實現(Struts2采用這種方式),也可以使用Servlet來實現。這裏我們就采用後一種方式來實現我們的MVC框架。
1.配置web.xml,使得我們的前端控製器可以攔截所有符合要求的用戶請求,這裏我們的前端控製器能處理所有以.do結尾的用戶請求。 <?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="https://java.sun.com/xml/ns/javaee"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<description>MVC Sample</description>
<display-name>MVC</display-name>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.google.mvc.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
2.FrameworkServlet實現。
FrameworkServlet是DispatcherServlet的直接父類,繼承自HttpServlet,主要用來初始話WebApplicationContext,把不同的Http請求操作委托給同一個方法去處理。package com.google.mvc.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.google.mvc.web.context.WebApplicationContext;
public abstract class FrameworkServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(FrameworkServlet.class);
private WebApplicationContext webApplicationContext;
@Override
public void init() throws ServletException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("----------Initializing servlet '" + getServletName() + "'----------");
}
this.webApplicationContext = initWebApplicationContext();
initServletBean();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("----------Servlet '" + getServletName() + "' configured successfully----------/n/n");
}
}
private WebApplicationContext initWebApplicationContext() {
WebApplicationContext wac = new WebApplicationContext(getServletContext());
wac.init();
onRefresh(wac);
return wac;
}
protected void onRefresh(WebApplicationContext context) {
// For subclasses: do nothing by default.
}
protected void initServletBean(){
}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
try {
doService(request, response);
} catch (ServletException ex) {
failureCause = ex;
throw ex;
} catch (IOException ex) {
failureCause = ex;
throw ex;
} catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
} finally {
if (failureCause != null) {
LOGGER.error("Could not complete request", failureCause);
} else {
long processingTime = System.currentTimeMillis() - startTime;
if (LOGGER.isDebugEnabled()) {
LOGGER.info("Successfully completed request, cost " + processingTime + " ms/n");
}
}
}
}
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
processRequest(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
processRequest(request, response);
}
@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
processRequest(request, response);
}
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
processRequest(request, response);
}
@Override
public void destroy() {
if(LOGGER.isDebugEnabled()){
LOGGER.info("Servlet destory");
}
super.destroy();
}
public WebApplicationContext getWebApplicationContext() {
return webApplicationContext;
}
}
3.DispatcherServlet實現。 package com.google.mvc.web.servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.google.mvc.web.context.WebApplicationContext;
public class DispatcherServlet extends FrameworkServlet {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(DispatcherServlet.class);
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
private static final Properties defaultStrategies = new Properties();
private List<HandlerMapping> handlerMappings;
private List<HandlerAdapter> handlerAdapters;
private List<ViewResolver> viewResolvers;
static {
try {
defaultStrategies.load(DispatcherServlet.class.getResourceAsStream(DEFAULT_STRATEGIES_PATH));
} catch (IOException ex) {
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
}
}
@Override
protected void onRefresh(WebApplicationContext wac) {
initHandlerMappings(wac);
initHandlerAdapters(wac);
initViewResolvers(wac);
}
private void initHandlerMappings(WebApplicationContext wac) {
Map<String, HandlerMapping> map = wac.beansOfType(HandlerMapping.class);
if (!map.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(map.values());
}
if (this.handlerMappings == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
this.handlerMappings = getDefaultStrategies(wac, HandlerMapping.class);
}
}
private void initHandlerAdapters(WebApplicationContext wac) {
Map<String, HandlerAdapter> map = wac.beansOfType(HandlerAdapter.class);
if (!map.isEmpty()) {
this.handlerAdapters = new ArrayList<HandlerAdapter>(map.values());
}
if (this.handlerAdapters == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
}
this.handlerAdapters = getDefaultStrategies(wac, HandlerAdapter.class);
}
}
private void initViewResolvers(WebApplicationContext wac) {
Map<String, ViewResolver> map = wac.beansOfType(ViewResolver.class);
if (!map.isEmpty()) {
this.viewResolvers = new ArrayList<ViewResolver>(map.values());
}
if (this.viewResolvers == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
}
this.viewResolvers = getDefaultStrategies(wac, ViewResolver.class);
}
}
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("DispatcherServlet with name '" + getServletName() + "' received request for ["
+ request.getRequestURI() + "]");
}
doDispatch(request, response);
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Bound request context to thread: " + request);
}
Object handler = getHandler(request);
HandlerAdapter ha = getHandlerAdapter(handler);
ModelAndView mv = ha.handle(request, response, handler);
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
}
}
protected <T> List<T> getDefaultStrategies(WebApplicationContext wac, Class<T> strategyInterface) {
String key = strategyInterface.getName();
List<T> strategies = new ArrayList<T>();
String value = defaultStrategies.getProperty(key);
if (value != null) {
StringTokenizer token = new StringTokenizer(value, ",");
while (token.hasMoreTokens()) {
String className = token.nextToken();
try {
Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
strategies.add((T) wac.createBean(clazz));
} catch (Exception e) {
LOGGER.error("Can't load class " + className + "", e);
}
}
} else {
strategies = Collections.emptyList();
}
return strategies;
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view = null;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName()
+ "' in servlet with name '" + getServletName() + "'");
}
} else {
// No need to lookup: the ModelAndView object contains the actual
// View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a "
+ "View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
view.render(mv.getModelInternal(), request, response);
}
protected View resolveViewName(String viewName, Map<String, Object> model, HttpServletRequest request)
throws Exception {
for (Iterator<ViewResolver> it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = it.next();
View view = viewResolver.resolveViewName(viewName);
if (view != null) {
return view;
}
}
return null;
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator<HandlerAdapter> it = this.handlerAdapters.iterator();
while (it.hasNext()) {
HandlerAdapter ha = it.next();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler
+ "]: Does your handler implement a supported interface like Controller?");
}
protected Object getHandler(HttpServletRequest request) throws Exception {
Iterator<HandlerMapping> it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = it.next();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName()
+ "'");
}
return hm.getHandler(request);
}
return null;
}
private String getDefaultViewName(HttpServletRequest request) {
String url = request.getServletPath();
url = url.replaceAll("/", "");
url = url.replaceAll(".html", "");
url = url.replaceAll(".htm", "");
url = "WEB-INF/" + url + ".jsp";
return url;
}
}
初始化操作.
- 檢查係統中是否已經定義HandlerMapping。如果沒有定義,則使用默認配置。
- 檢查係統中是否已經定義HandlerAdapter。如果沒有定義,則使用默認配置。
- 檢查係統中是否已經定義ViewResolover。如果沒有定義,則使用默認配置。
請求處理.
- 根據特定的請求,使用HandlerMapping找到相應的控製器Handler。
- 找到支持此種handler的HandlerAdapter,handler處理完響應業務後,HandlerAdapter把它轉化為ModelAndView對象。
- 利用ViewResolver對ModelAndView進行分析,生成相應的View對象。
- 生成響應。
默認配置 com.google.mvc.web.servlet.HandlerMapping=com.google.mvc.web.servlet.handler.URLHandlerMapping com.google.mvc.web.servlet.HandlerAdapter=com.google.mvc.web.servlet.mvc.HttpRequestHandlerAdapter,/ com.google.mvc.web.servlet.mvc.ControllerHandlerAdapter com.google.mvc.web.servlet.ViewResolver=com.google.mvc.web.servlet.mvc.DefaultViewResolver
相關文章:
- MVC架構探究及其源碼實現(1)-理論基礎
- MVC架構探究及其源碼實現(2)-核心組件定義
- MVC架構探究及其源碼實現(3)-WebApplicationContext
- MVC架構探究及其源碼實現(5)-相關組件實現
- MVC架構探究及其源碼實現(6)-簡單示例
最後更新:2017-04-02 04:01:42