Tomcat学习笔记(8)- Mapper(映射器)与 JNDI

1. 请求的模型映射

Mapper组件主要的职责是负责Tomcat的请求路由,每个客户端的请求到达Tomcat后,都将由Mapper路由到对应的处理逻辑(Servlet)上。
在Tomcat的结构中有两部分会包含Mapper组件,一个是Connector组件,称为全局路由Mapper;另外一个是Context组件,称为局部路由Mapper。本章将深入探讨Tomcat的路由模块Mapper组件。

从浏览器输入一个地址发送请求给Tomcat服务器,是否可以从请求链接分析出对应的层级?很明显是可以的。

在这里插入图片描述
对应配置如下

  <Host name="www.baidu.com"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
            <Context path="/web1" docBase="/xxx"/>
   ....         

2. Mapper的实现

Mapper组件的核心功能是提供请求路径的路由映射,根据某个请求路径,通过计算得到相应的Servlet(Wrapper)。

以Mapper作为映射的入口,按照容器等级,首先Mapper组件会包含了N个Host容器的引用,然后每个Host会有N个Context容器的引用,最后每个Context容器包含N个Wrapper容器的引用。例如,如果使用Mapper组件查找tomcat.apache. org/tomcat-7.0-doc/search,它首先会匹配名为tomcat.apache.org的Host,然后从中继续匹配名为tomcat-7.0-doc的Context,最后匹配名为search的Wrapper(Servlet)。

public final class Mapper {


    // ----------------------------------------------------- Instance Variables


    /**
    包含虚拟主机定义的数组。
     */
    //包私有,以方便测试
    volatile MappedHost[] hosts = new MappedHost[0];


    /**
    *默认主机名。
     */
    private String defaultHostName = null;
    private volatile MappedHost defaultHost = null;


    /**
    *支持从上下文对象到上下文版本的映射
	* RequestDispatcher映射。
     */
    private final Map<Context, ContextVersion> contextObjectToContextVersionMap =
            new ConcurrentHashMap<>();

其中MapElement提供一个基础的键-值对模型,name为容器的名称,object为具体的容器。

  protected abstract static class MapElement<T> {

        public final String name;
        public final T object;

        public MapElement(String name, T object) {
            this.name = name;
            this.object = object;
        }
    }

MappedHost 提供Host映射模型,它继承MapElement,且包含若干Context映射。

    protected static final class MappedHost extends MapElement<Host> {

        public volatile ContextList contextList;

        /**
         * Link to the "real" MappedHost, shared by all aliases.
         */
        private final MappedHost realHost;

        /**
         * Links to all registered aliases, for easy enumeration. This field
         * is available only in the "real" MappedHost. In an alias this field
         * is <code>null</code>.
         */
        private final List<MappedHost> aliases;

        /**
         * Constructor used for the primary Host
         *
         * @param name The name of the virtual host
         * @param host The host
         */
        public MappedHost(String name, Host host) {
            super(name, host);
            realHost = this;
            contextList = new ContextList();
            aliases = new CopyOnWriteArrayList<>();
        }

        /**
         * Constructor used for an Alias
         *
         * @param alias    The alias of the virtual host
         * @param realHost The host the alias points to
         */
        public MappedHost(String alias, MappedHost realHost) {
            super(alias, realHost.object);
            this.realHost = realHost;
            this.contextList = realHost.contextList;
            this.aliases = null;
        }

        public boolean isAlias() {
            return realHost != this;
        }

        public MappedHost getRealHost() {
            return realHost;
        }

        public String getRealHostName() {
            return realHost.name;
        }

        public Collection<MappedHost> getAliases() {
            return aliases;
        }

        public void addAlias(MappedHost alias) {
            aliases.add(alias);
        }

        public void addAliases(Collection<? extends MappedHost> c) {
            aliases.addAll(c);
        }

        public void removeAlias(MappedHost alias) {
            aliases.remove(alias);
        }
    }

MappedContext 提供Context映射模型,它继承MapElement,且包含不同类型的Wrapper(Servlet):默认Servlet、精确匹配Servlet、通配符Servlet和扩展Servlet。除此之外,还有欢迎页资源和path。

   protected static final class MappedContext extends MapElement<Void> {
        public volatile ContextVersion[] versions;

        public MappedContext(String name, ContextVersion firstVersion) {
            super(name, null);
            this.versions = new ContextVersion[] { firstVersion };
        }
    }
      protected static final class ContextVersion extends MapElement<Context> {
        public final String path;
        public final int slashCount;
        public final WebResourceRoot resources;
        public String[] welcomeResources;
        public MappedWrapper defaultWrapper = null;
        public MappedWrapper[] exactWrappers = new MappedWrapper[0];
        public MappedWrapper[] wildcardWrappers = new MappedWrapper[0];
        public MappedWrapper[] extensionWrappers = new MappedWrapper[0];
        public int nesting = 0;
        private volatile boolean paused;

        public ContextVersion(String version, String path, int slashCount,
                Context context, WebResourceRoot resources,
                String[] welcomeResources) {
            super(version, context);
            this.path = path;
            this.slashCount = slashCount;
            this.resources = resources;
            this.welcomeResources = welcomeResources;
        }

        public boolean isPaused() {
            return paused;
        }

        public void markPaused() {
            paused = true;
        }
    }

MappedWrapper 提供Wrapper映射模型,它继承MapElement。

 protected static class MappedWrapper extends MapElement<Wrapper> {

        public final boolean jspWildCard;
        public final boolean resourceOnly;

        public MappedWrapper(String name, Wrapper wrapper, boolean jspWildCard,
                boolean resourceOnly) {
            super(name, wrapper);
            this.jspWildCard = jspWildCard;
            this.resourceOnly = resourceOnly;
        }
    }

3. 路由Mapper

  1. 局部路由Mapper
    局部路由Mapper是指提供了Context容器内部路由导航功能的组件。它只存在于Context容器中,用于记录访问资源与Wrapper之间的映射,每个Web应用都存在自己的局部路由Mapper组件。
    局部路由Mapper只能在同一个Web应用内进行转发路由,而不能实现跨Web应用的路由。如果要实现跨Web应用,需要用到重定向功能,让客户端重定向到其他主机或其他Web应用上。而对于从客户端到服务端的请求,则需要全局路由Mapper组件的参与。
  2. 全局路由Mapper
    位于Tomcat的Connector组件中。通过它能对Host、Context、Wrapper等路由,即对于一个完整的请求地址,它能定位到指定的Host容器、Context容器以及Wrapper容器。所以全局路由Mapper拥有Tomcat容器完整的路由映射,负责完整的请求地址路由功能。

4. JNDI简介

JNDI即Java命名和目录接口(Java Naming and Directory Interface)。

其实一直对JNDI很疑惑。因为它说的是它提供了一组接口、类和关于命名空间的概念。接口,类,命名空间都知道是个啥,组合其实就不说人话了。

JNDI是基于提供商技术,它暴露一个API和一个服务供应接口(SPI)。它将名称和对象联系起来,使我们可以用名称访问对象。我们可以把JNDI简单地看成里面封装了一个名称到实体对象的映射,通过字符串可以方便得到想要的对象资源。

那么什么又是SPI?
初次理解SPI是在SpringBoot的自动配置上,每个starter的MATE-INF下有一个spring.properties的文件,而SpringBoot每次启动就是将这些文件内的全限定类名对应的类加载到Spring容器中去。而SPI就是做的这样的事。

从架构上看,JNDI包含了一个API层及SPI层,SPI层提供了服务的具体实现,再通过JNDI的API暴露给Java应用程序使用,这就将各种服务复杂的细节屏蔽了,提供统一的接口供应用程序使用。

一言以蔽之,JNDI使得用户可以用字符串去访问组件、资源或服务,提供一个上下文环境。


JNDI的主要工作就是维护两个对象:命名上下文和命名对象。
在JNDI中存在两种命名对象形态:
①直接存在内存中的命名对象;
②使用时再根据指定类及属性信息创建的命名对象。

JNDI运行机制是围绕着NamingManager的这些类和接口是JNDI能正常运行的基础,所有的上下文都要实现Context接口。

javax.naming.Context:

在这里插入图片描述
总结一下就是用一个Context类将资源与名字绑定在一起,那么我可以在全局的任意地方拿到这个对象资源的引用。其他的就是一些底层与细节的处理,就不继续深究了,感兴趣的可以把书找过来读一读。

5. 在Tomcat中使用JNDI

在Tomcat中可以通过字符串标识获取某个资源,而这个资源包括很多种,例如数据库数据源、JMS、EJB等。其中,全局命名资源无法从Web应用中直接获取,但它可以通过资源连接间接获得;
在这里插入图片描述
通过两行简单的代码就可以查找到一个数据源对象,里面包含了所有数据库连接对象,同时也封装了对这些连接的维护,以充当连接池。由于使用的是URL模式,因此在构造InitialContext时并不需要传入任何参数。

在Tomcat中以JNDI形式配置某Web应用使用的数据源(局部):
1)将数据源相应的驱动包复制到Tomcat安装目录的子目录lib下面。
2)修改Tomcat安装目录的子目录conf下面的server.xml配置文件。
在这里插入图片描述
服务器的全局配置方式:
1)将相应的驱动包复制到Tomcat安装目录的子目录lib下面。2)修改Tomcat安装目录的子目录conf下面的server.xml配置文件。这次server.xml文件将有两个地方需要配置,一个是添加GlobalNamingResources节点,另一个是添加ResourceLink节点。
在这里插入图片描述
在这里插入图片描述
看上面的使用,就知道了这是JNDI的作用及好处。

©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页