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