11.2、Spring源码学习 ——SpringMVC 之 DispatcherServet 的 init()方法 不念不忘少年蓝@ 2021-10-14 04:00 153阅读 0赞 ### 文章目录 ### * * * 前言 * HttpServlet 基本介绍 * * HttpServlet 的基本特性 * HttpServlet 的使用方法 * DispatcherServet 间接 继承了 HttpServlet * * DispatcherServet 、FrameworkServlet、HttpServletBean UML图 * HttpServletBean.init() * FrameworkServlet.initServletBean() * FrameworkServlet.initWebApplicationContext() ### 前言 ### > 体能状态先于精神状态,习惯先于决心,聚焦先于喜好。 ### HttpServlet 基本介绍 ### #### HttpServlet 的基本特性 #### > 参考 javax.servlet.http.HttpServlet 源码中的注解可以得知以下信息 * HttpServlet 是一个抽象类,允许子类为一个 WEB 站点创建一个合适的 HTTP servlet * HttpServlet 的子类必须覆盖重写下面中至少一个方法, * doGet,servlet 用于支持 HTTP GET 请求 * doPost,用于支持 HTTP POST 请求 * doPut,用于支持 HTTP PUT 请求 * doDelete,用于支持 HTTP DELETE 请求 * init 和 destroy, 用于管理 servlet 声明周期的资源 * getServletInfo,默认返回一个空字符串, 可以覆盖重写为 作者, 版本, 和 所属权等内容 。 * HttpServlet 中不建议覆盖重写的方法——尽管你可以覆盖重写 * service,该方法会独立分发不同的 HTTP 请求,根据其 HTTP 请求类型 * HttpServlet 不用覆盖重写的方法 * doOptions * doTrace * Servlets 经常运行在多线程的服务器,所以应该意识到 servlet 必须处理并发的请求,并且小心处理共享资源的同步使用问题。 * 共享的资源包括缓存中的数据——比如: * 实例或者类的变量 * 外部实体如文件、数据库连接资源、网络连接资源 #### HttpServlet 的使用方法 #### > 我们自定义一个子类来继承 HttpServlet ,演示一下 HttpServlet 的使用方法 * 自定义 HttpServlet 子类 package com.bestcxx.cn.webrecord.servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class MyHttpServlet extends HttpServlet { @Override public void init() throws ServletException { super.init(); System.out.println(System.currentTimeMillis()+":MyHttpServlet.init()方法被调用了"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //调用统一处理方法 handle(req,resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //不要调用 super 方法,否则报 HTTP method GET is not supported by this URL //调用统一处理方法 handle(req,resp); } private void handle(HttpServletRequest req, HttpServletResponse resp){ //获取 servlet 上下文信息 ServletContext servletContext=getServletContext(); //访问路径 String contextPath=servletContext.getContextPath(); System.out.println(System.currentTimeMillis()+":项目名称为:"+contextPath); //这里我们简化处理流程,仅返回一个字符串 PrintWriter printWriter=null; try{ printWriter = resp.getWriter(); printWriter.write("Success ,contetPath is :"+contextPath); }catch(Exception e){ System.out.println("系统异常:"+e.getMessage()+";"+e.getStackTrace()); }finally { if(printWriter!=null){ printWriter.close(); } } } } * web.xml 增加 servlet 配置 <servlet> <servlet-name>myservlet</servlet-name> <servlet-class> com.bestcxx.cn.webrecord.servlet.MyHttpServlet</servlet-class> <!--该值大于等于0 ,在servlet 初始化时运行 HttpServlet init()方法, 否则在第一次请求时运行且仅运行一次 HttpServlet init() 方法--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>myservlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> * 在 浏览器访问 http://localhost:8085/webrecord 两次 * 浏览器显示 Success ,contetPath is :/webrecord * 控制台打印-无 < load-on-startup>配置 > 当没有配置 < load-on-startup>1</ load-on-startup> > 第一次访问的时候 init()被调用,第二次访问时,init()不会被调用 1566105211059:MyHttpServlet.init()方法被调用了 1566105211070:项目名称为:/webrecord 1566105243018:项目名称为:/webrecord * 控制台打印-有 < load-on-startup>配置 > 当配置 < load-on-startup>1</ load-on-startup> > 启动阶段就会运行 servlet的init()方法 1566105211070:项目名称为:/webrecord 1566105243018:项目名称为:/webrecord ### DispatcherServet 间接 继承了 HttpServlet ### > DispatcherServet 间接 继承了 HttpServlet , 从UML关系图中可以看出来。 > HttpServlet 拥有的基本特性 DispatcherServet 也会拥有。 > 而在web项目启动阶段可以配置自动运行的 init()方法在 HttpServletBean进行了覆盖重写。 #### DispatcherServet 、FrameworkServlet、HttpServletBean UML图 #### > DispatcherServet extends FrameworkServlet > FrameworkServlet extends HttpServletBean > FrameworkServlet 和 HttpServletBean 都是抽象类,它俩都有一些方法留给子类来实现 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70] #### HttpServletBean.init() #### > 完整路径 org.springframework.web.servlet.HttpServletBean.init() > 重点是这里的 `initServletBean();`该方法由子类具体实现,这里依旧是一个空实现,运用了模板方法模式 @Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); //这是一个空方法 initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like. //该方法的实现由子类完成: initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } } #### FrameworkServlet.initServletBean() #### > 完整路径 org.springframework.web.servlet.FrameworkServlet.initServletBean() > 该方法覆盖重写了 父类 HttpServletBean 的方法 > 重点关注其中的 `this.webApplicationContext = initWebApplicationContext();` @Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //初始化 webApplicationContext this.webApplicationContext = initWebApplicationContext(); //一个空方法 initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } } #### FrameworkServlet.initWebApplicationContext() #### > 完整路径 org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext() > 为这个servlet 初始化并发布 WebApplicationContext 对象 > 尤其注意 `wac = createWebApplicationContext(rootContext);` protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { //WebApplicationContext 对象通过构造方法注入 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // WebApplicationContext 对象未被刷新 -> 提供类似于父上下文对象,设置应用的上下文对象的id if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // 是否通过构造器注入 -> see if one // 是否已经被注册到 servlet 上下文.如果存在,这表示任何父上下文已经初始化过并且制定了上下文对象的id wac = findWebApplicationContext(); } if (wac == null) { //如果这个 servlet 没有发现上下文对象,则新建一个 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; } [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Jlc3RjeHg_size_16_color_FFFFFF_t_70]: /images/20211013/6221b46edae44a2190e6bd9a82897121.png
还没有评论,来说两句吧...