RMI 报错:javax.naming.NoInitialContextException: specify class name environment system property

1 项目简介

  • 测试Java RMI 远程调用功能
  • 通过 JDK 1.3 及以下版本的方法(Naming.rebind())来注册远程对象可以正常运行
  • 通过 JDK 1.3 以上版本的方法 (new InitialCOntext()j.rebind()) 来注册远程对象报错

2 报错代码

public class StudentServer {
    public static void main(String[] args) throws Exception{
        StudentService stu = null;
        //在本地主机上创建和导出注册表实例,并在指定的端口上接受请求
        LocateRegistry.createRegistry(1099);
        stu = new StudentServiceImpl();
        //JDK 1.3及以下
		//Naming.rebind("stu",stu);//这里可以正常注册

        //JDK 1.3 以上
        Context namingContext = new InitialContext();
        namingContext.rebind("stu",stu);//在这里报错!
        System.out.println("服务器已启动!");
    }
}

3 报错代码跟踪

  • 从 rebind() 方法开始调试
public void rebind(String name, Object obj) throws NamingException {
    getURLOrDefaultInitCtx(name).rebind(name, obj);
}
protected Context getURLOrDefaultInitCtx(String name)
    throws NamingException {
    //如果已经有默认的上下文了,就直接返回默认上下文
    if (NamingManager.hasInitialContextFactoryBuilder()) {
        return getDefaultInitCtx();
    }
    //通过 name 得到上下文的主题(这里是通过分割 name 字符串中的 :和/ 符号,来得到主题的!!)
    String scheme = getURLScheme(name);
    if (scheme != null) {
    	//如果有主题,则通过主题来创建上下文
        Context ctx = NamingManager.getURLContext(scheme, myProps);
        if (ctx != null) {
            return ctx;
        }
    }
    //否则返回默认主题
    return getDefaultInitCtx();//因为 scheme 为null,错误代码运行到这里!
}
  • 获取默认上下文出错
protected Context getDefaultInitCtx() throws NamingException{
    if (!gotDefault) {//这里 gotDefault 为 false,因为在 new InitialContext(),没有调用这个方法,所以 gotDefault 是默认的false
        defaultInitCtx = NamingManager.getInitialContext(myProps);
        gotDefault = true;
    }
    if (defaultInitCtx == null)
        throw new NoInitialContextException();

    return defaultInitCtx;
}
protected Context getDefaultInitCtx() throws NamingException{
    if (!gotDefault) {
        defaultInitCtx = NamingManager.getInitialContext(myProps);//进入这里出错
        gotDefault = true;
    }
    if (defaultInitCtx == null)
        throw new NoInitialContextException();
    return defaultInitCtx;
}
public static Context getInitialContext(Hashtable<?,?> env)
    throws NamingException {
    InitialContextFactory factory;
    InitialContextFactoryBuilder builder = getInitialContextFactoryBuilder();//这里builder为null
    if (builder == null) {
        // 没有相关工厂被创建,获取环境变量中的初始化上下文工厂的 类名(className)
        // Get initial context factory class name
        String className = env != null ?
            (String)env.get(Context.INITIAL_CONTEXT_FACTORY) : null;
        // 因为没有找到对应的工厂类名,所以报错!!
        if (className == null) {
            NoInitialContextException ne = new NoInitialContextException(
                "Need to specify class name in environment or system " +
                "property, or as an applet parameter, or in an " +
                "application resource file:  " +
                Context.INITIAL_CONTEXT_FACTORY);
            throw ne;
        }
		//代码略
}
}

4 报错原因分析

  • 从JDK1.3以上版本开始,RMI的命名服务API被整合到JNDI(Java Naming and Directory Interface,Java名字与目录接口)中
  • 通过 JNDI 来注册远程,必须要在 name 中指定主题为 rmi,即 name = “rmi:…”,这样 JNDI 才可以通过获取到的 rmi 主题,来创建相应的上下文。正确代码如下:
	Context namingContext = new InitialContext();
	namingContext.rebind("rmi:stu",stu);//name 的完整形式为: "rmi:ip/port/name",默认为 localhost/1099
  • 因为 JDK 1.2 之前调用的是 java.rmi.Naming 类,这里就已经明确了主题是 rmi,所以正常运行

5 其它错误原因

参考

github 源码地址
Java RMI远程方法调用详解
java项目中rmi远程调用实例
Java RMI 远程方法调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值