JNDI 是 Java 里「按名字找资源」的统一接口,但它自己什么都不做,全靠提供者。这篇不用任何应用服务器:自己写一个提供者,用 JDK 自带的 RMI 与 LDAP 提供者连真实的服务,逐项看 bind / lookup / search 和各种异常。
1 没有任何配置就 new InitialContext().lookup("java:comp/env/jdbc/x"):javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial
lookup("foo"):javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial
JNDI 相关系统属性的默认值:java.naming.factory.initial=null;java.naming.provider.url=null;java.naming.factory.url.pkgs=null
安全相关默认值:com.sun.jndi.ldap.object.trustURLCodebase=null(未设置时按 false 处理);com.sun.jndi.rmi.object.trustURLCodebase=null;jdk.jndi.object.factoriesFilter=null;jdk.jndi.ldap.object.factoriesFilter=null2 自己的 InitialContextFactory:lookup(jdbc/main) = Cfg[url=jdbc:h2:mem:x, size=5];lookup(mail/host) = smtp.example.invalid
list():[jdbc/main→Cfg, mail/host→java.lang.String]
重复 bind:javax.naming.NameAlreadyBoundException: mail/host
lookup 不存在的名字:javax.naming.NameNotFoundException: nope not bound
unbind(代理里没实现):javax.naming.OperationNotSupportedException: unbind
Context.INITIAL_CONTEXT_FACTORY 的取值来源:显式传 env 优先于系统属性与 jndi.properties;当前 env={java.naming.factory.initial=Main$MemFactory}3 JDK 自带的 RMI Registry 提供者:javax.naming.InitialContext bind 一个只是 Serializable 的记录:java.lang.IllegalArgumentException: RegistryContext: object to bind must be Remote, Reference, or Referenceable bind 一个 javax.naming.Reference,lookup 得到:javax.naming.Reference,地址 host=db1.example.invalid(没有 ObjectFactory,所以返回的仍是 Reference) bind 一个导出的 Remote 对象,lookup 得到 动态代理 stub,调用 echo 返回 echo:x listBindings():[ref→Reference, echo→Proxy];registry.list() 直接看:[ref, echo] 重复 bind:javax.naming.NameAlreadyBoundException: echo ← java.rmi.AlreadyBoundException: echo lookup 不存在:javax.naming.NameNotFoundException: none rename 之后 list:[echo2, ref] unbind(ref) 之后 registry.list():[echo2]
ldap ready
4 JDK 的 LDAP 提供者 com.sun.jndi.ldap.LdapCtxFactory 连接内存 LDAP 服务器(UnboundID):javax.naming.directory.InitialDirContext
getAttributes(uid=alice,ou=people):{cn=Alice, createTimestamp=20261003162735.795Z, creatorsName=cn=Internal Root User, entryDN=uid=alice,ou=people,dc=example,dc=com, entryUUID=527503a9-d07e-4d6b-b419-ea8154fbea8e, mail=alice@example.com, modifiersName=cn=Internal Root User, modifyTimestamp=20261003162735.795Z, objectClass=inetOrgPerson, sn=A, subschemaSubentry=cn=schema, uid=alice}
search (&(objectClass=inetOrgPerson)(cn=*a*)):[uid=alice alice@example.com]
createSubcontext + modifyAttributes 之后 carol 的 mail:carol@new.example.com
lookup 不存在的条目:javax.naming.NameNotFoundException: [LDAP: error code 32 - Unable to perform the search because base entry 'uid=nobody,ou=people,dc=example,dc=com' does not exist in the server.]
重复创建:javax.naming.NameAlreadyBoundException: [LDAP: error code 68 - An entry with DN 'uid=carol,ou=people,dc=example,dc=com' already exists in the server.]
错误密码:javax.naming.AuthenticationException: [LDAP: error code 49 - Unable to bind as user 'cn=admin,dc=example,dc=com' because the provided password was incorrect.]