5 tomcat容器-context

1、context容器是什么?

直观上面看,context容器就是tomcat的webapps下面的一个应用

java的web应用,你是不是一下就想到了servlet,filter,listener这三大神器。context容器就是管理它们的。

这篇博文就是了解context如何管理它们的

从功能是我们回顾一下

servlet:处理能匹配到的请求的类

fiter:针对指定请求必须经过的类

listener:发生特定时间通知到的类

2、Servlet管理

之前我们有讲过Wrapper容器就是Servlet的一个包装类。主要负责的就是实例化和调用service。

我们知道web.xml的Servlet配置有一个loadOnStartUp,如果这个参数配置大于1,就会在启动tomcat的时候就实例化这个Servlet,这个工作就是在Context中管理完成的。

StandardContext中的StartInternal有这么一段


if (ok) {
                if (!loadOnStartup(findChildren())){
                    log.error(sm.getString("standardContext.servletFail"));
                    ok = false;
                }
            }

public boolean loadOnStartup(Container children[]) {
     
        TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
        // children中保存的就是web.xml中配置的所有的Servlet对应的Wrapper容器
        // 这里就是遍历所有的容器,查看其loadOnStartUp
        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);
            }
            // 如果loadOnStartUp大于0,加入Map中
            list.add(wrapper);
        }

        // 遍历map,也就是所有符合启动就加载的Wrapper
        for (ArrayList<Wrapper> list : map.values()) {
            for (Wrapper wrapper : list) {
                try {
                    // 实例化Wrapper中的Servlet
                    wrapper.load();
                } 
            }
        }
        return true;

    }

Context在启动的时候就会找到所有满足启动需要加载的Servlet,也就是Wrapper中配置的loadOnStartup参数大于0,实例化这个Servlet。

3 Filter管理

在Wrapper中涉及到Filter的业务操作,但是我并没有选择在那一篇中讲解Filter,毕竟真正管理Filter的是Context容器。

我们看看Context容器是怎么管理Filter

下面代码是StandardContext启动器中的一段

 if (ok) {
                if (!filterStart()) {
                    log.error(sm.getString("standardContext.filterFail"));
                    ok = false;
                }
            }


synchronized (filterConfigs) {
            filterConfigs.clear();
            // filterDefs在解析web.xml的时候生成
            for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
                String name = entry.getKey();
                
                // 创建一个FilterConfig
                    ApplicationFilterConfig filterConfig =
                            new ApplicationFilterConfig(this, entry.getValue());
                    filterConfigs.put(name, filterConfig);
            }
        }

这里创建了FilterConfig,我们来看看filterConfig是些什么内容

比如web.xml中配置了一个名为filter1的过滤器,全限定名为com.yanlink.filter.Filter1,匹配模式为/hello,配置如下

<filter>
    <filter-name>filter1</filter-name>
    <filter-class>com.yanlink.filter.Filter1</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>filter1</filter-name>
    <url-pattern>/hello</url-pattern>
  </filter-mapping>

先看看filterDef保存的什么

filterDef{

        filterClass=com.yanlink.filter.Filter1

        filterName=filter1

}

再看看filterConfig

filterConfig{

context:当前容器对象

filterDef:上面的对象

filter:对应的filter对象

}

我们还是来看看new ApplicationFilterConfig做了些什么

ApplicationFilterConfig(Context context, FilterDef filterDef)
        throws ClassCastException, ClassNotFoundException,
               IllegalAccessException, InstantiationException,
               ServletException, InvocationTargetException, NamingException {

        super();

        // 保存容器上下文
        this.context = context;
        // 保存过滤器定义
        this.filterDef = filterDef;
        
        if (filterDef.getFilter() == null) {
        // 创建过滤器
            getFilter();
        } else {
            this.filter = filterDef.getFilter();
            getInstanceManager().newInstance(filter);
            initFilter();
        }
    }

Filter getFilter() throws ClassCastException, ClassNotFoundException,
        IllegalAccessException, InstantiationException, ServletException,
        InvocationTargetException, NamingException {

        // Return the existing filter instance, if any
        if (this.filter != null)
            return (this.filter);

        // 获取类的全限定名
        String filterClass = filterDef.getFilterClass();
        // 实例化过滤器对象
        this.filter = (Filter) getInstanceManager().newInstance(filterClass);
        // 初始化过滤器对象也就是调用filter的init
        initFilter();
        // 
        return (this.filter);

    }

我们再来看看filter在Wrapper容器中的使用

public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {

        // 必须需要一个servlet,没有servlet的过滤器没意义
        if (servlet == null)
            return null;

        // Create and initialize a filter chain object
            filterChain = new ApplicationFilterChain();
            req.setFilterChain(filterChain);
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);

        // 获取wrapper对应的Context容器
        StandardContext context = (StandardContext) wrapper.getParent();
        // 上下文中获取filterMap,这个是用于filter与请求的uri匹配的,找到合适的filter
        FilterMap filterMaps[] = context.findFilterMaps();
    
        String servletName = wrapper.getName();

        // 过滤器匹配
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            // 在context中根据过滤器的名字找到对应的filterConfig
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
             // 找到filterConfig加入到过滤器链
            filterChain.addFilter(filterConfig);
        }

        // Return the completed filter chain
        return filterChain;
    }

context中创建的filterConfig在这里就有用到,上面的代码是由工厂方法创建了一个FilterChain,这个filterChain包含了一个servlet和一个filter数组。

servlet是必须有的,没有目标的servlet的过滤器链是没有意义的,所有如果servlet为null,返回null

从context中找一个filterMap,字面意思这是个映射器,我们看看保存的是什么

filterMap{

filterName=filter1,

urlPatterns:["/hello"]

}

从数据结构可以看出来,这个映射器是通过请求的uri来匹配对应的filter名称

然后在context中找到之前配置的filterConfigs,根据filterName匹配到对应的filterConfig,加入filterChain中

接下来看看filterChain的调用

public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

        internalDoFilter(request,response);
    }

private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // 
        if (pos < n) {
            // 从过这个pos定位执行到的filter
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();

                // 并且把自己的对象传过去,在这里为什么没有for,关键点在于传入的这个this
                    filter.doFilter(request, response, this);
                
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        try {
            // 过滤器执行完毕再执行service
                servlet.service(request, response);
            }
    }

看完这个大家是不是存在一些疑惑,没有for循环,怎么执行完所有的filter的。我们结合下应用使用filter的代码

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("filter1 dofilter");
        // 这里通过chain再次调用了doFilter
        chain.doFilter(request, response);
    }

看到chain.doFilter没,这个chain就是上面的this,也就是把自己这个对象传过去,应用的时候需要靠开发人员加入这个chain.doFilter如果没有则访问不到servlet,宏观层面看看,这个是不是和pipeline一样还是一个责任链的模式,只是使用了另一种实现方式

4、Listener管理

这里的listener我针对的是ServletContextListener ,我们来看看其接口

public interface ServletContextListener extends EventListener {
	/**
	 ** Notification that the web application initialization
	 ** process is starting.
	 ** All ServletContextListeners are notified of context
	 ** initialization before any filter or servlet in the web
	 ** application is initialized.
	 */

    public void contextInitialized ( ServletContextEvent sce );

	/**
	 ** Notification that the servlet context is about to be shut down.
	 ** All servlets and filters have been destroy()ed before any
	 ** ServletContextListeners are notified of context
	 ** destruction.
	 */
    public void contextDestroyed ( ServletContextEvent sce );
}

我们来看看关于监听器的代码

public boolean listenerStart() {
        // Instantiate the required listeners
        String listeners[] = findApplicationListeners();
        Object results[] = new Object[listeners.length];
        boolean ok = true;
        for (int i = 0; i < results.length; i++) {
                // listener是监听器的全限定名
                String listener = listeners[i];
                // 实例化listener
                results[i] = getInstanceManager().newInstance(listener);
        }
    
        ArrayList<Object> eventListeners = new ArrayList<>();
        ArrayList<Object> lifecycleListeners = new ArrayList<>();
        for (int i = 0; i < results.length; i++) {
           // 加入lifecycleListeners
           lifecycleListeners.add(results[i]);
        }
      
        
        getServletContext();
        // 获取当前关注容器状态的监听者
        Object instances[] = getApplicationLifecycleListeners();
        if (instances == null || instances.length == 0) {
            return ok;
        }
        // 创建事件,也就是把context对象带过去,这里是一个外观类
        ServletContextEvent event = new ServletContextEvent(getServletContext());
      
            ServletContextListener listener = (ServletContextListener) instances[i];
            // 执行listener的容器初始化
            listener.contextInitialized(event);
        return (ok);

    }

这里也就是一个典型的观察者模式,容器中注册 了所有的对context当前状态感兴趣的对象,context在启动和销毁的时候会给这些对象发送通知。

我们看看ServletContextListener接口中的参数ServletContextEvent ,在tomcat中创建这个事件的时候传入了一个容器对象,这个容器对象并不是this,而是一个外观类。这样保护了context类,免得一些不安全的方法开放给了开发者。

5、基础阀

再看看context与wrapper管道对接的基础阀StandardContextValve

// Disallow any direct access to resources under WEB-INF or META-INF
     
        Wrapper wrapper = request.getWrapper();
        if (wrapper == null || wrapper.isUnavailable()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        wrapper.getPipeline().getFirst().invoke(request, response);

这块逻辑还是相对比较简单的,找到Wrapper容器,调用Wrapper的管道的第一个阀的invoke,这里找到Wrapper是在request中实现的,我们可以大概猜测一下,Wrapper容器中是有Name的这个name也就是,我们在请求的uri中去掉ContextPath部分,剩下的部分做wrapper匹配,找到匹配度最高的Wrapper。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值