• 2004-06-09

    初始化游戏

    Tag:向上走

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/214567.html

    我们有两个类,A中的一个属性是B的实例,代码如下:
    public class A {
        private B b;
        ...
    }

    public class B {
        ...
    }

    以Spring完成代码时,通常我们会选择以Setter Injection将B的一个实例注射进去。不过,那么做法会使我们的游戏失去意义,我们换一种方式。

    在Spring的配置中,可以使用init-method定义一个初始化方法,这个方法会在bean构造的过程中执行,这就给了bean一个执行初始化动作的机会。我们就选择在这个初始化方法中获取B的实例。
    随之而来的问题是,从哪获取。

    这个问题很好解决,做一个全局的访问点,我们可以通过它获取bean的实例,下面是一种实现方式:
    public class Global {
        private static ApplicationContext ctx;

        static {
            try {
                ctx = new ClassPathXmlApplicationContext("config.xml");
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }

        public static Object getBean(String name) {
            return ctx.getBean(name);
        }
    }
    这里,我们用到Spring中ApplicationContext,接下来一切就简单了,A的初始化方法通过它来获取B。
    public void init() {
        b = (BeanB)Global.getBean("b");
    }

    补上我们欠下的配置文件,这样才显得完整。
    <bean id="a" class="A" init-method="init"/>
    <bean id="b" class="B"/>

    好了,加上启动代码一切就万事OK了。
    public static void main(String[] args) throws Exception {  
        BeanA a = (BeanA)Global.getBean("a");
    }

    可以跑了,运行结果怎么样?如果一切正常的话,出现在我们你看到的将是空指针异常,位置就在getBean的ctx那里。

    怎么会这样?从输出信息上,我们没有看到ctx初始化失败的信息,所以调用ctx的时候,它应该已经初始化成功,难不成Java的new会new出空来,这不是一个超大规模的应用,内存不会这么轻易被耗尽吧!

    我还是不要把大家在错误的方向上引得太远。
    仔细看一下异常信息,调用getBean就是我们的init方法,换句话说,是在A初始化的时候出现了异常。答案很简单,因为A的初始化是在ctx构造的过程中完成的,所以,A在初始化的时候ctx还没有构造完成,所以,当然赋值操作还没有做,所以ctx依然为空,这时候的调用当然会出现一个空指针异常了。

    也许你会质疑这种做法的必要性,其实我也怀疑。不过可以肯定的是,这段代码不是我凭空杜撰出来的代码,我的一个同事确实写出了这样的代码,也确实遇到了这个问题。
    事以至此,怨天尤人没有用,还是考虑一下怎么解决问题吧!

    出问题的主要原因是在A的init方法中,调用尚未完成构造的ctx。如果ctx能够完成构造,这个问题也就迎刃而解了。怎么能在调用A的init方法之前完成ctx的构造呢?我们换一个角度来考虑问题,如果ctx的构造时A不进行初始化,问题不也就解决了。也就是说,我们需要推迟A的初始化。
    推迟A的初始化……
    出现在我大脑中的是lazy-init的概念,不错,Spring支持lazy-init的概念,也就是将初始化推迟到不能再推的时候再去执行。

    我们可以不修改一行代码,而只修改配置文件就支持lazy-init的概念。
    <bean id="a" class="A" init-method="init" lazy-init="true"/>
    再运行一下,世界从此清净了。

    如果不能lazy-init怎么办?比如说我们会在init方法中启动一个服务监听来自客户端的请求,我们需要它在系统开始运行的时候就启动,lazy-init可能会让它在系统启动后丢掉许多请求。
    从前面的例子可以看出,我对这个问题没有更好的答案。

    不过,还回到前面提过的一个话题上,这种写法本身的合理性就值得质疑,如果改成Setter Injection,这些问题也就不是问题了。如果真有这种需求,我们何必在一棵树上吊死呢?

    分享到:

    历史上的今天:

    欢迎回归 2010-06-09
    Hello Hivemind 2005-06-09
    引用地址:

    评论

  • 这样的玩法实在是没有什么意思阿. 觉得.
  • Container Dependency, Concrete Class Dependency , 你的同事怎么可以写出这样垃圾的代码?


    Spring能够被使用成这个样子,也真是佩服。
  • 楼主的文章挺有深度的,能否在spring中文论坛上转载一下?
    回复ah_cai说:
    我就是Spring中文论坛的一个版主,所以肯定会转到其中的,先在自己的blog中让大家评论一下。
    2004-06-10 10:22:43