概述
架构图
目录结构
标准开发指南
开发说明
1、建立Action类
2、建立Service类
3、Service对象实例化方式
4、建立Command类
5、规范
异常处理
日志处理
无侵入开发指南
类级别(Command)动态代理
方法级(Service-Method)动态代理
概述
新架构与现行的架构能够很好的结合,前后端分离的同时,对后端增加了分层、AOP、IOC、interceptor的支持。
新架构要求service和Command层必须面向接口编程, 同时通过IOC和命令委托方式进行各层的解耦(具体参加下
方示例);
另外,新架构还提供全局interceptor和局部interceptor、SERVICE-AOP、COMMAND-AOP的支持,可以进行比如
日志记录、声明性事务、安全性,和缓存等等功能的实现和无侵入二开。
新架构采用命令模式和职责链模式作为基础开发模式,提供一系列的公共实现,用于规范开发过程。
架构图
目录结构
总体目录结构
目录说明:
目录
command
core
workflow
hrm
email
...
说明
公共模块
核心框架
流程模块
人力资源模块
邮件模块
其他
注意:每一个模块在该目录下都应该有一个对应的目录
内部文件结构
目录说明:
说明
模块内公共业务类目录
常量类目录
业务Command类目录
实体类目录
业务Service服务类目录
工具类目录
Action类目录
目录
biz
constant
cmd
entity
service
util
web
标准开发指南
开发说明
1、建立Action类
Action类需要在web目录下建立,web目录位于模块文件夹下; 每一个功能都应有一个与之对应的Action类,用于
对外提供接口服务,Action中不建议包含业务逻辑的处理,业务逻辑请放到Command层(见后文)。 Action类作
为边界类,对外提供接口服务, 对内做业务调用,并负责将内部返回的数据做JSON格式的转换,返回给接口的调
用者,这里需要注意的是:数据格式的转换尽量的放到Action中, 不要放到业务层(Service、Command层),
这样做的好处是有利于维护和二开。
示例 1 建立action类, 并配置方法的Path,以及返回数据的类型(注意:类并没有配置Path)
package com.engine.workflow.web.workflowPath;
/**
* 标题设置action
* */
public class TitleSetListAction {
private TitleSetService getService(){
//实例化Service类
return ServiceUtil.getService(TitleSetServiceImpl.class);
}
/**
* 标题设置
* */
@GET
@Path("/getCondition")
@Produces(MediaType.TEXT_PLAIN)
public String getCondition(@Context HttpServletRequest request,@Context HttpServletResponse
response){
Map apidatas = new HashMap();
try{
User user = HrmUserVarify.getUser(request, response);
//实例化Service 并调用业务类处理
apidatas = getService().getTitleSetCondition(ParamUtil.request2Map(request), user);
}catch(Exception e){
//异常处理
e.printStackTrace();
apidatas.put("api_status", false);
apidatas.put("api_errormsg", "catch exception : " + e.getMessage());
}
//数据转换
return JSONObject.toJSONString(apidatas);
}
}
com.engine目录是核心业务逻辑类所在目录,不允许直接暴露对外服务接口,对外服务接口请暴露在com.api下
(专门提供API服务的目录)。 具体操作是(见示例 1 和 2): 在com.api.模块.web目录下建立对外接口类,然后
通过extends(继承)的方式暴露RESTful服务接口。 示例 1中的Action建立后还不能被前端调用,因为类没有暴露
出来,还差一步,见示例 2
示例 2 在api目录下暴露接口,直接extens之前写好的action
package com.api.workflow.web.workflowPath;
import javax.ws.rs.Path;
/**
* 标题设置action
* */
@Path("/workflow/nodeSet/titleSet")
public class TitleSetAction extends TitleSetListAction{
}
2、建立Service类
Service类需要在service目录下建立,service目录位于模块文件夹下;
每一个功能都应有一个与之对应的Service接口和impl实现类, 注意:Service中不允许有具体的业务实现,仅作为
服务的提供者,具体业务委托给具体的Command。
Service接口不需要继承任何类,但需要将其中的服务接口描述清楚
Service接口示例
/**
* 后台流程监控service
* @author luosy 2017/12/20
* @version 1.0
*
*/
public interface WorkflowMonitorSettingService {
/**
* 获取监控类型sessionkey 列表数据
* @param params 参数列表
* @param user 用户
* @return sessionKey
*/
public Map getMonitorTypeSessionkey(Map params);
}
Service实现类需要实现Service业务接口, 并继承框架中的Service类;
Service实现类需要在impl目录下建立,impl目录位于service文件夹下;
Service实现类示例
/**
* 后台流程监控service 实现类
* @author luosy 2017/12/20
* @version 1.0
*
*/
public class WorkflowMonitorSettingServiceImpl extends Service implements
WorkflowMonitorSettingService {
@Override
public Map getMonitorTypeCondition(Map params,
String method) {
return commandExecutor.execute(new GetConditionCmd(params,user,method));
}
}
3、Service对象实例化方式
Action中不能够通过new的形式实例化Service类,需要调用新架构提供的API来实例化
示例
LoadWorkflowTreeService lwtService = ServiceUtil.getService(LoadWorkflowTreeServiceImpl.class)
4、建立Command类
Command采用单一职责原则,一个类,只做一件事。
如果一个类承担的职责过多,就等于把这些职责耦合在一起了。
一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。
这种耦合会导致脆弱的设计,当发生变化时,设计会遭受到意想不到的破坏。而如果想要避免这种现象的发生,就
要尽可能的遵守单一职责原则。此原则的核心就是解耦和增强内聚性,增加复用和可维护性;
Command需要在相应的CMD目录下建立XXXCmd(cmd目录位于模块文件夹下), 实现Command接口即可(实
现execute方法), 如果需要记录日志,兼容一些公共处理,可以直接继承:
com.engine.common.biz.AbstractCommonCommand,
该抽象类默认包含user 、params和get set方法 , 另外也包含了日志的。
示例
public class GetSearchConditionCmd extends AbstractCommonCommand
异常处理
业务Command对象中不允许抛出非运行时异常,如需要对异常处理, 请先捕捉,然后转成ECException进行抛
出,并对异常发生的情况添加大家可理解的说明。
示例:
try {
//TODO
} catch (Exception e) {
throw new ECException(command.getClass().getName() + "执行过程中异常", e);
}
日志处理
com.engine.common.biz.AbstractCommonCommand
已经包含了日志接口, 大家只需要实现对应方法, 作成日志对象返回即可, 该抽象类包含单日志记录和批量日志
记录两个方法,大家需要根据自身情况进行选择性实现。
批量日志记录方法:
public List getLogContexts()
为了方便记录批量 和 更新操作,增加了一个日志处理类,仅需要少量的代码即可完成日志的记录,且可做到与业
务代码解耦。 该类的实现方式是在业务更新前后去DB中查询,做数据做对比,区分出新建、更新、删除动作,其
对性能可能会有稍微的影响(具体视功能SQL的执行效率而定),对于性能有严苛要求的功能,请酌情使用。
示例 1 使用SimpleBizLogger进行日志主从日志记录
package com.engine.workflow.cmd.workflowPath.node.operatorSetting;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import weaver.conn.RecordSetTrans;
import weaver.general.Util;
import weaver.hrm.HrmUserVarify;
import weaver.hrm.User;
import weaver.systeminfo.SystemEnv;
import weaver.workflow.workflow.WfRightManager;
import weaver.workflow.workflow.WorkflowComInfo;
import com.engine.common.biz.AbstractCommonCommand;
import com.engine.common.biz.SimpleBizLogger;
import com.engine.common.biz.SimpleBizLogger.SubLogInfo;
import com.engine.common.constant.BizLogSmallType4Workflow;