该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-05-16
声明:该贴并不讨论Domain Model对于企业应用是否有意义,JE已经讨论过很多了,有很多优秀的帖供参考,这里仅谈实现。 其次,领域模型的生命周期不依赖环境, 不依赖hibernate的构造,不被spring管理等。一般的情况,dao都是无状态的,单例的,那么把领域模型中的依赖的dao设置为static,static是class级别的,和具体的instance没关系,只要在应用初始化的时候给所有的领域模型的class注入一次,整个应用运行的时间周期,领域模型随意怎么new,在哪new,new出来的Domain Object的都可以访问一开始被注入的static的dao,实现了领域模型的生命周期不依赖环境,这样也易于测试,易于MOCK,和目前传统编程方式差别很小,仅仅把dao改为static的。
但是spring可以实现对一个class的静态peroperty注入吗?答案是不能(经bottom同学修正,spring是可以静态property注入,但是还是要在所有bean初始化之前注入才行,所以还是要扩展,我在3楼已经回复说明),但是spring是个优美扩展性强的框架,我们只要稍微扩展一下,就可以达到我们的目的。 /** * Finish the initialization of this context's bean factory, * initializing all remaining singleton beans. */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // 省略 // Instantiate all remaining (non-lazy-init) singletons. // 这里,会初始化所有的单例 beanFactory.preInstantiateSingletons(); }
private class StaticPropertyInjectSupportListableBeanFactory extends DefaultListableBeanFactory { @Override//覆盖父类的方法 public void preInstantiateSingletons() throws BeansException { //先注入所有的领域模型的class injectStaticPropertyForBasePackage(); //再调用父类的该方法去初始化所有的非LAZY单例 super.preInstantiateSingletons(); } } 继续观察代码,finishBeanFactoryInitialization的方法的参数ConfigurableListableBeanFactory,是通过调用的createBeanFactory方法获得的,默认的实现在AbstractRefreshableApplicationContext中有这样一个方法,去创建DefaultListableBeanFactory,而这个方法是protected的,摆明着让我们去复盖扩展其功能,这也是spring易于扩展的体现,我们只要实现一个自己的ApplicaitonContext,覆盖createBeanFactory方法创建我们刚才自己实现的StaticPropertyInjectSupportListableBeanFactory就OK了,代码也巨简单 public class StaticPropertyInjectSupportXmlWebApplicationContext extends XmlWebApplicationContext { @Override protected DefaultListableBeanFactory createBeanFactory() { return new StaticPropertyInjectSupportListableBeanFactory(getInternalParentBeanFactory()); } }
<!--加入这两个参数,其余不变 --> <!--告诉spring用我们自己实现的支持静态bean注入的context --> <context-param> <param-name>contextClass</param-name> <param-value>com.norther.sps.StaticPropertyInjectSupportXmlWebApplicationContext</param-value> </context-param> <!--注入com.norther包下(包含子包)所有的领域模型的class--> <context-param> <param-name>configurableBeanBasePackage</param-name> <param-value>com.norther</param-value> </context-param>
那么如何辨别哪些是领域模型呢?就利用了spring的Configurable这个annotation去标识这个是需要静态注入的领域模型,然后自己实现了两种自动装配,BY_TYPE和BY_NAME,以及JSR-250的Resource annotation,例子如下 @Configurable public class Student { private Long id; @Resource //注入field的名字所代表的bean, private static StudentDao studentDao; public static void setStudentDao(StudentDao studentDao) { Student.studentDao = studentDao; }
@Configurable public class Assistant { private static StudentDao studentDao; @Resource(name = "studentDao") //注入name为studentDao的bean public static void setDao(StudentDao studentDao) { Assistant.studentDao = studentDao; } @Configurable(autowire = Autowire.BY_TYPE)//自动装配 public class SchoolMaster { private static StudentDao studentDao; // by type public static void setDao(StudentDao studentDao) { SchoolMaster.studentDao = studentDao; } 单元测试以及Mock @Configurable(autowire = Autowire.BY_NAME)//自动装配 public class Teacher { private static StudentDao studentDao; public static void setStudentDao(StudentDao studentDao) { Teacher.studentDao = studentDao; } public List<Students> getStudents() { return studentDao.getByClassId(this.getClassId()); }
StudentDao studentDao = EasyMock.createMock(StudentDao.class); EasyMock.expected(studentDao.getByClassId(998877)).andReturn(students); Teacher.setStudentDao(studentDao); Teacher zhang3 = new Teacher(); zhang3.setClassId(998877);//设置班级ID List<Student> actual = zhang3.getStudents(); .......
当然这个代码只是demo性质的,实现的比较简单,只有web方式的实现方法,希望和大家共同讨论。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-05-16
但是spring可以实现对一个class的静态peroperty注入吗?答案是不能。
这个算不算是静态peroperty注入? <bean id="teacher" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod"> <value>com...Teacher.setStudentDao</value> </property> <property name="arguments"> <list> <ref bean="studentDao" /> </list> </property> </bean> |
|
返回顶楼 | |
发表时间:2008-05-16
谢谢楼上同学的纠正,这个当然算静态property注入,但是,在bean的初始化的时候,每个bean初始化的时间是不确定的,仅spring管理的bean有依赖初始化的关系,而我们的domain model是class级别的注入,这个依赖关系,spring无法知道,而只有在这个MethodInvokingFactoryBean被spring初始化的时候才去注入,有可能这个时候,其他调用Domain Object的quartz的job已经再开始跑了,但是他们去执行一些domain object的方法时,他们都还没有被注入,所以必须要要在所有的bean初始化之前去注入,才能保证所有的spring管理的bean去调用domain object时,他们都是被注入过的。
|
|
返回顶楼 | |
发表时间:2008-05-16
利用static的缺陷就是在一个JVM中,它是一直在那的。如果有多个测试用一个JVM跑的话,容易产生上下文依赖。而且static隐含的要求了所有的依赖都必须是singleton的。
同时,个人认为mock DAO实在太麻烦,而且没法测试到行为,只能defend你的实现。 |
|
返回顶楼 | |
发表时间:2008-05-16
你这个思路个人感觉的确不错,值得一试。但是仍有一个非常关键的问题没有解决,那就是事务问题。按照你这种方式实现的Rich Domain Object怎么配置事务呢?事务放在哪一层?
|
|
返回顶楼 | |
发表时间:2008-05-16
taowen 写道 利用static的缺陷就是在一个JVM中,它是一直在那的。如果有多个测试用一个JVM跑的话,容易产生上下文依赖。而且static隐含的要求了所有的依赖都必须是singleton的。
同时,个人认为mock DAO实在太麻烦,而且没法测试到行为,只能defend你的实现。 1 这个缺陷很好克服,在tearDown时给他set成Null就行了,static也不是在一个jvm,是classloader级别的,另外产生下文依赖不是很明白什么意思? 2 static要求所有的依赖都是单例的,这个要求大家都已经实现了,普遍情况下,被spring管理的dao和service也都是无状态的,单例的,IOC就是被注入,是被动的,bean是无法控制注入进来的对象的生命周期,至少不是他构造的, 3 mock dao的麻烦在哪?能否请taowen同学说明,我不是很理解。mock是不能测试行为,他仅仅测试接口之间的约定,那用stub就可以了,易于mock的意义不在于mock本身,而在于弱耦合可以随意替换实现,不管mock还是什么其他的方式, set进去就好了。 |
|
返回顶楼 | |
发表时间:2008-05-16
moonranger 写道 你这个思路个人感觉的确不错,值得一试。但是仍有一个非常关键的问题没有解决,那就是事务问题。按照你这种方式实现的Rich Domain Object怎么配置事务呢?事务放在哪一层?
方式很多,粒度粗的直接在action的execute声明事务,粒度细的可以用facade去包装一下,在facade方法上声明事务,粒度再细的直接上spring transaction template, |
|
返回顶楼 | |
发表时间:2008-05-16
Norther 写道 谢谢楼上同学的纠正,这个当然算静态property注入,但是,在bean的初始化的时候,每个bean初始化的时间是不确定的,仅spring管理的bean有依赖初始化的关系,而我们的domain model是class级别的注入,这个依赖关系,spring无法知道,而只有在这个MethodInvokingFactoryBean被spring初始化的时候才去注入,有可能这个时候,其他调用Domain Object的quartz的job已经再开始跑了,但是他们去执行一些domain object的方法时,他们都还没有被注入,所以必须要要在所有的bean初始化之前去注入,才能保证所有的spring管理的bean去调用domain object时,他们都是被注入过的。
既然你的调用是有依赖的 用spring配置了这些有依赖关系的bean 那么初始化的时候也肯定会根据这个关系生成实例,怎么会和你说的这样呢? 比方说 a 依赖于b 在实例化a的时候 发现b还没有实例化 那肯定会先去实例化b啊,即使lazy了 哪么用的时候肯定要实例化了吧 |
|
返回顶楼 | |
发表时间:2008-05-16
moonranger 写道 你这个思路个人感觉的确不错,值得一试。但是仍有一个非常关键的问题没有解决,那就是事务问题。按照你这种方式实现的Rich Domain Object怎么配置事务呢?事务放在哪一层?
事物和原来差别不大吧 domain涵盖了service的功能而已 |
|
返回顶楼 | |
发表时间:2008-05-16
static的话 数据库连接限制很大吧 针对同一个domain只能有一个db session了吧?
|
|
返回顶楼 | |