Spring 和 AspectJ实现DDD领域驱动设计
DDD能够帮助我们跟随业务概念的复杂变化而顺利实现软件开发。无论是Spring或EJB3,下面三层架构:
- 领域对象, 映射到关系数据库的POJO. .
- 数据访问层 – 典型的无态服务, 包装(JDBC, Hibernate, JPA, iBatis)等实现于内部,对外提供抽象,比如DAO模式
- 业务服务层 – 另外一种无态服务, 对领域对象进行操作. 一般的设计是引入了一系列领域对象,或返回域对象,执行这些对象的逻辑,然后通过数据访问层访问数据库。服务层是伟大的,因为它专注于业务逻辑,委派技术细节DAO层。
- 用户界面 – 面向Web留nowadays, typically via web browser. User interface is great because… just the fact it is.
一个领域模型对象内部封装了状态,对象的操作是一种有态方式, 对象在行为被调用后导致内部状态变化,对象的状态切换是由行为影响导致,这是一种状态模式。
以Reservation 预约实体为例子,它有下面几个状态:
当Reservation预订时被创建,它有新的状态(状态)。一些授权人可以接受预订,比如暂时保留他们的座位,并发送一封电子邮件,要求他为预订支付钞票。然后,当用户执行货币转移,钱入账,打印票和第二电子邮件发送到客户端。
首先Reservation实体是有方法行为的,不是以前的贫血模型,只有属性的setter/getter方法,这些行为用来切换状态变化的。
如果我们使用Hibernate持久化Reservation,带来问题是,Spring如何知道Hibernate管理的那个Reservation实体对象,当Hibernate创建领域对象时,SPring并不知道那个实例,也就无法接管这个领域对象,服务就无法操作到那个Reservation实体。
首先我们使用@Configurable配置领域对象:
@Configurable
@Entity
public class Reservation implements Serializable {
//...
}
这是告诉Spring要管理Reservation对象,而@Entity属于Hibernate标注,Spring不知道Hibernate何时创建它,这时必须使用AspectJ。配置中加入:
<context:load-time-weaver/>
告诉Spring使用 AspectJ load-time weaving (LTW). 对Hibernate加载的对象进行织入管理。
但是会出现下面错误:
java.lang.IllegalStateException: ClassLoader [org.apache.catalina.loader.WebappClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:spring-agent.jar
at org.springframework.context.weaving.DefaultContextLoadTimeWeaver.
setBeanClassLoader(DefaultContextLoadTimeWeaver.java:82)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
initializeBean(AbstractAutowireCapableBeanFactory.java:1322)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
… 59 more
失败了,当应用启动时,其实它并没有发现AspectJ agent,然后就告诉我们错误,加入 -javaagent:spring-agent.jar 到JVM命令参数,Reservation第一次被加载时,agent会发现@Configurable配置,然后将ApectJ的方面aspect应用于它。
用AspectJ将依赖注入到领域对象
配置:
<context:spring-configured />
<context:component-scan base-package="some.package.domain" />
<context:load-time-weaver aspectj-weaving="on" />
使用Hhibernate的实体类代码:
package come.package.domain;
import java.io.Serializable;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public abstract class Entity<T, ID extends Serializable> {
@Autowired
private SessionFactory sessionFactory;
public T getById(ID id) {
return (T) getSession().get(getClass(), id);
}
public ID save() {
return (ID) getSession().save(this);
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
}
编译时JVM参数加入:
-javaagent:~/.m2/repository/org/springframework/spring-instrument/3.0.2.RELEASE/spring-instrument-3.0.2.RELEASE.jar
详细配置见Spring 3 AOP配置
预订模型
回到预订模型, 因为这个增强的Reservation是Spring-aware.它不管是由Hibernate或Struts2创建的,这样,我们可以将依赖注入到领域对象中:
@Configurable
@Entity
public class Reservation implements Serializable {
@PersistenceContext
private transient EntityManager em;
@Transactional
public void persist() {
em.persist(this);
}
//...
}
注意,我们已经将Spring和Hibernate配置无缝地整合在一个领域模型中了。@Entity和@PersistenceContext是Hibernate,@Configurable和@Transactional是Spring的。
你能注入通常依赖dependencies (其他 Spring beans)到你的领域对象.你可以选择(@Autowire or even 或者 @Resource ,或者手工setting properties.