原文地址:http://blog.sina.com.cn/s/blog_605f5b4f0100qwra.html
今天跟踪代码,发现在IntialContext的构造方法中会调用System.getProperties(),竟然从中得到了在jndi.properties文件中配置的信息,于是就将InitialContext的API中内容又重新读了一遍。
API中写道:
JNDI 通过按顺序合并取自以下两个源的值来确定每个属性值:
- 构造方法的环境参数、(适当属性的)applet 参数,以及系统属性中最先出现的属性。
- 应用程序资源文件 (jndi.properties)。
对于同时存在于两个源或多个应用程序资源文件中的每个属性,用以下方式确定属性值。如果该属性是指定 JNDI 工厂列表的标准 JNDI 属性之一(参见 Context),则所有值都被串联成一个以冒号分隔的列表。对于其他属性,只使用最先找到的值。
这一定位初始上下文和 URL 上下文工厂的默认策略可以通过调用 NamingManager.setInitialContextFactoryBuilder() 重写。
资源文件
要简化设置 JNDI 应用程序所需环境的任务,可以将资源文件 与应用程序组件和服务提供程序一起发布。JNDI 资源文件是使用属性文件格式的文件(参见 java.util.Properties
),包括一个键/值对列表。键是属性的名称(例如 "java.naming.factory.object"),而值是使用为该属性定义的格式的字符串。以下是 JNDI 资源文件的一个示例:
java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
JNDI 类库读取资源文件,并使属性值随意可用。因此应该认为 JNDI 资源文件是“所有人可读的”,敏感信息(比如明文密码)不应该存储在那里。
有两种 JNDI 资源文件:提供程序 和应用程序。
提供程序资源文件
每个服务提供程序都有一个可选的资源,该资源列出了特定于该提供程序的属性。此资源的名称是:
[prefix/]jndiprovider.properties
其中 prefix 是提供程序的上下文实现的包名称,其每个句点 (".") 都被转换成一个斜杠 ("/")。 例如,假设服务提供程序定义了一个带有类名称 com.sun.jndi.ldap.LdapCtx 的上下文实现。此提供程序的提供程序资源被命名为 com/sun/jndi/ldap/jndiprovider.properties。如果该类不在一个包中,则资源的名称就是jndiprovider.properties。
JNDI 类库中的某些方法使用指定 JNDI 工厂列表的标准 JNDI 属性:
- java.naming.factory.object
- java.naming.factory.state
- java.naming.factory.control
- java.naming.factory.url.pkgs
在确定这些属性的值时,JNDI 库将参考提供程序资源文件。这以外的属性可由服务提供程序在提供程序资源文件中设置。服务提供程序的文档应该明确声明哪些属性是被允许的;文件中的其他属性将被忽略。
应用程序资源文件
在部署应用程序时,该应用程序通常将在其类路径中生成若干代码基目录和 JAR。类似地,在部署 applet 时,它将有一个指定 applet 类所处地址的代码基和档案文件。JNDI 查找(使用ClassLoader.getResources()
)类路径中所有名为 jndi.properties 的应用程序资源文件。此外,如果文件 java.home/lib/jndi.properties 存在并且是可读的,则 JNDI 会将其视为一个额外的应用程序资源文件。(java.home 指示由 java.home 系统属性命名的目录。)包含在这些文件中的所有属性都被放置在初始上下文环境中。然后此环境由其他上下文继承。
对于同时出现在多个应用程序资源文件中的每个属性,JNDI 使用最先找到的值,或者在少数有意义的情况下串联所有这些值(细节在下文给出)。例如,如果在三个 jndi.properties 资源文件中存在 "java.naming.factory.object" 属性,则对象工厂列表是所有三个文件中的属性值的串联。使用此方案,每个可部署组件都要负责列出它导出的工厂。JNDI 在搜索工厂类时自动收集和使用所有这些导出列表。
从 Java 2 Platform 开始可使用应用程序资源文件,java.home/lib 中的文件除外,它在较早的 Java 平台上也可以使用。
属性的搜索算法
当 JNDI 构造一个初始上下文时,该上下文的环境是使用传递给构造方法的环境参数中定义的属性、系统属性、applet 参数和应用程序资源文件进行初始化的。有关细节请参见 InitialContext。然后此初始环境由其他上下文实例继承。
如果 JNDI 类库需要确定某一属性的值,它将通过按顺序合并取自以下两个源的值来实现这一点:
- 将在其上执行操作的上下文的环境。
- 将在其上执行操作的上下文的提供程序资源文件 (jndiprovider.properties)。
对于每个同时存在于这两个源中的属性,JNDI 用以下方式确定属性的值。如果该属性是指定 JNDI 工厂列表的标准 JNDI 属性之一(如上文所列),则这些值被串联成一个以冒号分隔的列表。对于其他属性,只使用最先找到的值。
当服务提供程序需要确定某一属性的值时,它通常将直接从环境中获取该值。服务提供程序可以定义将置于其本身提供程序资源文件中的特定于提供程序的属性。在这种情况下,它应该根据上文所述合并这些值。
这样,每个服务提供程序开发人员便可以指定与该服务提供程序一起使用的工厂列表。这可以由应用程序或 applet 的部署方指定的应用程序资源修改,而这些资源又可以由用户修改。
如上即为所查API关于jndi.properties文件的全部说明。总结起来即为:
jndi.properties是jndi初始化文件。通常我们有两种方式来创建一个初始上下文:
1.通过创建一个Properties对象,设置Context.PROVIDER_UR,Context.InitialContextFactroy等等属性,创建InitialContext,例如:
Properties p = new Properties();
p.put(Cotnext.PROVIDER_URL, "localhost:1099 ");//主机名和端口号
//InitialContext的创建工厂类(类名是我乱写的)
p.put(Context.InitialContextFactroy, "com.sun.InitialContextFactory ");
InitialContext ctx = new InitialContext(p);
2.通过jndi.properties文件创建初始上下文
java.naming.factory.initial=com.sun.NamingContextFactory
java.naming.provider.url=localhost:1099
如果直接创建初始上下文,如下:
InitialContext ctx = new InitialContext();
InitialContext的构造器会在类路径中找jndi.properties文件,如果找到,通过里面的属性,创建初始上下文。
所以从上面可以看出,两种方式完成的目标是相同的。
因此,对于本地测试(并且JNDI资源没有设置安全属性)可以不添加properties属性,但是如果要访问远程的JNDI资源,就必须用饱含JNDI环境参数Hashtable初始化InitialContext。
必要的环境参数如:
Context.INITIAL_CONTEXT_FACTORY//连接工厂
Context.PROVIDER_URL//访问连接
Context.SECURITY_PRINCIPAL//安全用户
Context.SECURITY_CREDENTIALS//用户密码
有时会出现如NoInitialContextException是因为无法从System.properties中获得必要的JNDI参数中获得必要的JNDI参数,在服务器环境下,服务器启动时就把这些参数放到System.properties中了,于是直接new InitialContext()就搞定了,不要搞env那么麻烦,搞了env你的代码还无法移植,弄不好管理员设置服务器用的不是标准端口还照样抛异常。
但是在单机环境下,可没有JNDI服务在运行,那就手动启动一个JNDI服务。我在JDK 5的rt.jar中一共找到了4种SUN自带的JNDI实现:LDAP,CORBA,RMI,DNS。
这4种JNDI要正常运行还需要底层的相应服务。一般我们没有LDAP或CORBA服务器,也就无法启动这两种JNDI服务,DNS用于查域名的,以后再研究,唯一可以在main()中启动的就是基于RMI的JNDI服务。
现在我们就在main()中启动基于RMI的JNDI服务并且绑一个Date对象到JNDI上:
LocateRegistry.createRegistry(1099);
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
System.setProperty(Context.PROVIDER_URL, "rmi://localhost:1099");
InitialContext ctx = new InitialContext();
class RemoteDate extends Date implements Remote {};
ctx.bind("java:comp/env/systemStartTime", new RemoteDate());
ctx.close();
注意,我直接把JNDI的相关参数放入了System.properties中,这样,后面的代码如果要查JNDI,直接new InitialContext()就可以了,否则,你又得写Hashtable env = ...
这段话里提到了system.properties属性,这就是调用system.getProperties的来由,猜想应该是先找到jndi.properties文件,之后通过System.setProperties(),而后通过System.getProperties()来得到。
相关推荐
- **通过jndi.properties文件**:可以在`CLASSPATH`路径中或`$JAVA_HOME/lib/`目录下放置`jndi.properties`文件来指定缺省属性。 - **通过Hashtable类**:创建一个`Hashtable`对象,并将环境属性放入其中,再传递给`...
2. 创建初始上下文:这是JNDI查找的第一步,通过`InitialContext`类的构造函数,提供一个环境属性列表,其中包含了获取服务器上下文所需的配置信息。 ```java Properties props = new Properties(); props.put...
- **jndi.properties**:配置JNDI的环境变量,如目录服务的位置和类型。 - **Java系统属性**:通过`System.setProperty()`设置JNDI相关属性。 6. **最佳实践** - **资源的管理**:避免硬编码JNDI名,使用配置...
InitialContext context = new InitialContext(); DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/MyDB"); Connection conn = ds.getConnection(); ``` - **绑定资源**: ```java context...
- **配置文件**:确保JNDI环境配置正确,例如`jndi.properties`文件。 - **环境变量**:设置适当的环境变量,如`JAVA_HOME`和`CLASSPATH`。 - **安全策略**:根据需要配置安全策略文件以允许网络访问和其他敏感操作...
这是因为`InitialContext`无法从`System.properties`中获取必要的JNDI参数。 为了解决这一问题,可以通过显式设置环境属性来创建`InitialContext`实例: ```java Hashtable env = new Hashtable(); env.put...
hibernate.properties # # Hibernate, Relational Persistence for Idiomatic Java # # License: GNU Lesser General Public License (LGPL), version 2.1 or later. # See the lgpl.txt file in the root directory...
**JNDI**(Java Naming and Directory Interface)是Java平台的一部分,用于开发与名称和服务目录进行交互的应用程序。它允许Java应用程序查找并使用远程对象和服务。在J2EE环境中,JNDI提供了一种标准化的方式来...
- **松耦合**:JNDI允许应用程序与资源之间保持松散耦合,因为它们之间的联系是通过名称而不是硬编码的实例引用。 - **可扩展性**:随着系统的发展,可以轻松添加新的命名或目录服务,而无需修改现有代码。 - **集中...
- 在Servlet或JSP中,可以使用`InitialContext`查找JNDI资源: ```java InitialContext ic = new InitialContext(); DataSource ds = (DataSource) ic.lookup("jdbc/myDataSource"); Connection conn = ds.get...
这样,客户端只需知道EJB的JNDI名称,而无需直接与EJB的实现细节打交道,增强了系统的可扩展性和可维护性。 3. 初始化Context: 使用JNDI时,首先需要创建一个Context对象,它是与特定命名和目录服务的连接点。在...
1. **JNDI的基本概念与作用**: - JNDI 提供了一种统一的接口,使得开发者可以透明地访问各种类型的命名和目录服务,如LDAP、DNS、RMI等。 - 它允许应用通过逻辑名称查找物理资源,如数据源、EJB等,使应用更具可...
EJB中的JNDI绑定则是将对象与一个JNDI名称关联的过程,使得其他应用程序可以通过这个名称找到该对象。这在部署EJB或配置资源时特别有用。例如,我们可能会将一个数据源绑定到JNDI名称`jdbc/MyDataSource`: ```java...
3. 一旦有了`InitialContext`,就可以像操作其他JNDI上下文一样,使用`lookup`方法查找或`bind`方法绑定文件系统路径了。 在实际应用中,这种基于文件系统的JNDI实现可能用于简化配置,或者在没有实际目录服务的...
通过以上步骤,我们成功地在Weblogic服务器上配置了Hibernate,并使其通过JNDI与数据库连接池集成。这种方式不仅方便了事务管理,还提高了应用程序的可移植性和可管理性。在实际的生产环境中,这种配置方法可以...
properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); properties.put(Context.PROVIDER_URL, "rmi://localhost:1099"); InitialContext context = new ...
如果客户端和服务器位于不同的Java虚拟机上,则需要在客户端提供一个`jndi.properties`文件,该文件应包含如下信息: - `java.naming.factory.initial`: 初始化上下文工厂类名。 - `java.naming.provider.url`: ...
在实际应用中,开发者通常会使用`InitialContext`作为JNDI的入口点,通过构造函数传入环境属性来初始化上下文,并进行后续的查找或绑定操作。例如,查找名为“java:comp/env/jdbc/MyDataSource”的数据源: ```java...
1. **导入JNDI库**:在Java项目中,需要引入`jndi.properties`配置文件,并导入相应的JNDI API库,如`javax.naming`和`javax.naming.directory`包下的类。 2. **创建Initial Context**:这是JNDI的第一步,通过`...
JNDI通常与Java应用程序服务器一起使用,帮助开发者在分布式环境中管理资源。 在JNDI中,命名服务允许程序将名字与对象关联起来,而目录服务则扩展了命名服务,添加了属性查询和分类功能。通过JNDI,开发者可以方便...