11.2、Spring源码学习 ——SpringMVC 之 DispatcherServet 的 init()方法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/bestcxx/article/details/99703232

前言

体能状态先于精神状态,习惯先于决心,聚焦先于喜好。

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 都是抽象类,它俩都有一些方法留给子类来实现

在这里插入图片描述

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;
	}

猜你喜欢

转载自blog.csdn.net/bestcxx/article/details/99703232