tomcat如何访问jndi信息

在上一篇我们知道tomcat在启动时会解析context.xml并将相应的数据源信息解析到上下文中。那么接下来的工作就是如何去访问这个数据源信息了。我们访问数据源信息是通过以下代码进行访问的 

InitialContext initialContext = new InitialContext();
	DataSource dataSource = (DataSource) initialContext.lookup("java:comp/env/jdbc/xx");

那么我们就按照这个访问思路跟踪一下代码。我们看一下InitialContext的lookup实现,如下所示:

public Object lookup(String name) throws NamingException {
	return getURLOrDefaultInitCtx(name).lookup(name);
    }

这里会取得一个UrlContext或InitContext,究竟取得哪个取决于传递的参数信息,这里我们的方法是以java:开头,即它有一个scheme_id信息,按照官方对于传递的URL字符串规则,显示如下所示(该描述在javadoc中的InitialContext中):

解析 URL 字符串时生成此策略的一个异常,如下所述。
 在将 URL 字符串(一个 scheme_id:rest_of_name 形式的 String)作为名称参数传递给任一方法时,将定位处理该方案的一个 URL 上下文工厂,并
将它用于解析该 URL。如果没有找到这样的工厂,则使用由 "java.naming.factory.initial" 指定的初始上下文。类似地,当将第一个组件是 URL 字
符串的 CompositeName 对象作为名称参数传递给任一方法时,将定位一个 URL 上下文工厂并将它用于解析第一个名称组件

因此,它将尝试找一个URLContext,而在前文中我们提到在tomcat支持命名服务中,设置了一个Context.URL_PKG_PREFIXES参数为org.apache.naming,因此,会尝试寻找一个名叫
org.apache.naming.java.javaURLContextFactory的类,而恰好存在这个类。那么InitContext将转向这个类,再从这个类找回一个UrlContext实例,具体实现如下所示:

//类 InitialContext方法getURLOrDefaultInitCtx
String scheme = getURLScheme(name);
	if (scheme != null) {
	    Context ctx = NamingManager.getURLContext(scheme, myProps);
	    if (ctx != null) {
		return ctx;
	    }
	}

//类NamingContext 方法getURLContext

Object answer = getURLObject(scheme, null, null, null, environment);

//方法 getURLObject


	ObjectFactory factory = (ObjectFactory)ResourceManager.getFactory(
	    Context.URL_PKG_PREFIXES, environment, nameCtx,
	    "." + scheme + "." + scheme + "URLContextFactory", defaultPkgPrefix);//这里就是我们所说的查找的那个类
	......
	    return factory.getObjectInstance(urlInfo, name, nameCtx, environment);

那么具体的逻辑就转到tomcat所提供的javaUrlContextFactory类中了,我们来看这个类的getObjectInstance方法。

public Object getObjectInstance(Object obj, Name name, Context nameCtx,
                                    Hashtable<?,?> environment)
        throws NamingException {
        if ((ContextBindings.isThreadBound()) || 
            (ContextBindings.isClassLoaderBound())) {
            return new SelectorContext((Hashtable<String,Object>)environment);
        }
        return null; 
    }

这里会返回一个SelectorContext实例,为什么,因此在创建NamingContext时,已经将上下文所在的classLoader绑定在ContextBinding上了。由在NamingContextListener中的如下代码实现:                    

ContextBindings.bindClassLoader
                        (container, container, 
                         ((Container) container).getLoader().getClassLoader());
//进入方法内部

Context context = contextNameBindings.get(name);
            if (context == null)
                throw new NamingException
                    (sm.getString("contextBindings.unknownContext", name));
            clBindings.put(classLoader, context);//以classLoader为key, 上下文为值
            clNameBindings.put(classLoader, name);//以classLoader为key,容器为值,这里的name为context容器

看到上面的clBindings.put(classLoader, context)这段代码没,既然把context以classLoader的方式绑定了,那么肯定可以通过classLoader将其取出来。在实现中,的确如此。我们接下来继续回到创建的SelectContext实例,看它是如何来查找jndi信息的。查看其实现:

public Object lookup(String name)
        throws NamingException {
        return getBoundContext().lookup(parseName(name));
    }

看到没,这里准备去取绑定的context了,但取到是我们在之前绑定的context还是其他context呢。这就要看getBoundContext的实现了。实现代码如下所示:

        if (initialContext) {
            。。。。。。
        } else {
            if (ContextBindings.isThreadBound()) {
                return ContextBindings.getThread();
            } else {
                return ContextBindings.getClassLoader();
            }

首先看这几个判断,第一个判断是为取InitialContext的,而我们这里这个判断值为false.第二个判断是为取绑定在线程上的context,我们这里也不存在绑定在当前线程的context。所以进入到最后一个,正是这个取得了我们在之前绑定在classLoader上的context。

至些,整个访问结束,找到了context。下面的工作就是从context中取jndi信息了。这在之前的文章 tomcat如何解析resource信息 如何将信息绑定在context中已经说了,不再叙述。

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201208090001.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

电子邮件地址不会被公开。 必填项已用*标注