Tomcat的组织架构及启动原理

Tomca组件结构

tomcat.png

connector:主要负责接受浏览器发送过来的tcp连接请求,创建一个request和response

container:接受request和response,并开启线程处理请求封装结果

service:结合connector和container,对外提供接口服务,一个service可以有多个connector(多种连接协议)和一个container(engine,可以理解为servlet容器)

server:为service提供生存环境,负责他的生命周期,它可以包含多个service服务

Tomcat容器结构

Tomcat.png

Engine:servlect的顶层容器,包含一个或多个Host子容器;
Host:虚拟主机,负责web应用的部署和context的创建
Context:web应用上下文,包含多个wrapper,负责web配置的解析、管理所有的web资源
Wrapper:最底层的容器,是对servlet的封装,负责servlet实例的创建、执行、销毁

Tomcat启动过程

Tomcat启动时,执行启动脚本startup.sh->catalina.sh->Bootstrap->Bootstrap.main方法

    public static void main(String args[]) {
      // .... 
      if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                    // 1)如果daemon是空的话,先执行init方法
                    bootstrap.init();
                } catch (Throwable t) {
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                daemon = bootstrap;
            }
      // ....
      
      // 2)不同命令会调用不同daemon的不同方法,daemon是Bootstrap
      String command = "start";
      if (args.length > 0) {
        command = args[args.length - 1];
      }

      if (command.equals("startd")) {
        args[args.length - 1] = "start";
        daemon.load(args);
        daemon.start();
      } else if (command.equals("stopd")) {
        args[args.length - 1] = "stop";
        daemon.stop();
      } else if (command.equals("start")) {
        daemon.setAwait(true);
        // 3)若命令是start,调用Boostrap的load方法和start方法
        daemon.load(args);
        daemon.start();
        if (null == daemon.getServer()) {
          System.exit(1);
        }
      } else if (command.equals("stop")) {
        daemon.stopServer(args);
      } else if (command.equals("configtest")) {
        daemon.load(args);
        if (null == daemon.getServer()) {
          System.exit(1);
        }
        System.exit(0);
      } else {
        log.warn("Bootstrap: command \"" + command + "\" does not exist.");
      }
      // ...
    }

    private void load(String[] arguments) throws Exception {
        // Call the load() method
        String methodName = "load";
        Object param[];
        Class<?> paramTypes[];
        if (arguments==null || arguments.length==0) {
            paramTypes = null;
            param = null;
        } else {
            paramTypes = new Class[1];
            paramTypes[0] = arguments.getClass();
            param = new Object[1];
            param[0] = arguments;
        }
        // 4)此处通过反射调用,catalinaDaemon的load方法,catalinaDaemon是啥呢?
        Method method =
            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled()) {
            log.debug("Calling startup class " + method);
        }
        method.invoke(catalinaDaemon, param);
    }

// 1.1) Boostrap的init方法会在main中先执行,由此可知catalinaDaemon就是Catalina类
    public void init() throws Exception {
        // ... 
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.getConstructor().newInstance();
        // ...
        catalinaDaemon = startupInstance;
    }

    public void start() throws Exception {
        if (catalinaDaemon == null) {
            init();
        }

        Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
        method.invoke(catalinaDaemon, (Object [])null);
    }

1. Catalina.load()

所以最后Bootstrap.main()->Catalina.load(),load方法中主要做了2个事情:

  1. 创建Digester类,解析conf/server.xml文件
  2. 调用server.init()进行初始化server
  protected String configFile = "conf/server.xml";  
  protected File configFile() {
    File file = new File(configFile);
    if (!file.isAbsolute()) {
      file = new File(Bootstrap.getCatalinaBase(), configFile);
    }
    return file;
  }
    public void load() {
        // 1)Create and execute our Digester
        Digester digester = createStartDigester();

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
          // 1.1)创建文件
          file = configFile();
          inputStream = new FileInputStream(file);
          inputSource = new InputSource(file.toURI().toURL().toString());
          // ... 
          inputSource.setByteStream(inputStream);
          digester.push(this);
          // 1.2) 解析文件:conf/server.xml
          digester.parse(inputSource);
          // ...
        } catch (Exception e) {
          log.warn("Catalina.start using " + getConfigFile() + ": " , e);
          return;
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
                // ... 
      
        // 2) Start the new server
        try {
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }
        }
                // ... 
    }

getServer.init()->LiftcycleBase.init():

  1. 更新server的LifecycleState,并发布状态变化事件
  2. 调用StandServer.initInternal()进行server的初始化
public abstract class LifecycleBase implements Lifecycle {
        @Override
    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }
        try {
            // 1) 设置状态为initalizing,并发布状态变化的事件
            // LifecycleState有12种状态,每种状态都有对应的Event
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            // 2)初始化
            initInternal();
            // 3)设置状态为initialized,并发布状态变化事件
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            handleSubClassException(t, "lifecycleBase.initFail", toString());
        }
    }
  
    private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)
            throws LifecycleException {
                // ...
        // 1)设置新状态
        this.state = state;
        // 2)获取状态的事件并发布
        String lifecycleEvent = state.getLifecycleEvent();
        if (lifecycleEvent != null) {
            fireLifecycleEvent(lifecycleEvent, data);
        }
    }
    protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        // 循环监听:lifecycleListeners并发布事件LifecycleEvent ??监听器有哪些?如何初始化的?
        for (LifecycleListener listener : lifecycleListeners) {
            listener.lifecycleEvent(event);
        }
    }
}

public enum LifecycleState {
    NEW(false, null),
    INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
    INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
    STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
    STARTING(true, Lifecycle.START_EVENT),
    STARTED(true, Lifecycle.AFTER_START_EVENT),
    STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
    STOPPING(false, Lifecycle.STOP_EVENT),
    STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
    DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
    DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
    FAILED(false, null);

    private final boolean available;
    private final String lifecycleEvent;
}


image-20211106154522867.png

    protected void initInternal() throws LifecycleException {
        super.initInternal();
        // Initialize our defined Services
        for (Service service : services) {
            service.init();
        }
    }

StandServer.initInternal()->LeftcycelBase.init()->StandService.initeInternal(),standService.initernel()主要实现:

  1. 初始化engine
  2. 初始化connectors
    protected void initInternal() throws LifecycleException {
        super.initInternal();
                // 1) 初始化engine
        if (engine != null) {
            engine.init();
        }
                // ... 
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                try {
                    // 2)初始化connectors
                    connector.init();
                } catch (Exception e) {
                    // ...
                }
            }
        }
    }

初始化engine.init()->LifecycleBase.init()->StandardEngine.initernal()

初始化connetor.init()->LiftcycleBase.init()->Connector.initernal()->ProtocolHandler.init()

2. Catalina.start()

    public void start() {
                // ... 
        long t1 = System.nanoTime();

        // 1) Start the new server
        try {
            getServer().start();
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }
      
        // 2) Register shutdown hook
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);

            // If JULI is being used, disable JULI's shutdown hook since
            // shutdown hooks run in parallel and log messages may be lost
            // if JULI's hook completes before the CatalinaShutdownHook()
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }

        if (await) {
            await();
            stop();
        }
    }

Catalina.start()->LifecycleBase.start()->StandardServer.startIniternal()

    @Override
    protected void startInternal() throws LifecycleException {
                // 1)更新server状态为starting,并发布状态变更事件
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);

        globalNamingResources.start();

        // 2)初始化service 
        synchronized (servicesLock) {
            for (Service service : services) {
                service.start();
            }
        }
    }

StandardServer.startIniternal()->LifecycleBase.start()->StandardService.startIniternal()

    protected void startInternal() throws LifecycleException {
        // 1) Start our defined Container first
        if (engine != null) {
            synchronized (engine) {
                engine.start();
            }
        }
        // ... 
        // 2) Start our defined Connectors second
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                try {
                    // If it has already failed, don't try and start it
                    if (connector.getState() != LifecycleState.FAILED) {
                        connector.start();
                    }
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
        }
    }

1)engine.start()->LifecycleBase.start()->engine.startInternal()->ContainerBase.startInternal()

     protected synchronized void startInternal() throws LifecycleException {
                // ... 
        // 1)启动子容器们的任务提交到线程池中
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        for (Container child : children) {
            results.add(startStopExecutor.submit(new StartChild(child)));
        }

        MultiThrowable multiThrowable = null;
                // 2)采用异步获取结果的方式,启动容器的结果
        for (Future<Void> result : results) {
            try {
                result.get();
            } catch (Throwable e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                if (multiThrowable == null) {
                    multiThrowable = new MultiThrowable();
                }
                multiThrowable.add(e);
            }

        }
        // ... 
        // Start our thread
        threadStart();
    }

StandardEngine的子容器是Host,所以:ContainerBase.startInternal()->LifecycleBase.start()->发布事件->HostConfig监听此事件

    public void lifecycleEvent(LifecycleEvent event) {
        // ...
        if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
            check();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.START_EVENT)) {
            // 调用这个方法
            start();
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            stop();
        }
    }

    public void start() {
                // 发布项目
        if (host.getDeployOnStartup()) {
            deployApps();
        }
    }

    protected void deployApps() {
        File appBase = host.getAppBaseFile();
        File configBase = host.getConfigBaseFile();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase
        deployDescriptors(configBase, configBase.list());
        // 发布war包项目
        deployWARs(appBase, filteredAppPaths);
        // Deploy expanded folders
        deployDirectories(appBase, filteredAppPaths);
    }

    protected void deployWARs(File appBase, String[] files) {
                // ...
        for (String file : files) {
            // ...
            File war = new File(appBase, file);
            if (file.toLowerCase(Locale.ENGLISH).endsWith(".war") && war.isFile() && !invalidWars.contains(file)) {
                ContextName cn = new ContextName(file, true);
                if (tryAddServiced(cn.getName())) {
                    try {
                        // ...  
                        // 发布war的任务提交到线程池中
                        results.add(es.submit(new DeployWar(this, cn, war)));
                    } catch (Throwable t) {
                        ExceptionUtils.handleThrowable(t);
                        removeServiced(cn.getName());
                        throw t;
                    }
                }
            }
        }
                // 异步获取发布结果
        for (Future<?> result : results) {
            try {
                result.get();
            } catch (Exception e) {
                log.error(sm.getString("hostConfig.deployWar.threaded.error"), e);
            }
        }
    }

    private static class DeployWar implements Runnable {
                // ... 
        @Override
        public void run() {
            // 发布单个war
            config.deployWAR(cn, war);
        }
    }

    protected void deployWAR(ContextName cn, File war) {
                // ... 
        Context context = null;
        boolean deployThisXML = isDeployThisXML(war, cn);
        try {
            // ...
            context.addLifecycleListener(listener);
            context.setName(cn.getName());
            context.setPath(cn.getPath());
            context.setWebappVersion(cn.getVersion());
            context.setDocBase(cn.getBaseName() + ".war");
            // 添加StandardContext到当前Host中
            host.addChild(context);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString("hostConfig.deployWar.error", war.getAbsolutePath()), t);
        } finally {
            // ... 
        }
        // ...
    }

到这里StandardContext算是创建了,并且是Host的子容器,然后回到LifecycleBase.start(),执行startInternal()->StandardHaost.startInternal()->ContainerBase.startInternal(),最终又回到父类ContainerBase的startIniternal()方法中,重复执行StandardContext的事件监听ContextConfig和startInternal():

  1. ContextConfig的事件监听中,会加载项目的web.xml文件

        @Override
        public void lifecycleEvent(LifecycleEvent event) {
             // ... 
            // Process the event that has occurred
            if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
                // 加载web.xml文件
                 configureStart();
            } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
                beforeStart();
            } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
                // Restore docBase for management tools
                if (originalDocBase != null) {
                    context.setDocBase(originalDocBase);
                }
            } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
                configureStop();
            } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
                init();
            } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
                destroy();
            }
        }
        protected synchronized void configureStart() {
            // ...
            webConfig();
                 // ... 
        }
         // 为了简单理解,省略了很多步骤的代码
        protected void webConfig() {
            // ...
            WebXml webXml = createWebXml();
             // ...
            if (!webXml.isMetadataComplete()) {
                // ... 
                // Step 9. Apply merged web.xml to Context
                if (ok) {
                    configureContext(webXml);
                }
            } 
            // ...
        }
         // 解析web.xml配置的信息
        private void configureContext(WebXml webxml) {
            // ...
             // 加载Filter们
            for (FilterDef filter : webxml.getFilters().values()) {
                if (filter.getAsyncSupported() == null) {
                    filter.setAsyncSupported("false");
                }
                context.addFilterDef(filter);
            }
            for (FilterMap filterMap : webxml.getFilterMappings()) {
                context.addFilterMap(filterMap);
            }
            // ...
             // 加载Servlet们
            for (ServletDef servlet : webxml.getServlets().values()) {
                Wrapper wrapper = context.createWrapper();
                // ... 
                wrapper.setOverridable(servlet.isOverridable());
                // 添加到StandardContext的child中,且之后在startInternal()中初始化
                 context.addChild(wrapper);
            }
            for (Entry<String, String> entry :
                    webxml.getServletMappings().entrySet()) {
                context.addServletMappingDecoded(entry.getKey(), entry.getValue());
            }
                 // 加载默认页面
            for (String welcomeFile : webxml.getWelcomeFiles()) {
                if (welcomeFile != null && welcomeFile.length() > 0) {
                    context.addWelcomeFile(welcomeFile);
                }
            }
                 // ...
        }
    
  1. 进入StandardContext.startIniternal()中

        protected synchronized void startInternal() throws LifecycleException {
          // ...
          // Call ServletContainerInitializers,调用ServletContainerInitializers的方法
          for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
               initializers.entrySet()) {
            try {
              entry.getKey().onStartup(entry.getValue(), getServletContext());
            } catch (ServletException e) {
              log.error(sm.getString("standardContext.sciFail"), e);
              ok = false;
              break;
            }
          }
          // 运行项目中配置的监听器方法
          if (ok) {
            if (!listenerStart()) {
              log.error(sm.getString("standardContext.listenerFail"));
              ok = false;
            }
          }
          // ... 
          // 初始化servlet们
          if (ok) {
            if (!loadOnStartup(findChildren())){
              log.error(sm.getString("standardContext.servletFail"));
              ok = false;
            }
          }
        }
        public boolean listenerStart() {
                 // ... 
            for (Object instance : instances) {
                if (!(instance instanceof ServletContextListener)) {
                    continue;
                }
                ServletContextListener listener = (ServletContextListener) instance;
                try {
                    fireContainerEvent("beforeContextInitialized", listener);
                    if (noPluggabilityListeners.contains(listener)) {
                        // 应用上下文监听
                         listener.contextInitialized(tldEvent);
                    } else {
                        listener.contextInitialized(event);
                    }
                    fireContainerEvent("afterContextInitialized", listener);
                } catch (Throwable t) {
                    // ...
                }
            }
            return ok;
        }
        public boolean loadOnStartup(Container children[]) {
             // ...
            for (ArrayList<Wrapper> list : map.values()) {
                for (Wrapper wrapper : list) {
                    try {
                         // 循环加载servlets
                        wrapper.load();
                    } catch (ServletException e) {
                     // ...
                    }
                }
            }
            return true;
        }
    

    监听:这个监听就是web.xml中配置的,就是启动spring容器的入口

    <!-- listener -->
    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    

    初始化servlet:StandardContext.loadOnStartup()->StandardWrapper.load()

    @Override
    public synchronized void load() throws ServletException {
        instance = loadServlet();
        if (!instanceInitialized) {
            initServlet(instance);
        }
         // ...
    }
    
        private synchronized void initServlet(Servlet servlet)
                throws ServletException {
          // ... 
             servlet.init(facade);
          // ...
        }
    

    最后调用的是:GenericServlet.init()

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    

    GenericServlet.init()->HttpServletBean.init()->FramworkServlet.initServletBean(),此时已经进入spring-webmvc.jar

    public final void init() throws ServletException {
        // ...
        this.initServletBean();
         // ...
    }
    
        protected final void initServletBean() throws ServletException {
            // ...
            try {
                // 初始化spring容器
                this.webApplicationContext = this.initWebApplicationContext();
                this.initFrameworkServlet();
            } // ...
        }
        protected WebApplicationContext initWebApplicationContext() {
            // ...
            if (!this.refreshEventReceived) {
                 // 此处就是启动DispatcherServlet七大组件的地方!!!
                this.onRefresh(wac);
            }
             // ...
            return wac;
        }
    

    最后的最后进入:DispatcherServlet.onRefresh()

    protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }
    protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }
    

最后附上一张DispatcherSerlvet的类图

2)还有个Connector的初始化:connector.start()->LifecycleBase.start()->Connector.startIniternal()->protocalHandler.start()

    protected void startInternal() throws LifecycleException {

        // Validate settings before starting
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }

        setState(LifecycleState.STARTING);

        try {
            protocolHandler.start();
        } catch (Exception e) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }

总结一下子:

  1. Tomcat的启动
    • 启动脚本startup.sh,最终执行Bootstrap.main(),且会把命令(start、stop等)作为参数传入,不同参数会调用不同的方法

    • 当命令是start时,Bootstrap.main()会调用Catalina.load()Catalina.start()

  2. Tomcat组件的加载:load()
    • 在Catalina.load()中,解析conf/server.xml,最后调用getServer.init(),对server初始化
    1. 调用getServer.init(),会进入LifecycleBase.init(),这个方法中会先这是server状态为initializing且发布事件,再调用StandardServer.initInternal()
    2. 在StandardServer.initInternal()中,会循环调用service.init()方法,会先进入LifecycleBase.init(),然后进入StandardService.initInternal()
    3. 在StandardService.initInternal()中会初始化engineconnectors
  3. Tomcat容器的加载:start()
    • Catalina.start(),最后调用getServer.start(),进入LifecycleBase.start(),最后进入StandardServer.startIniternal()
    1. StandardServer.startIniternal()中,会启动engine.start()和connector.start()
    2. 在engine.start()中,调用ContainerBase.startIniternal(),会将子容器Host的启动任务提交到线程池中,Host容器启动过程:
      • 先发布事件,且HostConfig监听到这个事件,在HostConfig中会发布web项目,且创建当前web项目的StandardContext,然后把这个StandardContext添加到Host的子容器中
      • 然后执行StandardHost.startIniternal(),这个方法中又回到父类ContainerBase.startIniternal(),然后重复提交StandardContext的启动任务到线程池中
    3. StandardContext的启动过程
      • 发布事件,ContextConfig监听到这个事件,ContextConfig中加载并解析项目的web.xml文件,初始化配置的Listener、Filter、Servlet等信息,servlet会解析成Wrapper然后添加到Context的子容器中
      • 然后调用StandardContext.startIniternal(),启动Context,这里会调用web.xml配置的监听contextInitialized(),spring容器在这里得到创建
      • 在StandardContext.startIniternal()中,然后加载Servlet,最终会调用spring-webmvc.jar中DispatcherSerlvet.onRefresh()加载Serlvet的七大组件

再来个简单版的总结

Tomcat启动分成2个部分:

  1. 组件的初始化
  2. 容器的启动

其中组件包括:Server->Services->Engine->Connectors
容器包括:Engine->StandardHost->StandardContext->StandardWrapper

web项目的加载、Spring容器的初始化入口、Servlet容器的加载都在第二步,也就是Catalina.start()中

  • web项目的加载:在启动Host容器前发布的事件,也就是监听HostConfig.start()中,并且创建子容器StandardContext
  • Spring容器的初始化入口:在StandardContext启动过程中执行了web.xml中配置的ServletContextListener的实现类的contextInitialized()
  • Servlet容器的加载:在StandardContext启动过程中执行的loadOnStartup(),最终调用DispatcherServlet.onRefresh(),初始化了Servlet的七大组件

留个大疑问

上文中的ServletContainerInitializer的作用是啥?有什么应用?可以怎么拓展?

作用:它是Servlet3.0提供的接口,只有一个方法,主要作用替代web.xml,通过初始化自定义的Listener、Filter、Servlet等信息

public interface ServletContainerInitializer {
    /**
     * @param c     启动时初始化的类
     * @param ctx web应用的servlet上下文
     */
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

应用:spring-web.jar中,SpringServletContainerInitializer实现了此接口

// 1)通过此注解将WebApplicationInitializer类,添加到onStartup方法的第一个参数中
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }

    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList();
        Iterator var4;
        if (webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class<?> waiClass = (Class)var4.next();
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        // 2)创建WebApplicationInitializer对象
                        initializers.add((WebApplicationInitializer)waiClass.newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            var4 = initializers.iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                // 3)调用WebApplicationInitializer的方法初始化
                initializer.onStartup(servletContext);
            }

        }
    }
}

SpringServletContainerInitializer在Tomcat启动时,容器初始化阶段初始化StandardContext时,被调用:

    protected synchronized void startInternal() throws LifecycleException {
      // Call ServletContainerInitializers
      for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializers.entrySet()) {
        try {
          // 循环多个ServletContainerInitializer,并调用onStartup()
          entry.getKey().onStartup(entry.getValue(), getServletContext());
        } catch (ServletException e) {
          log.error(sm.getString("standardContext.sciFail"), e);
          ok = false;
          break;
        }
      }
    }

SpringServletContainerInitializer是如何加载到的?毕竟在第三方jar包中,也是在Tomcat启动初始化容器StandardContext之前,发布的事件,在事件监听ContextConfig中加载的:(方法链路:lifecycleEvent()->configureStart()->webConfig()->processServletContainerInitializers()->WebappServiceLoader.load()),spring-web.jar的classpath/META-INF/services/路径下配置了需要初始化的ServletContianerInitializer,由Tomcat的ContextConfig加载初始化,在StandardContext中调用

    protected void processServletContainerInitializers(){   
      // 1) 指定类型为ServletContainerInitializer
      WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
      detectedScis = loader.load(ServletContainerInitializer.class);    
      // ...
      }
    
        // 2)加载META-INF/services/路径下
        private static final String SERVICES = "META-INF/services/";
        public List<T> load(Class<T> serviceType) throws IOException {
    String configFile = SERVICES + serviceType.getName();
        // ...
    }

自定义:ServletContainerInitializer

//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
//传入感兴趣的类型;
@HandlesTypes(value={HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {

    /**
     * 应用启动的时候,会运行onStartup方法;
     * 
     * Set<Class<?>> arg0:感兴趣的类型的所有子类型;
     * ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
     * 
     * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
     * 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
     *      必须在项目启动的时候来添加;
     *      1)、ServletContainerInitializer得到的ServletContext;
     *      2)、ServletContextListener得到的ServletContext;
     */
    @Override
    public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
        // TODO Auto-generated method stub
        System.out.println("感兴趣的类型:");
        for (Class<?> claz : arg0) {
            System.out.println(claz);
        }
        
        //注册组件  ServletRegistration  
        ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
        //配置servlet的映射信息
        servlet.addMapping("/user");
        
        
        //注册Listener
        sc.addListener(UserListener.class);
        
        //注册Filter  FilterRegistration
        FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
        //配置Filter的映射信息
        filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
        
    }

}

参考:

😈如果有错误请大家指正...

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容