在传统的servlet开发模式下,一个web应用必然需要有对应的应用的配置,即web.xml文件。web.xml是应用的入口,针对特定请求进行相应的处理类的配置:
<!--配置Servlet--><servlet> <servlet-name>FristServlet</servlet-name> <servlet-class>FristServlet</servlet-class></servlet><!--映射Servlet -- ><servlet-mapping> <servlet-name>FristServlet</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping>
其中包括了处理类的定义<servlet/>,和请求与处理的映射配置<servlet-mapping />,当web容器启动时首先会读取web.xml,根据文件配置内容进行相关servlet类的加载和实例化,servlet标签中<load-on-startup></load-on-startup>定义了该类是否在容器启动时加载,其值大于等于0时为加载,(据说值越大,加载优先级越低),当类加载后会调用其init()方法。而其其它没有此项配置的servlet类,则会在第一次被请求时进行加载和实例化,即容器根据URL匹配到映射配置,然后检查该类没有实例化,则进行实例化,并调用init()方法,然后处理请求。
传统方式下,当应用逐渐变大,servlet数量大量增加时,web.xml文件配置将变得复杂而庞大,不利于开发。于是各种解决此问题的web框架应运而生,包括struts,struts2,springmvc等,将相关的处理方法集中在一个处理类中,通过通配符进行匹配,简化对外的配置,同时提供各种功能强大的请求参数拦截和包装功能,简化开发。
web框架到底是一个什么存在呢,我觉得其实就是一个应用,实现了对原有servlet功能的包装,提供更多简便的功能:请求参数拦截与包装,进行请求与处理类的对应关系的维护,DispatcherServlet则实现了对容器创建,请求发起到请求处理结束的全过程的功能的串联。
/** * @PROJECT_NAME smartweb * @PACKAGE_NAME org.smartweb.web * @USER takou * @CREATE 2018/4/5 **/@WebServlet(loadOnStartup = 0,urlPatterns = "*.do")public class DispatcherServlet extends HttpServlet { private static final Logger LOGGER = LoggerFactory.getLogger(DispatcherServlet.class); @Override public void init(ServletConfig config) throws ServletException { BeanContainerBuilder.buildWebContainer(); ServletContext context = config.getServletContext(); ServletRegistration jsp = context.getServletRegistration("jsp"); jsp.addMapping(ConfigUtil.getJspPath() + "*"); ServletRegistration asset = context.getServletRegistration("default"); asset.addMapping(ConfigUtil.getAsset() + "*"); }
通过init()方法在web容器启动时调用,初始化框架相关内容,通过buildWebContainer()实现
/** * @PROJECT_NAME smartweb * @PACKAGE_NAME org.smartweb.container * @USER takou * @CREATE 2018/4/5 **/public final class BeanContainerBuilder { private static Class<?>[] containers = new Class<?>[]{BeanContainer.class,WebController.class}; /** * @Description 构建bean容器 * @Param [] * @Return void * @Author takou * @Date 2018/4/6 * @Time 下午12:17 */ public static void buildWebContainer() { for (Class<?> cls : containers) { ClassUtil.loadClass(cls.getName(),true); } }}
其中包括了ioc对bean的管理以及对请求与处理类的映射关系管理
/** * @PROJECT_NAME smartweb * @PACKAGE_NAME org.smartweb.web * @USER takou * @CREATE 2018/4/5 **/public final class WebController { private static final Map<Request,Handler> ACTION_MAP = new HashMap<Request, Handler>(); private static final Set<Class<?>> controllerSet = BeanContainer.getControllerSet(); static { //扫描所有controller,建立request和handler的对应关系 for (Class<?> cls : controllerSet) { Method[] methods = cls.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Action.class)) { Action action = method.getAnnotation(Action.class); String reqMethod = action.method(); String reqResource = action.resource(); Request request = new Request(reqMethod,reqResource); Handler handler = new Handler(method,cls); ACTION_MAP.put(request,handler); } } } } public static Handler getHandler(String method, String resource) { Request request = new Request(method,resource); return ACTION_MAP.get(request); } public static Map<Request, Handler> getActionMap() { return ACTION_MAP; }}
此处通过Action注解实现对请求与处理方法的映射配置,并放入ACTION_MAP中进行管理,而handler包含了处理方法和所属Class,请求过来时,由URL获得handler,
@Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获得请求的方法和请求的资源 String method = req.getMethod().toLowerCase(); String resource = req.getServletPath(); //获得处理请求的action Handler handler = WebController.getHandler(method,resource);
handler配置的Class从ioc容器中获得实例,然后进行调用 @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获得请求的方法和请求的资源 String method = req.getMethod().toLowerCase(); String resource = req.getServletPath(); //获得处理请求的action Handler handler = WebController.getHandler(method,resource); if (handler == null) { handler = WebController.getHandler("get","/customer/goToIndex"); } Object obj = BeanContainer.getBean(handler.getCls()); Method reqMethod = handler.getMethod(); //拦截请求参数,进行封装 Map<String,String[]> param = req.getParameterMap(); //调用目标方法 Object object = null; try { ModelAndView view = new ModelAndView(); view.setReqParam(param); object = reqMethod.invoke(obj,view); } catch (IllegalAccessException e) { LOGGER.error(reqMethod.getName() + " illegal access",e); throw new RuntimeException(e); } catch (InvocationTargetException e) { LOGGER.error(reqMethod.getName() + " invoke failure",e); throw new RuntimeException(e); } //处理调用后的返回结果,进行封装 if (object instanceof ModelAndView) { ModelAndView view = (ModelAndView) object; view.setRequest(req); view.setResponse(resp); view.forward(); } else if (object instanceof Data) { //处理json数据... Data data = (Data) object; //Object jsonObj = data.getModel(); //String json = JSONValue.toJSONString(jsonObj); resp.setCharacterEncoding("utf-8"); resp.setContentType("application/json"); PrintWriter writer = resp.getWriter(); //JSONValue.writeJSONString(data.getModel().toString()); writer.write(data.getModel().toString()); writer.flush(); writer.close(); } }
统一请求处理方法的声明,便于反射调用时的参数传递,这里就包括了对请求参数拦截和包装的具体功能
//拦截请求参数,进行封装 Map<String,String[]> param = req.getParameterMap(); //调用目标方法 Object object = null; try { ModelAndView view = new ModelAndView(); view.setReqParam(param); object = reqMethod.invoke(obj,view); } catch (IllegalAccessException e) { LOGGER.error(reqMethod.getName() + " illegal access",e); throw new RuntimeException(e); } catch (InvocationTargetException e) { LOGGER.error(reqMethod.getName() + " invoke failure",e); throw new RuntimeException(e); }
调用完成后,对返回结果进行分类包装处理,返回静态资源,页面跳转或返回json数据等
//处理调用后的返回结果,进行封装 if (object instanceof ModelAndView) { ModelAndView view = (ModelAndView) object; view.setRequest(req); view.setResponse(resp); view.forward(); } else if (object instanceof Data) { //处理json数据... Data data = (Data) object; //Object jsonObj = data.getModel(); //String json = JSONValue.toJSONString(jsonObj); resp.setCharacterEncoding("utf-8"); resp.setContentType("application/json"); PrintWriter writer = resp.getWriter(); //JSONValue.writeJSONString(data.getModel().toString()); writer.write(data.getModel().toString()); writer.flush(); writer.close(); }
由此,一个请求处理完成,可见DispatcherServlet的框架入口功能和请求处理的总控功能。