JNDI在J2EE中的作用

由于J2EE平台改善了普通企业开发人员的生活,因此,这种改进的代价是必须了解J2EE竞标中纳入的众多规范和技术,以使其成为一个包罗万象的分布式计算平台。 Dolly Developer只是众多开发人员之一,尚未发现一项可减轻任何企业级应用程序部署带来的负担的功能:JNDI, Java命名和目录接口 。 让我们看看Dolly如何在没有JNDI的情况下进行管理,以及正确使用JNDI如何改善她的处境。

完全熟悉的旅程

Dolly Developer正在编写一个使用JDBC数据源的Web应用程序。 她知道自己正在使用MySQL,因此她编码了对MySQL JDBC驱动程序类的引用,并使用适当的JDBC URL连接到Web应用程序中的数据库。 她认识到数据库连接池很重要,因此她包括一个连接池程序包,并将其配置为使用不超过64个连接。 她知道数据库服务器已设置为分配128个客户端连接。

多莉前往灾难

在开发阶段,一切都会顺利进行。 但是,在部署时,事情开始瓦解。 她的网络管理员告诉她,她无法从桌面访问生产或登台服务器,因此她必须为部署的每个阶段创建不同版本的代码。 由于这种情况,她需要一个新的JDBC URL,因此需要一个单独的可部署用于测试,登台和生产。 (熟悉配置管理的人们对在每个环境中部署单独的内部版本的想法很勉强,但是由于这似乎是一种相当普遍的情况,因此他们的痛苦程度可能不如预期。)

就在Dolly认为自己通过使用不同的URL旋转单独的可部署对象“解决”了配置问题时,她发现她的数据库管理员不想在生产环境中运行MySQL实例。 他说,对于开发来说很好,但是关键任务数据的业务标准是DB2®。 现在,她的构建不仅在数据库URL上有所不同,而且还需要具有不同的驱动程序。

情况变得更糟。 她的应用程序是如此有用且变得至关重要,以至于它从应用程序服务器获得故障转移功能,并被复制到一个四服务器集群中。 但是数据库管理员提出了一个危险信号,因为她的应用程序的每个实例都使用64个连接,并且数据库服务器整体上只有200个可用的连接-所有这些都由Dolly的应用程序争夺。 此外,DBA已确定Dolly的应用程序仅需要32个连接,并且仅在一天中的一个小时内。 随着应用程序的扩展,该应用程序最终在数据库层出现争用问题,并且她唯一的选择是更改群集连接的数量,并准备在群集增长或在另一个群集中复制她的应用程序时再次进行更改。 看来她已经做出了有关应用程序配置的决定,最好由系统和数据库管理员来决定。

J2EE角色

如果Dolly在了解J2EE角色的情况下开发了自己的应用程序,则可以避免这种困境。 J2EE规范将职责委派给许多开发角色:组件提供程序,应用程序组装程序,部署程序和系统管理员。 (在许多组织中,“组件提供者”和“组装者”角色以及“部署者”和“系统管理员”角色都是合并的。)在能够真正理解JNDI在J2EE中的角色之前,掌握J2EE角色的作用很重要。

组件提供者
该角色负责创建J2EE组件,这些组件可以是Web应用程序,Enterprise JavaBean(EJB)组件或应用程序客户端(例如,基于Swing的GUI客户端应用程序)。 组件提供者包括HTML内容设计者,文档程序员和其他开发人员角色。 大多数J2EE开发人员在组件提供者角色上花费了大量时间。
应用组装商
该角色将多个J2EE模块绑定为一个紧密可部署的整体:企业归档(EAR)文件。 应用程序组装器选择组件,确定它们将如何交互,配置其安全性和事务属性,并将应用程序打包到EAR文件中。 许多IDE,例如WebSphere®Studio,IDEA,JBuilder,WebLogic Workshop等,都具有辅助EAR文件交互式配置的功能。
部署者
该角色负责部署,这意味着将EAR安装到J2EE容器(应用程序服务器)中,配置资源(例如数据库连接池),将应用程序所需的资源绑定到应用程序服务器中的特定资源,并启动应用程序。
系统管理员
该角色负责确保容器所需的资源可供容器使用。

行动中的角色

想象一个企业应用程序,它包含一个Web应用程序和一个用于业务逻辑和持久性的EJB组件。 开发此应用程序可能涉及许多组件提供程序,尽管在许多情况下,同一个人可以履行所有职责。 这些组件可能包括数据传输对象(一个JAR文件),EJB接口(另一个JAR文件),EJB实现本身(还有另一个JAR文件)以及用户界面组件-Servlet,JSP,HTML页面和其他静态组件。网页内容。 用户界面组件还被打包到一个Web应用程序中,该Web应用程序包含Servlet类,JSP文件,静态内容以及包含其他必需组件(包括EJB接口)的JAR。

尽管听起来好像有很多组件需要突破,但这几乎没有超出范围,特别是当您考虑使用多少个JAR文件来构建典型的Web应用程序时。 重要的是要意识到必须在此处仔细管理依赖项。 接口和传输对象是Web应用程序和EJB实现的允许依赖关系,但是依赖关系的所有行都应沿同一方向运行; 要避免循环依赖性。 J2EE组件(例如WAR文件和EJB JAR文件)必须声明其对部署单元外部资源的依赖。

应用程序组装程序负责将依赖项包含在Web应用程序中,并将整个程序包装到单个企业应用程序中。 工具在这里有很大帮助。 IDE可以帮助创建反映模块和JAR依赖关系的项目结构,并允许您随意指定包含或排除模块。

部署者负责确保组件所需的资源存在于部署环境中,并将其绑定到平台的可用资源。 例如,此时,Web应用程序中的外部EJB引用(部署描述符中的ejb-ref )已绑定到实际的已部署EJB组件上,并且此刻不会很快。

后期绑定外部资源

任何不平凡的J2EE应用程序都将需要访问描述其预期运行环境的信息。 这意味着,开发和测试组件将要求开发人员承担某些部署职责,即使只是出于测试代码的目的而临时承担这些职责。 重要的是要了解,通过这样做,您将走出开发人员领域。 否则,诱惑是依赖JDBC驱动程序,URL,JMS队列名称或其他机器资源,而这会带来意想不到的,有时是灾难性的影响。

JNDI进行救援

解决Dolly问题的方法是从她的应用程序代码中删除对数据存储的所有直接引用。 没有对JDBC驱动程序的引用,没有服务器名称,没有用户名或密码-甚至没有数据库池或连接管理。 多莉需要编写她的代码,以不知道它将访问哪些特定的外部资源,同时要理解其他人将提供利用这些外部资源所需的链接。 这将使部署者(无论是谁)都可以将数据库连接分配给她的应用程序,而无需Dolly的参与。 (从数据安全到Sarbanes-Oxley法规遵从性,这也有很好的商业原因。)

许多开发人员知道,代码与外部资源之间的紧密耦合可能是一个问题,但在实践中通常会忽略角色的分离。 在较小的开发工作中(就团队规模或部署而言),无视角色分离会足够成功。 (毕竟,当您的应用程序只是个人配方应用程序,并且您不打算依赖它时,可以将您的应用程序锁定到特定的PostgreSQL实例中。)

J2EE规范要求所有J2EE容器都提供JNDI规范的实现。 JNDI在J2EE中的作用是充当“总机”,这是J2EE组件在运行时间接查找其他组件,资源或服务的通用机制。 在大多数情况下,容器提供的JNDI提供程序可以用作有限的数据存储,因此管理员可以在一个应用程序中设置执行属性,并让其他应用程序引用它们(Java管理扩展(JMX)也可以用于此目的) 。 JNDI在J2EE应用程序中的主要作用是提供间接层,以便组件可以在不十分了解间接的情况下找到所需的资源。

多莉移动到平静的水域

让我们回顾一下多莉的情况。 在简单的Web应用程序中,她直接从应用程序代码中使用了JDBC连接。 通过清单1的检查,我们可以看到她已将JDBC驱动程序的名称,数据库URL以及她的用户名和密码显式编码到servlet中:

清单1.典型(但不好)的JDBC使用
Connection conn=null;
try {
  Class.forName("com.mysql.jdbc.Driver",
                true, Thread.currentThread().getContextClassLoader());
  conn=DriverManager.getConnection("jdbc:mysql://dbserver?user=dolly&password=dagger");
  /* use the connection here */
  conn.close();
} 
catch(Exception e) {
  e.printStackTrace();
} 
finally {
  if(conn!=null) {
    try {
      conn.close();
    } catch(SQLException e) {}
  }
}

与其以这种方式指定配置信息,不如使用JNDI查找JDBC DataSource来更好地服务Dolly(及其同事),如清单2所示:

清单2.使用JNDI来获取数据源
Connection conn=null;
try {
  Context ctx=new InitialContext();
  Object datasourceRef=ctx.lookup("java:comp/env/jdbc/mydatasource");
  DataSource ds=(Datasource)datasourceRef;
  conn=ds.getConnection();
  /* use the connection */
  conn.close();
} 
catch(Exception e) {
  e.printStackTrace();
} 
finally {
  if(conn!=null) {
    try {
      conn.close();
    } catch(SQLException e) { }
  }
}

为了获得JDBC连接,我们首先需要执行一些次要的部署配置,以便我们可以在本地组件的JNDI上下文中查找JDBC DataSource 。 这可能有点琐事,但很容易学习。 不幸的是,这意味着即使要测试组件,开发人员也必须涉足部署人员的领域,并准备配置应用程序服务器。

配置JNDI参考

为了让JNDI解析java:comp/env/jdbc/mydatasource参考,部署程序需要将<resource-ref>标记插入web.xml文件(Web应用程序的部署描述符)。 <resource-ref>标记是一种表达方式,“此组件依赖于外部资源”。 清单3显示了一个示例:

清单3.一个资源引用条目
<resource-ref>
  <description>Dollys DataSource</description>
  <res-ref-name>jdbc/mydatasource</res-ref-name>
  <res-ref-type>javax.sql.DataSource</res-ref-type>
  <res-auth>Container</res-auth>
</resource-ref>

<resource-ref>条目通知Servlet容器,部署程序将在组件命名上下文中名为jdbc/mydatasource的资源进行设置。 组件命名上下文由前缀java:comp/env/指示,因此完全限定的本地资源名称为java:comp/env/jdbc/mydatasource

这仅定义了对外部资源的本地引用,而没有创建该引用将指向的实际资源。 (Java语言的相似之处可能是<resource-ref>声明了一个引用,例如Object foo ,但没有将foo设置为实际上引用任何Object 。)

部署者的工作是创建一个DataSource (或者,在我们的Java语言示例中,创建一个供foo指向的Object )。 每个容器都有其自己的设置数据源的机制。 例如,在JBoss中,数据源是通过服务定义的(示例请参见$ JBOSS / server / default / deploy / hsqldb-ds.xml),该服务指定它是DataSource的全局JNDI名称(默认情况下) , DefaultDS 。)创建资源后,仍然需要执行关键的第三步:将资源连接或绑定到应用程序组件使用的本地名称。 对于Web应用程序,使用特定于供应商的部署描述符扩展来指定此绑定,清单4中显示了该绑定的示例。(JBoss使用名为jboss-web.xml的文件用于特定于供应商的Web应用程序部署描述符。)

清单4.在供应商特定的部署描述符中将资源绑定到JNDI名称
<resource-ref>
   <res-ref-name>jdbc/mydatasource</res-ref-name>
   <jndi-name>java:DefaultDS</jndi-name>
</resource-ref>

这表示本地资源引用名称( jdbc/mydatasource )应该映射到名为java:DefaultDS的全局资源。 如果全局资源名称由于任何原因而更改,则应用程序代码不会更改-仅此映射。 这里有两个间接级别-一个用于定义和命名资源( java:DefaultDS ),另一个将本地特定于组件的名称( jdbc/mydatasource )绑定到命名资源。 (实际上,由于您也可以在EAR级别映射资源,因此可能存在第三级间接访问。)

越过数据源

当然,J2EE中的资源不限于JDBC数据源。 有几种类型的引用,包括资源引用(已经讨论过),环境条目和EJB引用。 EJB引用特别暴露了JNEE中JNDI的另一个关键角色:查找其他应用程序组件。

考虑当公司从订单本体处理服务(OOPS)购买可部署的EJB组件来处理客户订单时会发生什么。 为了示例,我们将其称为ProcessOrders V1.0。 ProcessOrders 1.0分两部分:一组接口和支持类(主接口和远程接口,以及支持传输对象),以及实际的EJB组件本身。 之所以选择OOPS是因为其在这一领域的专业知识

该公司遵循J2EE规范,并编写了一个使用EJB引用的Web应用程序。 它的Deployer以ejb/ProcessOrders/1.0身份将ProcessOrders 1.0绑定到JNDI树中,并解析Web应用程序的资源名称以指向该全局JNDI名称。 到目前为止,这都是EJB组件的非常正常的用法。 但是,当我们考虑公司的发展周期与供应商的发展周期之间的相互作用时,情况就变得更加复杂。 同样,JNDI也可以在这里为我们提供帮助。

假设OOPS发布了新版本ProcessOrders V1.1。 这个新版本具有公司内部的新应用程序所需的某些功能,并且自然地为其EJB组件扩展了业务接口。

该公司在这里有几种选择:它可以更新其所有应用程序以使用新版本,可以编写自己的应用程序,或者可以使用JNDI的参考解析来允许每个应用程序使用其自己版本的EJB组件,而不会影响任何其他应用程序。 一次更新所有应用程序将是维护的噩梦,需要对所有组件进行全面的回归测试,这通常是一个沉重的负担,如果任何功能测试失败,则需要进行另一轮调试。

编写内部组件通常是不必要的重复工作。 如果组件是由在该业务领域具有专业知识的公司编写的,则给定的IT商店和专门的组件提供商不太可能设法掌握业务功能。

您可能已经猜到了,最好的解决方案是使用JNDI分辨率。 EJB JNDI引用与JDBC资源引用非常相似。 对于每个引用,部署程序都将以特定名称(例如ejb/ProcessOrders/1.1)将新组件绑定到全局树中,对于需要EJB组件的其他所有组件,请在部署描述符中解析该EJB引用。零件。 依赖于V1.0的较旧的应用程序无需更改且无需重新测试,从而降低了实施时间,成本和复杂性。 在倾向于对服务进行版本控制的环境中,这是一种强大的方法。 可以对应用程序体系结构中的所有可获取组件进行这种配置管理,从EJB组件到JMS队列和主题,再到简单的配置字符串或其他对象,可以随着服务随时间的变化而降低维护成本,并简化了部署和集成工作。

结论

有个古老的计算机科学笑话说,每个编程问题都可以仅通过一层抽象(或间接)来解决。 在J2EE中,JNDI是将J2EE应用程序结合在一起的粘合剂,但结合得并不紧密,以至于它们不容易拆开并重新组装。 JNDI提供的间接功能使企业范围内的可伸缩,功能强大且灵活的应用程序得以交付。 这就是J2EE的承诺,并且通过一些计划和周到的考虑,它是可以完全实现的。 实际上,它比许多人想象的要容易。


翻译自: https://www.ibm.com/developerworks/java/library/j-jndi/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值