Spring 和 AspectJ实现DDD领域驱动设计

89377510 2018-12-14

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 预约实体为例子,它有下面几个状态:

Spring 和 AspectJ实现DDD领域驱动设计

当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.

相关推荐