在传统的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的框架入口功能和请求处理的总控功能。