Tomcat初始化

Tomcat初始化

maven依赖

	<dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>8.5.16</version>
    </dependency>

    <!-- tomcat对jsp支持 -->
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-jasper</artifactId>
      <version>8.5.16</version>
    </dependency> 

Tomcat启动类

package web.embedded.tomcat;

import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;

public class LjhTomcat {
    private static final String CONTEXTPATH = "/context";
    private static final String FILTERNAME = "ljhFilter";
    private static final String FILTERURL = "/*";
    private static final String SERVLETNAME = "ljhServlet";
    private static final String SERVLETURL = "/servlet";

    public static void main(String[] args) throws LifecycleException {
        Tomcat tomcat = new Tomcat();
        Service service = tomcat.getService();
        Connector connector = new Connector("HTTP/1.1");
        connector.setPort(8080);
        service.addConnector(connector);
        Engine engine = tomcat.getEngine();
        service.setContainer(engine);
        StandardHost host = new StandardHost();
        host.setName("localhost");
        engine.addChild(host);
        Context context = new StandardContext();
        context.setName("index");
        context.setPath(CONTEXTPATH);
        context.addLifecycleListener(new Tomcat.FixContextListener());
        host.addChild(context);
        FilterDef filterDef = new FilterDef();
        filterDef.addInitParameter("key", "value");
        filterDef.setFilterName(FILTERNAME);
        filterDef.setFilter(new LjhFilter());
        context.addFilterDef(filterDef);
        FilterMap filterMap = new FilterMap();
        filterMap.setFilterName(FILTERNAME);
        filterMap.addURLPattern(FILTERURL);
        context.addFilterMap(filterMap);
        context.addParameter("name", "value");
        Wrapper wrapper = new StandardWrapper();
        wrapper.addInitParameter("servletkey", "servletvalue");
        wrapper.setServlet(new LjhServlet());
        wrapper.setName(SERVLETNAME);
        context.addChild(wrapper);
        context.addServletMappingDecoded(SERVLETURL,SERVLETNAME);
        Set set = new HashSet();
        set.add("A");
        context.addServletContainerInitializer(new LjhServletContainerInitializer(),set);
        context.addApplicationListener("web.listener.MyServletContextListener");
        context.addApplicationListener("web.listener.MyServletContextAttributeListener");
        context.addApplicationListener("web.listener.MyServletRequestListener");
        context.addApplicationListener("web.listener.MyServletRequestAttributeListener");
        context.addApplicationListener("web.listener.MyHttpSessionListener");
        context.addApplicationListener("web.listener.MyHttpSessionAttributeListener");
        tomcat.start();
        tomcat.getServer().await();
    }
}

生命周期介绍
在这里插入图片描述
每一个组件都实现了Lifecycle接口,因此都符合上图中的生命周期流程。每一个组件运行前都需要先init>start,具体抽象实现在LifecycleBase的start()方法中。

Tomcat容器主要的几个组成部分
在这里插入图片描述
这几个组件并不是语法上的父子关系,只是因为子容器需要在指定的父容器内运行。Engine、Host、Context、Wrapper实现了容器接口,对应的组件实现类StandardEngine、StandardHost、StandardContext、StandardWrapper也都继承了Container的抽象实现类ContainerBase,ContainerBase中有一个Pipeline属性,主要是用来管理valve。以上的几个实现容器接口的组件都有对应的valve,分别是StandardEngineValve、StandardHostValve、StandardContextValve、StandardWrapperValve,用来完成对应组件负责的请求处理。我们常用的Filter、Servlet处理逻辑都在其中。

各组件的启动流程
在这里插入图片描述
StandardService的startInternal()方法如下:
在这里插入图片描述

StandardContext的startInternal()方法如下:

@Override
    protected synchronized void startInternal() throws LifecycleException {
		......
                // Notify our interested LifecycleListeners
                //这里就开始了事件通知,会将扫描到的ServletContainerInitializer实例和其上的@HandlesTypes注解配置的类包装到一个Map中,最终将这些类和ServletContext作为两个参数传入ServletContainerInitializer的onStartup方法。
                fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

                // Start our child containers, if not already started
                //先初始化启动StandardWrapper,Pipeline,StandardWrapperValve
                for (Container child : findChildren()) {
                    if (!child.getState().isAvailable()) {
                        child.start();
                    }
                }

                // Start the Valves in our pipeline (including the basic),
                // if any
                if (pipeline instanceof Lifecycle) {
                    ((Lifecycle) pipeline).start();
                }
				......
            // servletContext.setInitParameter(String name, String value)
            mergeParameters();

            // 执行ServletContainerInitializers接口的onStartup方法
            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;
                }
            }

            // 配置并且调用application event listeners
            if (ok) {
                if (!listenerStart()) {
                    log.error(sm.getString("standardContext.listenerFail"));
                    ok = false;
                }
            }
			......
            // 主要执行Filter的初始化工作
            if (ok) {
                if (!filterStart()) {
                    log.error(sm.getString("standardContext.filterFail"));
                    ok = false;
                }
            }

            // 加载StandardWrapper(Servlet),并初始化
            if (ok) {
                if (!loadOnStartup(findChildren())){
                    log.error(sm.getString("standardContext.servletFail"));
                    ok = false;
                }
            }

            // Start ContainerBackgroundProcessor thread
            super.threadStart();
        } finally {
            // Unbinding thread
            unbindThread(oldCCL);
        }
        ......
    }

ServletContainerInitializer接口:接口的初始化工作在其他监听器、过滤器、Servlet初始化之前完成。ServletContainerInitializer的xml形式的逻辑主要在ContextConfig中,他是由ContainerMBean类加入的。
在这里插入图片描述
可以看到最终将这个上下文配置类加入了StandardContext生命周期监听集合中。容器中调用的fireLifecycleEvent()方法会触发ContextConfig的lifecycleEvent方法。
ContextConfig的主要方法webConfig()代码如下:

protected void webConfig() {
......
        // Step 3. Look for ServletContainerInitializer implementations
        if (ok) {
        	//主要去META-INF/services/javax.servlet.ServletContainerInitializer文件中寻找ServletContainerInitializer的实现类,并且实例化出来(需要一个无参构造方法),
        	//同时还会获取其实现类上的@HandlesTypes注解配置的Class<?>[]
            processServletContainerInitializers();
        }
......
        // Step 11. Apply the ServletContainerInitializer config to the
        // context
        //最终加入到StandardContext的servlet容器初始化Map中
        if (ok) {
            for (Map.Entry<ServletContainerInitializer,
                    Set<Class<?>>> entry :
                        initializerClassMap.entrySet()) {
                if (entry.getValue().isEmpty()) {
                    context.addServletContainerInitializer(
                            entry.getKey(), null);
                } else {
                    context.addServletContainerInitializer(
                            entry.getKey(), entry.getValue());
                }
            }
        }
    }

关于ServletContainerInitializer的使用可以参考org.springframework.web.SpringServletContainerInitializer。

listenerStart():先将配置的Listener类名实例化,然后按接口类型ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionIdListener、HttpSessionAttributeListener归入事件监听器集合,ServletContextListener、HttpSessionListener归入生命周期监听器集合,最后执行ServletContextListener接口的contextInitialized()方法。

loadOnStartup:启动load-on-startup参数必须为整数,小于0代表启动时期不做初始化,默认为-1。正数的值越小,启动优先级越高。如果值相同,有hash值决定,主要通过hashmap.values().toArray(results)获取。

public boolean loadOnStartup(Container children[]) {

        // Collect "load on startup" servlets that need to be initialized
        TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
        for (int i = 0; i < children.length; i++) {
            Wrapper wrapper = (Wrapper) children[i];
            int loadOnStartup = wrapper.getLoadOnStartup();
            if (loadOnStartup < 0)
                continue;
            Integer key = Integer.valueOf(loadOnStartup);
            ArrayList<Wrapper> list = map.get(key);
            if (list == null) {
                list = new ArrayList<>();
                map.put(key, list);
            }
            list.add(wrapper);
        }

        // Load the collected "load on startup" servlets
        for (ArrayList<Wrapper> list : map.values()) {
            for (Wrapper wrapper : list) {
               ......
               //servlet.init(StandardWrapperFacade)
               wrapper.load();
               ......
        return true;
    }

设计模式
通知方法fireLifecycleEvent的代码如下:

protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        for (LifecycleListener listener : lifecycleListeners) {
            listener.lifecycleEvent(event);
        }
    }
public class EventObject implements java.io.Serializable {
    private static final long serialVersionUID = 5516075349620653480L;
    protected transient Object  source;
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");
        this.source = source;
    }
    public Object getSource() {
        return source;
    }
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}
public final class LifecycleEvent extends EventObject {
    private static final long serialVersionUID = 1L;
    private final Object data;
    private final String type;

    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
        super(lifecycle);
        this.type = type;
        this.data = data;
    }
   
    public Object getData() {
        return data;
    }
    public Lifecycle getLifecycle() {
        return (Lifecycle) getSource();
    }
    public String getType() {
        return this.type;
    }
}
public interface LifecycleListener {
    public void lifecycleEvent(LifecycleEvent event);
}
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值