3.3 模型层结构
Model层为整个系统的核心部分,完成应用的业务逻辑及与数据库的通信。AppFuse中将Model分为两层:持久层和业务层。采用Spring+Hibernate框架实现,这里以对用户User数据的操作为例详细阐述其实现方式。
对持久化数据的访问基于DAO(Data Access Object)模式实现。DAO模式提供了访问关系型数据库系统所需的所有接口操作的接口。DAO模式将底层数据访问操作与高层业务逻辑分离开,对上层提供面向对象的数据访问接口。
Model层与User相关的类有:
POJO:
User:管理员表的业务对象。
业务层:
UserManager:业务层接口,为控制层所调用。
UserManagerImpl:业务层接口的实现,调用持久层接口。
持久层:
UserDAO:持久层接口,为业务层实现所调用。
UserDAOHibernate:持久层接口的实现。
XML配置文件:
applicationContext-service.xml:业务层接口的配置文件。
applicationContext-hibernate.xml:持久层接口的配置文件。
3.3.2 Spring的IoC
Ioc(Inversion of Control)即反转控制。Ioc模式即Dependency Injection模式是依赖注射的意思,也就是将依赖先剥离,然后在适当时候再注射进入。
Spring的轻量级的bean容器为业务对象(business objects)、DAO对象和资源(如:JDBC数据源或者Hibernate
SessionFactorie等)对象提供了IoC类型的装配能力。Spring使用一个xml格式的应用配置文件为开发者提供了一种通过解析定制的属
性文件来手动管理单实例对象或者工厂对象的选择性。由于Spring将非入侵性做为一个重要的目标,因此可以由Spring配置管理的bean对象均不需
要依赖Spring自有的接口和类就可以通过它们的bean属性完成配置。
就实现上来讲Spring采取了配置文件的形式来实现依赖的注射,并且支持Type2 IOC(Setter Injection)以及Type3 IOC(Constructor Injection)。
在Model层,使用Spring提供的Setter Injection(type2)注入方式。以User为例,下面是其用法。
在applicationContext- hibernate.xml中定义
<bean id="userDAO" class="org.appfuse.dao.hibernate.UserDAOHibernate">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
在UserManager类中有一句:
public void setUserDAO(UserDAO dao);
这就是一个DAO Object设置方法(注射器)。UserDAO将被调用,和持久层通信。以这种方式创建UserDAO的实例,同样达到了由UserManager创建UserDao的目的。避免了直接实例化UserDAO的实现而使业务层和持久层紧密耦合。
在控制层调用业务层方法时,使用服务定位器返回给Spring context,Spring的BeanFactory提供了getBean方法。BeanFactory是一个通用的Factory,它使对象能够按名称获取,并且能管理对象之间的关系。
在applicationContext-service.xml中配置
<bean id="userManager" parent="txProxyTemplate">
<property name="target">
<bean class="org.appfuse.service.impl.UserManagerImpl">
<property name="userDAO"><ref bean="userDAO"/></property>
</bean>
</property>
</bean>
在控制层BaseAction定义通用方法:
private static ApplicationContext ctx = null;
public Object getBean(String name) {
if (ctx == null) {
ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(servlet.getServletContext());
}
return ctx.getBean(name);
}
在UserAction中创建UserManager的实例:
UserManager mgr = (UserManager) getBean("userManager");
这样,通过BeanFactory的getBean方法,以及xml配置文件,避免了在UserAction类中直接实例化UserManager,消除了控制层与业务层及业务层与持久层之间的耦合,实现了依赖的注射。
ApplicationContext 是BeanFactory的子接口,为下列东西提供支持:
信息查找,支持着国际化
事件机制,允许发布应用对象以及可选的注册以接收到事件
可移植的文件和资源访问
3.3.3 Spring的事务管理
在数据持久层的杰出贡献,可能是Spring最为闪亮的优点。
Spring提供了通过容器的集约式参数化事务机制,实现事务的外部管理。容器管理的参数化事务为程序开发提供了相当的灵活性,同时因为将事务委托给容器
进行管理,应用逻辑中无需再编写事务代码,大大节省了代码量(特别是针对需要同时操作多个事务资源的应用),从而提高了生产率。
AppFuse在applicationContext-service.xml文件中进行了对事务的配置
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
这里定义了一个名为txProxyTemplate的TransactionProxyFactoryBean服务。它对包含实际数据逻辑的持久层对象进
行了事务的封装。在这里,通过transactionAttributes属性,我们指定了事务的管理策略,即将所有的名称以save和remove开头
的方法纳入事务管理范围。如果此方法中抛出异常,则Spring将当前事务回滚,如果方法正常结束,则提交事务。
而对所有其它方法则以只读的事务处理机制进行处理。设为只读型事务,可以使持久层尝试对数据操作进行优化,如对于只读事务Hibernate将不执行flush操作,而某些数据库连接池和JDBC 驱动也对只读型操作进行了特别优化。
如果有其他的方法需要进行写数据库操作,可以在相应的Manager配置中声明。如在UserManager中,就添加了属性
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="*LoginCookie">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
这样,以LoginCookie结尾的方法也可以写数据库了。
Spring可以将任意Java Class 纳入事务管理,而无需对其进行任何修改,因此我们的类可能完全不知道它正在被进行事务管理。
3.3.3 Spring+Hibernate操作持久层
Spring对Hibernate有很好的支持。
Hibernate中通过SessionFactory创建和维护Session。Spring对SessionFactory的配置进行了整合,无需再
通过Hibernate.cfg.xml对SessionFactory进行设定。SessionFactory节点的mappingResources
属性包含了映射文件的路径,list节点下可配置多个映射文件。hibernateProperties节点则容纳了所有的属性配置。可以对应传统的
Hibernate.cfg.xml文件结构对这里的SessionFactory配置进行解读。
下面是HibernateSessionFactory 和 HibernateTransactionManager的配置:
在applicationContext-hibernate.xml中:
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
<property name="mappingResources">
<list>
<value>com/mycompany/model/User.hbm.xml</value>
……………………………
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">@HIBERNATE-DIALECT@</prop>
<!--prop key="hibernate.show_sql">true</prop-->
<!--prop key="hibernate.hbm2ddl.auto">update</prop-->
</props>
</property>
</bean>
Spring 提供了一个
HibernateTransactionManager,采用面向Hibernate的TransactionManager实现:
org.springframework.orm.hibernate.HibernateTransactionManager。他用线程捆绑了一个
Hibernate Session,用它来支持transactions。
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
sessionFactory Bean引用了HibernateSessionFactory,而transactionManager
Bean引用了HibernateTransactionManage。 transactionManager
Bean中有个sessionFactory属性。 HibernateTransactionManager有个sessionFactory
setter 和 getter方法,用来在Spring启动的时候实现“依赖注入” (dependency injection)的。
在sessionFactory 属性里引用sessionFactory Bean。这两个对象在Spring容器初始化后就被组装了起来了。
User使用一个TransactionProxyFactoryBean,它定义了一个setTransactionManager()。能很方便的处
理申明的事物还有Service Object。TransactionProxyFactoryBean 还有个setter.
这会被Business service object(UserManager)引用,
UserManager定义了业务层,并且它还有个属性,由setUserDAO()引用。
系统持久层中所有的类都继承自Spring提供的HibernateDaoSupport类,HibernateSupport实现了
HibernateTemplate和SessionFactory实例的关联。HibernateTemplate对Hibernate
Session操作进行了封装,提供了一个简单的方式实现了Hibernate-based
DAO对象。借助HibernateTemplate我们可以脱离每次数据操作必须首先获得Session实例、启动事务、提交/回滚事务以及烦杂的
try/catch/finally的繁琐操作。一个简单的Hibernate访问方法就完全解决了些麻烦!
无论是在多个DAO接口还是在多方事务的情况下,Spring使得多种DAO对象无缝地协同工作。
对于简单的单步的动作,象find,
load,
saveOrUpdate或者delete的调用,HibernateTemplate提供更为便利的选择以代替象一行的回调的执行。此外
HibernateDaoSupport类提供了setSessionFactory方法来接受一个SessionFactory,同时提供了
getSessionFactory和getHibernateTemplate方法供其继承类使用。将这些结合起来,允许对于典型的需求给出了非常简单
的DAO实现,如获得所有用户的方法:
public List getUsers(User user) {
return getHibernateTemplate().find("from User u order by upper(u.username)");
}