- 浏览: 149392 次
最新评论
-
zjhgx:
写的不错
tomcat架构分析 (Session管理) -
smallbug_vip:
very very good 如此好文章,一定要刷新一下最后评 ...
tomcat架构分析(概览) -
slightz:
楼主我已经深深的爱上你了
tomcat 性能之connector -
js_nicle:
受益匪浅,谢谢博主。
tomcat架构分析-索引 -
lianglaiyang:
楼主,什么时候给我们讲一下nio connector中的com ...
tomcat架构分析 (connector NIO 实现)
出处:http://gearever.iteye.com
在tomcat架构分析 (JNDI配置)一文里,以配置JDBC数据库连接为例,介绍了tomcat中常用的JNDI配置的几种用法。使用这种配置,在app里可以通过JNDI API非常简单的调用相应的资源对象。但是调用越简单,那其背后封装的逻辑越多。就好比汽车分为手动档自动挡一样。对司机而言,自动挡开起来会轻松很多,那是因为很多复杂的操作,已经封装起来由机器来完成了。
本篇就是从代码原理角度来揭示tomcat中JNDI的配置是如何生效的,以及app中的调用逻辑是如何实现的。通过这些,可以看到tomcat中一块比较重要的体系结构,同时加深对JNDI的理解。
上文介绍了两种配置方案,一个是global的配置,在各个app中引用;一个是各个app自己配置资源对象。这两种方案,从实现角度来看,原理一样,只是第一种比第二种多了一层mapping关系。所以为了方便理解,先从第二种方案,即各个app配置自己的资源对象来说明。
另外,需要说明的是,本章涉及的代码
先看一个概念图
JNDI体系分为三个部分;
这里需要说明的是,在后面会经常涉及到两类context,一个是作为tomcat内部实现逻辑的容器StandardContext;一个是作为JNDI内部分支对象NamingContext;它们实现不同接口,互相没有任何关系,不要混淆。
开始看看每个部分详细情况吧。
初始化NamingResources
先看看配置;
<tomcat>/conf/server.xml
通过这个配置,可以非常清楚的看出tomcat内部的层次结构,不同的层次实现不同的作用域,同时每个层次都有相应的类进行逻辑封装,这是tomcat面向对象思想的体现。那么相应的,Context节点下的Resource节点也有类进行封装;
上面例子中Resource节点配置的所有属性会以键值对的方式存入ContextResource的一个HashMap对象中,这一步只是初始化,不会用到每个属性,它只是为了每个真正处理的资源对象用到,例如后面会说的缺省的tomcat的数据库连接池对象BasicDataSourceFactory,如果用其他的数据库连接池,例如c3p0,那么其配置的属性对象就应该按照c3p0中需要的属性名称来配。
但是,这些属性中的name和type是ContextResource需要的,name是JNDI对象树的分支节点,上面配的“jdbc/mysql”,那么这个数据库连接池对象就对应在“java:comp/env/jdbc/mysql”的位置。type是这个对象的类型,如果是“javax.sql.DataSource”,tomcat会有一些特殊的逻辑处理。
当tomcat初始化时,StandardContext对象内部会生成一个NamingResources对象,这个对象就是做一些预处理,存储一些Resource对象,看一下NamingResources存储Resource对象的逻辑;
需要说明的是,不仅仅是Resource一种对象,还有Web Service资源对象,EJB对象等,这里就是拿数据库连接的Resource对象举例。
启动JNDI绑定
当tomcat启动时,会抛出一个start event,由StandardContext的NamingContextListener监听对象捕捉到,响应start event。
注意上面方法中有两层绑定关系;
ContextBindings.bindContext()
ContextBindings.bindClassLoader()
主要看一下将初始化时的资源对象绑定JNDI对象树的createNamingContext()方法;
看一下addResource的具体加载逻辑;
这就是上面配置的jdbc/mysql数据库连接池的JNDI对象树;
到目前为止,完成了JNDI对象树的绑定,可以看到,每个app对应的StandardContext对应一个JNDI对象树,并且每个app的各个classloader与此JNDI对象树分别绑定,那么各个app之间的JNDI可以不互相干扰,各自配置及调用。
需要注意的是,NamingContext对象就是JNDI对象树上的树枝节点,类似文件系统中的目录,各个Resource对象则是JNDI对象树上的叶子节点,类似文件系统的具体文件,通过NamingContext对象将整个JNDI对象树组织起来,每个Resource对象才是真正存储数据的地方。
本篇就描述tomcat内部是如何构造JNDI对象树的,如何通过JNDI获取对象,涉及到JNDI API内部运作了,将在另一篇中继续。
无论是global配置的,还是各个app自己配置的,其实现的原理都是一样的,只不过是不同classloader来加载的,global的多了一层引用映射而已
在tomcat架构分析 (JNDI配置)一文里,以配置JDBC数据库连接为例,介绍了tomcat中常用的JNDI配置的几种用法。使用这种配置,在app里可以通过JNDI API非常简单的调用相应的资源对象。但是调用越简单,那其背后封装的逻辑越多。就好比汽车分为手动档自动挡一样。对司机而言,自动挡开起来会轻松很多,那是因为很多复杂的操作,已经封装起来由机器来完成了。
本篇就是从代码原理角度来揭示tomcat中JNDI的配置是如何生效的,以及app中的调用逻辑是如何实现的。通过这些,可以看到tomcat中一块比较重要的体系结构,同时加深对JNDI的理解。
上文介绍了两种配置方案,一个是global的配置,在各个app中引用;一个是各个app自己配置资源对象。这两种方案,从实现角度来看,原理一样,只是第一种比第二种多了一层mapping关系。所以为了方便理解,先从第二种方案,即各个app配置自己的资源对象来说明。
另外,需要说明的是,本章涉及的代码
- Tomcat源码
- JNDI源码(javax.naming.*),参考OpenJDK项目
先看一个概念图
JNDI体系分为三个部分;
- 在tomcat架构分析 (容器类)中介绍了StandardContext类,它是每个app的一个逻辑封装。当tomcat初始化时,将根据配置文件,对StandardContext中的NamingResources对象进行赋值,同时,将实例化一个NamingContextListener对象作为这个context作用域内的事件监听器,它会响应一些例如系统启动,系统关闭等事件,作出相应的操作;
- 初始化完成后,tomcat启动,完成启动逻辑,抛出一个系统启动event,由那个NamingContextListener捕获,进行处理,将初始化时的NamingResources对象中的数据,绑定到相应的JNDI对象树(namingContext)上,即java:comp/env分支,然后将这个根namingContext与这个app的classloader进行绑定,这样每个app只有在自己的JNDI对象树上调用,互不影响;
- 每个app中的类都由自己app的classloader加载,如果需要用到JNDI绑定对象,也是从自己classloader对应的JNDI对象树上获取资源对象
这里需要说明的是,在后面会经常涉及到两类context,一个是作为tomcat内部实现逻辑的容器StandardContext;一个是作为JNDI内部分支对象NamingContext;它们实现不同接口,互相没有任何关系,不要混淆。
开始看看每个部分详细情况吧。
初始化NamingResources
先看看配置;
<tomcat>/conf/server.xml
<Server port="8005"> <Service> <Engine> <Host> <Context> <Resource name="jdbc/mysql" type="javax.sql.DataSource" username="root" password="root" driverClassName="com.mysql.jdbc.Driver" maxIdle="200" maxWait="5000" url="……" maxActive="100"/> </Context> </Host> </Engine> </Service> …… </Server>
通过这个配置,可以非常清楚的看出tomcat内部的层次结构,不同的层次实现不同的作用域,同时每个层次都有相应的类进行逻辑封装,这是tomcat面向对象思想的体现。那么相应的,Context节点下的Resource节点也有类进行封装;
org.apache.catalina.deploy.ContextResource
上面例子中Resource节点配置的所有属性会以键值对的方式存入ContextResource的一个HashMap对象中,这一步只是初始化,不会用到每个属性,它只是为了每个真正处理的资源对象用到,例如后面会说的缺省的tomcat的数据库连接池对象BasicDataSourceFactory,如果用其他的数据库连接池,例如c3p0,那么其配置的属性对象就应该按照c3p0中需要的属性名称来配。
但是,这些属性中的name和type是ContextResource需要的,name是JNDI对象树的分支节点,上面配的“jdbc/mysql”,那么这个数据库连接池对象就对应在“java:comp/env/jdbc/mysql”的位置。type是这个对象的类型,如果是“javax.sql.DataSource”,tomcat会有一些特殊的逻辑处理。
当tomcat初始化时,StandardContext对象内部会生成一个NamingResources对象,这个对象就是做一些预处理,存储一些Resource对象,看一下NamingResources存储Resource对象的逻辑;
public void addResource(ContextResource resource) { //确保每一个资源对象的name都是唯一的 //不仅是Resource对象之间,包括Service等所有的资源对象 if (entries.containsKey(resource.getName())) { return; } else { entries.put(resource.getName(), resource.getType()); } //建立一个name和资源对象的mapping synchronized (resources) { resource.setNamingResources(this); resources.put(resource.getName(), resource); } support.firePropertyChange("resource", null, resource); }
需要说明的是,不仅仅是Resource一种对象,还有Web Service资源对象,EJB对象等,这里就是拿数据库连接的Resource对象举例。
启动JNDI绑定
当tomcat启动时,会抛出一个start event,由StandardContext的NamingContextListener监听对象捕捉到,响应start event。
public void lifecycleEvent(LifecycleEvent event) { container = event.getLifecycle(); if (container instanceof Context) { //这个namingResources对象就是StandardContext的namingResources对象 namingResources = ((Context) container).getNamingResources(); logger = log; } else if (container instanceof Server) { namingResources = ((Server) container).getGlobalNamingResources(); } else { return; } //响应start event if (event.getType() == Lifecycle.START_EVENT) { if (initialized) return; Hashtable contextEnv = new Hashtable(); try { //生成这个StandardContext域的JNDI对象树根NamingContext对象 namingContext = new NamingContext(contextEnv, getName()); } catch (NamingException e) { // Never happens } ContextAccessController.setSecurityToken(getName(), container); //将此StandardContext对象与JNDI对象树根NamingContext对象绑定 ContextBindings.bindContext(container, namingContext, container); if( log.isDebugEnabled() ) { log.debug("Bound " + container ); } // Setting the context in read/write mode ContextAccessController.setWritable(getName(), container); try { //将初始化时的资源对象绑定JNDI对象树 createNamingContext(); } catch (NamingException e) { logger.error (sm.getString("naming.namingContextCreationFailed", e)); } // 针对Context下配置Resource对象而言 if (container instanceof Context) { // Setting the context in read only mode ContextAccessController.setReadOnly(getName()); try { //通过此StandardContext对象获取到JNDI对象树根NamingContext对象 //同时将此app的classloader与此JNDI对象树根NamingContext对象绑定 ContextBindings.bindClassLoader (container, container, ((Container) container).getLoader().getClassLoader()); } catch (NamingException e) { logger.error(sm.getString("naming.bindFailed", e)); } } // 针对global资源而言,这里不用关注 if (container instanceof Server) { namingResources.addPropertyChangeListener(this); org.apache.naming.factory.ResourceLinkFactory.setGlobalContext (namingContext); try { ContextBindings.bindClassLoader (container, container, this.getClass().getClassLoader()); } catch (NamingException e) { logger.error(sm.getString("naming.bindFailed", e)); } if (container instanceof StandardServer) { ((StandardServer) container).setGlobalNamingContext (namingContext); } } initialized = true; } //响应stop event else if (event.getType() == Lifecycle.STOP_EVENT) { ...... } }
注意上面方法中有两层绑定关系;
ContextBindings.bindContext()
public static void bindContext(Object name, Context context, Object token) { if (ContextAccessController.checkSecurityToken(name, token)) //先是将StandardContext对象与JNDI对象树根NamingContext对象绑定 //注意,这里第一个参数name是StandardContext对象 contextNameBindings.put(name, context); }
ContextBindings.bindClassLoader()
public static void bindClassLoader(Object name, Object token, ClassLoader classLoader) throws NamingException { if (ContextAccessController.checkSecurityToken(name, token)) { //根据上面的StandardContext对象获取刚才绑定的NamingContext对象 Context context = (Context) contextNameBindings.get(name); if (context == null) throw new NamingException (sm.getString("contextBindings.unknownContext", name)); //将classloader与NamingContext对象绑定 clBindings.put(classLoader, context); clNameBindings.put(classLoader, name); } }
主要看一下将初始化时的资源对象绑定JNDI对象树的createNamingContext()方法;
private void createNamingContext() throws NamingException { // Creating the comp subcontext if (container instanceof Server) { compCtx = namingContext; envCtx = namingContext; } else { //对于StandardContext而言,在JNDI对象树的根namingContext对象上 //建立comp树枝,以及在comp树枝上建立env树枝namingContext对象 compCtx = namingContext.createSubcontext("comp"); envCtx = compCtx.createSubcontext("env"); } ...... // 从初始化的NamingResources对象中获取Resource对象加载到JNDI对象树上 ContextResource[] resources = namingResources.findResources(); for (i = 0; i < resources.length; i++) { addResource(resources[i]); } ...... }
看一下addResource的具体加载逻辑;
public void addResource(ContextResource resource) { // Create a reference to the resource. Reference ref = new ResourceRef (resource.getType(), resource.getDescription(), resource.getScope(), resource.getAuth()); // 遍历Resource对象的各个属性,这些属性存在一个HashMap中 Iterator params = resource.listProperties(); while (params.hasNext()) { String paramName = (String) params.next(); String paramValue = (String) resource.getProperty(paramName); //封装成StringRefAddr,这些都是JNDI的标准API StringRefAddr refAddr = new StringRefAddr(paramName, paramValue); ref.add(refAddr); } try { if (logger.isDebugEnabled()) { logger.debug(" Adding resource ref " + resource.getName() + " " + ref); } //在上面创建的comp/env树枝节点上,根据Resource配置的name继续创建新的节点 //例如配置的name=”jdbc/mysql”,则在comp/env树枝节点下再创建一个jdbc树枝节点 createSubcontexts(envCtx, resource.getName()); //绑定叶子节点,它不是namingContext对象,而是最后的Resource对象 envCtx.bind(resource.getName(), ref); } catch (NamingException e) { logger.error(sm.getString("naming.bindFailed", e)); } //这就是上面说的对于配置type="javax.sql.DataSource"时的特殊逻辑 //将数据库连接池类型的资源对象注册到tomcat全局的JMX中,方便管理及调试 if ("javax.sql.DataSource".equals(ref.getClassName())) { try { ObjectName on = createObjectName(resource); Object actualResource = envCtx.lookup(resource.getName()); Registry.getRegistry(null, null).registerComponent(actualResource, on, null); objectNames.put(resource.getName(), on); } catch (Exception e) { logger.warn(sm.getString("naming.jmxRegistrationFailed", e)); } } }
这就是上面配置的jdbc/mysql数据库连接池的JNDI对象树;
到目前为止,完成了JNDI对象树的绑定,可以看到,每个app对应的StandardContext对应一个JNDI对象树,并且每个app的各个classloader与此JNDI对象树分别绑定,那么各个app之间的JNDI可以不互相干扰,各自配置及调用。
需要注意的是,NamingContext对象就是JNDI对象树上的树枝节点,类似文件系统中的目录,各个Resource对象则是JNDI对象树上的叶子节点,类似文件系统的具体文件,通过NamingContext对象将整个JNDI对象树组织起来,每个Resource对象才是真正存储数据的地方。
本篇就描述tomcat内部是如何构造JNDI对象树的,如何通过JNDI获取对象,涉及到JNDI API内部运作了,将在另一篇中继续。
评论
4 楼
tmj_159
2013-01-21
楼主写个connector的专题吧
3 楼
rrsy23
2013-01-05
贵在坚持 希望你比我有坚持心 加油
2 楼
gearever
2013-01-03
tianhandigeng 写道
写得非常的好,不过这里只讲了每个应用单独使用的请求,在tomcat中全部配置的怎么没写呢
无论是global配置的,还是各个app自己配置的,其实现的原理都是一样的,只不过是不同classloader来加载的,global的多了一层引用映射而已
1 楼
tianhandigeng
2012-12-25
写得非常的好,不过这里只讲了每个应用单独使用的请求,在tomcat中全部配置的怎么没写呢
发表评论
-
mina架构分析 (NIO 网络接口)
2013-04-10 23:39 6288出处:http://gearever.iteye.com A ... -
tomcat架构分析 (connector NIO 实现)
2013-04-09 00:43 27384出处:http://gearever.iteye.com 上 ... -
tomcat架构分析(connector BIO 实现)
2013-04-03 17:41 12820出处:http://gearever.iteye. ... -
tomcat架构分析 (JNDI配置)
2012-06-07 11:46 7793出处:http://gearever.iteye.com J ... -
tomcat架构分析 (Session管理)
2012-06-01 12:34 20585出处:http://gearever.iteye.com S ... -
tomcat架构分析-索引
2012-05-30 21:26 7405出处:http://gearever.iteye.com ... -
tomcat 性能之connector
2012-05-30 20:55 2725今天重新分析了一下tomcat的connector接口的阻塞模 ... -
tomcat架构分析(valve源码导读)
2012-05-24 19:17 5988出处:http://gearever.iteye.com 源 ... -
tomcat架构分析(valve机制)
2012-05-21 10:00 16772出处:http://gearever.iteye. ... -
tomcat架构分析(容器类)
2012-05-17 14:32 14958出处:http://gearever.itey ... -
tomcat架构分析(概览)
2012-05-16 16:23 16461出处:http://gearever.iteye.com T ... -
重新开始
2012-05-16 16:04 143之前架了个blog在GAE上,但是不是很稳定,GAE时常被盾, ...
相关推荐
7. **JNDI(Java Naming and Directory Interface)**:Tomcat支持JNDI,允许应用程序查找和绑定到各种命名和目录服务,如数据源、邮件会话等。 8. **Web应用程序**:Tomcat可以部署多个Web应用程序,每个应用程序...
一、Tomcat架构概述 Tomcat的核心架构主要由以下几个部分组成: 1. **Catalina**:Catalina是Tomcat的主要Servlet容器,负责处理Servlet和JSP的生命周期管理。它实现了Servlet和ServletContext接口,是Tomcat的...
Java Naming and Directory Interface (JNDI) 是Java平台中用于访问命名和目录服务的一组接口与服务提供商架构。它提供了一种标准的方式来查找和管理分布式环境中的对象,比如数据库连接池、邮件服务器配置等。JNDI...
1. **Tomcat架构**:Tomcat由几个主要组件构成,包括Catalina(Servlet容器)、 Coyote(HTTP/1.1协议处理器)、Jasper(JSP编译器)等。这些组件协同工作,处理请求并返回响应。 2. **配置文件**:`build.xml`是...
1. **Tomcat架构**:Tomcat由几个关键组件组成,如Catalina(Servlet容器), Coyote(HTTP/HTTPS连接器),Jasper(JSP编译器)等,它们协同工作以提供Web服务。 2. **角色与部署**:Tomcat支持Web应用的部署,...
1. **Tomcat架构**:理解Tomcat的内部工作原理,包括Catalina核心引擎、 Coyote HTTP/1.1连接器和Jasper JSP编译器等组件的作用。 2. **安装与配置**:了解如何在不同操作系统上安装Tomcat,以及配置服务器以满足...
在Spring框架中,JNDI(Java Naming and Directory Interface)主要用来查找和绑定资源,如数据源、EJB等。JNDI允许我们通过名称来访问分布式环境中的对象,这在多层架构或集群环境中非常有用。下面我们将深入探讨...
6. **JNDI(Java Naming and Directory Interface)**:Tomcat提供了JNDI服务,允许应用查找和绑定资源,如数据源、邮件会话等。 7. **国际化与本地化**:Tomcat支持多语言环境,通过配置可以实现不同地区的本地化...
在Java应用服务器中,JNDI通常被用来查找和绑定数据源(DataSource),这些数据源可以是预配置好的数据库连接池,使得多个应用能够共享同一个数据库连接,提高系统性能和资源利用率。 1. **配置数据源**: 在Java...
3. **Tomcat架构** Tomcat的架构主要包括几个关键组件:Catalina(核心Servlet容器)、 Coyote(HTTP/HTTPS连接器)、Jasper(JSP编译器)、Apr(Apache Portable Runtime,提供高性能的网络功能)等。这些组件协同...
3. **JNDI增强**:加强了Java Naming and Directory Interface (JNDI)支持,提供了更灵活的资源绑定和查找。 4. **更好的内存管理**:优化了内存分配和回收策略,减少了内存泄漏的可能性,提高了服务器的稳定性和...
5. **JNDI**(Java Naming and Directory Interface):Tomcat提供了JNDI服务,允许在应用中查找和绑定资源,如数据源或EJB引用。 6. **JMX(Java Management Extensions)**:用于监控和管理Tomcat,提供诸如启动...
Tomcat7.0提供JNDI(Java Naming and Directory Interface)服务,允许应用通过名字查找和绑定各种资源,如数据源、邮件会话等。这些配置通常在conf/server.xml的元素内进行。 九、日志管理 Tomcat的日志系统可以在...
1. **Tomcat架构** Tomcat的架构主要由几个核心组件构成,包括Catalina(Servlet容器)、 Coyote(HTTP/HTTPS连接器)、Jasper(JSP编译器)和Commons(共享类库)。Catalina负责管理和运行Servlet,Coyote处理网络...
它是整个Tomcat架构中的最高级别组件,通常是单例模式实现。 **1.2 Service** - **定义**:`Service`元素表示一种组合,它包含了一个或多个`Connector`组件与一个`Engine`组件。`Service`负责处理来自所有`...
三、Tomcat架构 Tomcat的架构主要由以下几个部分组成: 1. **Catalina**:这是Tomcat的核心,负责Servlet和JSP的生命周期管理。 2. ** Coyote**:处理HTTP连接,接收和响应HTTP请求。 3. **Jasper**:JSP引擎,...
一、Tomcat架构 1. **Catalina核心**:Catalina是Tomcat的核心组件,负责处理Servlet和JSP的生命周期。它遵循Servlet规范,管理Servlet的加载、初始化、服务和销毁过程。 2. **Connector组件**:Connector是Tomcat...
7. **JNDI服务**:Tomcat提供Java Naming and Directory Interface (JNDI)服务,允许应用程序查找和绑定资源。 8. **连接器**:Tomcat支持多种连接器,例如Apache HTTP Server与Tomcat的连接器mod_jk或mod_proxy_...
5. **Naming and Directory**:Tomcat支持JNDI(Java Naming and Directory Interface),允许应用查找和绑定资源,如数据源和EJB引用。 6. **Connector(Engine、Host、Context)**:这些是Tomcat的容器概念,...
4. **JNDI服务**:Java Naming and Directory Interface (JNDI) 提供了一个接口,使得应用可以查找和绑定数据,包括资源工厂和目录服务。 5. **JMX(Java Management Extensions)**:5.0版本中,Tomcat集成了JMX,...