在上一篇我们知道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