jiveJdon业务层架构设计
系统业务层包括几个主要功能:业务核心;Model缓存;权限验证,这三大主要模块如何能够松耦合实现,最好是在业务层重再分层实现,彼此耦合联系很小,这样,可提供最大可能的可维护型和可拓展性。
我们使用Filter过滤器模式来实现这三大模块,过滤器设计目前有几个设计选择:使用AOP拦截器或使用Decorator模式,具体分析可参考文章:AOP vs Decorator(/AOPdesign/decorator.htm)。
通常Model缓存是靠近持久层或在持久层实现(实体Bean和Hibernate都是这样机制),也就是在业务层后端实现;而权限验证主要是面向调用者,因此,应该是在业务层和表现层之间实现,也就是在业务层前端实现。如下图:

如图,整个架构主要划分三大模块;其中AOP拦截器用来实现权限验证,对每个业务服务方法进行检查,只有被配置允许访问的角色才能通过进行业务服务壳中。
业务服务Shell是一个业务层的壳,这个壳也可以是多层,就象水果外一层层皮一样,我们将一些和业务数据相关的权限验证在这里使用Decorator实现,因为AOP拦截器并不能实现所有的权限检验功能,一般只能实现基于方法的权限检验,也就是检查某个业务方法是否可被调用,但是对于某些数据是否可被方法的数据相关验证则无能为力,这里使用装饰模式实现。
在持久层Dao中,我们又使用装饰模式实现缓存和数据库操作,特别是Model的读取,每个Model在和数据库交互之前,首先从缓存中获取,缓存中没有才真正访问数据库。
关于代码设计编写上,这里有一个不成文规定:我们编写构造类时,需要注意前后顺序,业务层主要以Service为主,Service类调用持久层的Dao或其他类;但是持久层Dao类不能反调用业务Service类。
因为我们使用Jdon框架的Ioc的autowiring功能,只要在一个类的构造参数中引用其他类就可以,而不必再写其他类的创建等代码,这样编写代码起来非常方便,但是也不能在一个类的构造胡乱调用其他类,造成互相循环调用,这是不允许的,例如:A构造参数有B;而B构造参数有C;而C构造参数有A,这就是循环调用,是无法运行通过的。
ForumMessageShell
我们以ForumMessage这个Model来举例说明Shell的具体实现。
上图中业务服务Shell是处于最外面的一个壳,在壳之前是AOP的拦截器,这个壳具备很多特点:它必须是可接受运行上下文,因此需要继承com.jdon.container.visitor.data.SessionContextAcceptable,同时它也是一个Poolable实现,也就是说,Shell类运行时是以对象池方式运行的。
因此,对于ForumMessage这个Model我们实现一个壳ForumMessageShell,ForumMessageShell的结构如下:
public class ForumMessageShell extends ForumMessageServiceImp
implements SessionContextAcceptable, Poolable
它必须首先是一个业务逻辑核心ForumMessageServiceImp的子类,然后利用Jdon框架两个特性,继承两个接口。
在ForumMessageShell中,可以对每个方法进行Decorator模式的方法拦截,这样进一步完成权限验证。
登录信息拦截
正如上节指出一样,ForumMessageShell必须继承SessionContextAcceptable,这样提供setSessionContext(SessionContext sessionContext)方法供Jdon框架将用户通过Web容器登陆的用户信息注射到ForumMessageShell中,我们通过getLoginAccount方法从sessionContext中获取用户登陆信息,如下代码:
protected Account getLoginAccount() throws Exception {
UserPrincipalSetup userPrincipalSetup = containerUtil.getUserPrincipalSetup();
//获得用户从容器登陆系统登陆后的Principle,这里是用户名
String principleName = userPrincipalSetup.getPrincipalName(sessionContext);
if (principleName == null) {
throw new Exception("the login principle name is null");
}
logger.debug(" the login name is:" + principleName);
//根据用户名获得用户完整信息
Account account = accountDao.getAccountByName(principleName);
return account;
}
这样,我们获得用户信息后,因为创建帖子等业务功能需要创建者的信息,所以,在ForumMessageShell中的createTopicMessage方法中我们实现如下代码:
public void createTopicMessage(EventModel em){
ForumMessage forumMessage = (ForumMessage)em.getModel();
try {
Account account = getLoginAccount();
forumMessage.setAccount(account);
//执行真正业务核心ForumMessageImp创建帖子
//很显然的Decorator模式,上面两行代码就是刷的油漆。
super.createTopicMessage(em);
} catch (Exception e) {
logger.error(e);
em.setErrors(Constants.ERRORS);
}
}
访问权限过滤
ForumMessageShell一个主要功能是实现与数据相关的权限检验,实现上节ACL中的数据访问权限。
我们前面已经通过组件访问拦截器实现了各种角色对ForumMessageService各个方法的访问权限设置,这是通过AOP拦截器com.jdon.jivejdon.service.interceptor. PermissionInterceptor类和配置jivejdon_permission.xml实现的。
这只是基于方法的授权访问约束,不能实现数据访问约束,例如帖子创建者可以对自己的帖子进行修改,非创建者则不能,这个功能因为需要取出ForumMessage的创建者这个数据,所以无法使用AOP的拦截器实现。
修改帖子这个权限约束可以在ForumMessageShell的updateMessage方法实现如下:
public void updateMessage(EventModel em){
ForumMessage modForumMessage = (ForumMessage)em.getModel();
ForumMessage forumMessage = getMessage(modForumMessage.getMessageId());
if (forumMessage == null) return;
if(!updateMessageAuthCheck1(forumMessage)) {
em.setErrors(Constants.NOPERMISSIONS);
return;
}
…..
//执行真正业务核心ForumMessageImp创建帖子
//很显然的Decorator模式,上面两行代码就是刷的油漆。
super.updateMessage(em);
}
其中updateMessageAuthCheck1方法是进行判断当前调用updateMessage方法的用户是否是帖子创建者这个条件,具体代码如下:
protected boolean updateMessageAuthCheck1(ForumMessage forumMessage){
boolean ret = false;
logger.debug(" enter updateMessageAuthCheck1 id =" + forumMessage.getMessageId());
try {
Account account = getLoginAccount();
if (account.getRoleName().equals(Role.ADMIN)){//管理者可修改
ret = true;
}else if (account.getUserId().equals(forumMessage.getAccount().getUserId())){
//创建者自己可以修改
ret = true;
}else{
logger.warn("not the owner , no permission update Message!");
}
} catch (Exception e) {
logger.error(e);
}
return ret;
}
当然,修改帖子除只有创建者自己可以修改以外,还有一个权限约束:如果该贴有回帖了,那么就是创建者也无法修改这个帖子(管理者除外)这个约束我们也在ForumMessageShell中实现,具体实现在下面章节的修改帖子中描述。
删除帖子权限等同修改帖子,这里就不再叙述。
业务层过滤器
根据Evans DDD,领域层包括大量的业务规则,这些业务规则将和领域模型实现互动,我们是通过专门的业务层过滤器实现,分两种类型:In和Out,具体参见“业务层过滤器体系”。