宅急送 项目第十二天 项目总结

时间:2022-03-11 20:55:54

重点:
1、 复习 JBPM 工作流开发流程
2、 完成 中转配送流程
3、 流程实例监控 模块
4、 异常处理
5、 二级缓存

1. JBPM开发流程回顾

1.1. 在项目整合工作流框架 (第九天)

导入jar包 (使用 maven坐标导入 )

配置文件 jbpm.cfg.xml 核心配置文件, 默认引入 hibernate 配置文件 jbpm.hibernate.cfg.xml , 如果整合Spring ,引入
<import resource="jbpm.tx.spring.cfg.xml" /> 将hibernate 配置权力,交给Spring
引入hbm

<value>jbpm.repository.hbm.xml</value>
<value>jbpm.execution.hbm.xml</value>
<value>jbpm.history.hbm.xml</value>
<value>jbpm.task.hbm.xml</value>
<value>jbpm.identity.hbm.xml</value>

Spring 整合 JBPM ,需要通过 SpringHelper 工厂类,创建 ProcessEngine 流程引擎

<!-- 整合jbpm 配置 -->
<bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper">
<property name="jbpmCfg" value="jbpm.cfg.xml"></property>
</bean>
<bean id="processEngine" factory-bean="springHelper"
factory-method="createProcessEngine" />

注意问题 :
1、 MySQ整合,需要使用
org.hibernate.dialect.MySQL5InnoDBDialect 方言
2、 JBPM 依赖 juel jar包, 会和 tomcat发布/lib/el-api.jar 冲突, 解决将三个jar放入tomcat/lib,不要放入项目WEB-INF/lib

1.2. 流程定义管理 (与业务无关)

1.2.1. 可以通过模块,将业务流程部署到项目中 ,上传zip压缩包

宅急送 项目第十二天 项目总结

1.2.2. 流程设计

在线设计器 和 线下设计器 ,通过设计 生成 jpdl.xml 和 png ,在线设计器直接部署,线下设计器,需要上传zip压缩包!

宅急送 项目第十二天 项目总结

1.2.3. 流程定义查看

宅急送 项目第十二天 项目总结

1.2.4. 流程图查看

宅急送 项目第十二天 项目总结

1.3. 如何启动流程 ?

不同业务流程,启动时,方式不同

中转配送流程,通过 工作单审批 功能, 审批工作单,启动中转流程

宅急送 项目第十二天 项目总结

通过 key 启动 ,默认启动相同 key 最高版本的流程
在流程实例,关联流程变量 ZhongZhuanInfo , 关联流程中 所有任务节点对应 数据

1.4. 任务节点,谁来办理

任务节点,使用 candidate-groups 组任务办理方式 (要根据业务使用 swimlanes)
将系统用户角色管理 和 JBPM用户和组关联 起来!

添加系统用户,添加JBPM的用户
添加系统角色,添加JBPM的用户组
为用户授予角色, 完成JBPM 用户 和 组关系建立

1.5. 任务办理

组任务查看 —- 拾取组任务查看 — 个人任务 — 个人任务 办理

1.5.1. 如何将页面中显示,流程实例变量信息

服务器查询任务列表 List<Task> ,但是Task接口没有获取流程变量方法,使用 TaskImpl 实现类 API ,显示流程业务数据 !

1.5.2. 在任务列表,显示所有流程对应任务,如果跳转到不同的页面办理?

通过 <task> 节点 form 属性,执行任务办理表单(页面), 通过 Task对象获得form页面, 点击办理时,跳转到不同页面办理

1.5.3. 办理任务时,服务器如何操作

将业务数据,关联PO对象,进行持久化
将业务数据,关联流程实例上
办理任务,流转自动流转

在办理中转环节任务时,使用*流转技术(动态Transition), 通过当前节点,流向任何节点

面试题 : 如果流程已经写好了,需要在流程中 新增一个节点,如何做 ?
方案一: 修改 流程图,重新发布,版本+1 ,再次启动该流程 使用新流程定义 (问题, 原来流程 无法使用 新流程定义 )
方案二 : 修改已经发布的流程定义 ,数据保存 jbpm4_lob 表,是二进制blob ,先通过数据 读取blob,成为 InputStream , 使用 dom4j 加载到内存 ,使用dom4j 为流程添加新节点 ,将内存数据回显 lob表,流程被修改

2. 完成中转配送流程 剩余节点任务

入库、出库、配送签收 三个任务节点 办理

宅急送 项目第十二天 项目总结

当办理入库任务,跳转 instore_complete.jsp
当办理出库任务,跳转 outstore_complete.jsp
当办理配送签收任务时,跳转 receiveinfo_complete.jsp

编写 三个任务节点 办理的业务代码
入库, TaskAction 提供 instorecomplete 方法
出库: TaskAction 提供 outstorecomplete 方法
配送签收 : TaskAction 提供 receiveinfocomplete 方法

在 BaseService 注入 inStoreDAO、outStoreDAO、receiveGoodsInfoDAO

办理入库任务示例代码:

宅急送 项目第十二天 项目总结

配置结果集

<result name="instorecompleteSUCCESS" type="redirectAction">task_findpersonaltask</result>
<result name="outstorecompleteSUCCESS" type="redirectAction">task_findpersonaltask</result>
<result name="receiveinfocompleteSUCCESS" type="redirectAction">task_findpersonaltask</result>

3. 流程实例管理模块

问题: 如何查看正在的运行实例? 如何查看已经完成流程? 如何查看流程实例变量? 如何流程实例运行到了哪个节点 ?
查看正在运行流程实例信息,jbpm4_execution 表 —- ExecutionService

宅急送 项目第十二天 项目总结

查看已经完成流程实例信息,jbpm4_hist_procinst表 — HistoryService

宅急送 项目第十二天 项目总结

3.1. 查看正在运行流程实例信息

/WEB-INF/pages/workflow/processinstance.jsp 流程实例列表页面

修改 admin.jsp 系统菜单

{ "id":"1005", "pId":"100", "name":"查看正在运行的任务", 
"t":"","page":"processinstance_list.action"}

编写服务器代码 ProcessInstanceAction

public class ProcessInstanceAction extends BaseAction {
}

编写list方法,查看所有正在运行流程实例信息

// 获得ExecutionService
ExecutionService executionService = processEngine.getExecutionService();
ProcessInstanceQuery query = executionService.createProcessInstanceQuery();
List<ProcessInstance> processInstances = query.list();

配置结果集

<!-- 流程实例管理 -->
<action name="processinstance_*" class="processInstanceAction" method="{1}">
<result name="listSUCCESS">/WEB-INF/pages/workflow/processinstance.jsp</result>
</action>

问题: 在显示流程实例列表时,能否显示业务数据呢?
ProcessInstance 接口并没有提供 业务数据显示方法, 可以看 ExecutionImpl 实现类 API

宅急送 项目第十二天 项目总结

显示业务数据时,控制datagrid 自动换行

宅急送 项目第十二天 项目总结

3.2. 实例流程图查看

除了显示流程图之外,绘制红色标记框,标记当前节点

3.2.1. 弹出窗口,显示页面

function show(processInstacneId){
// 弹出页面
window.showModalDialog("${pageContext.request.contextPath}/processinstance_showpng.action?processInstacneId="+processInstacneId);
}

先根据 实例id ,查询 发布id 和图片name

在 ProcessInstanceAction 添加 showpng 方法

宅急送 项目第十二天 项目总结

跳转 viewpng.jsp

<result name="showpngSUCCESS">/WEB-INF/pages/workflow/viewpng.jsp</result> 

3.2.2. 通过 img 标签,引用流程图

<img src="${pageContext.request.contextPath }/processdefinition_viewpng.action
?deploymentId=${deploymentId}&imageResourceName=${imageResourceName}"
/>

3.2.3. 绘制红色标记框

从 JBPM中获得当前任务节点坐标,绘制红框
RepositoryService 提供

宅急送 项目第十二天 项目总结

问题: 一个流程有几个当前活动节点 ?
不一定是一个 ,因为 fork/join

获得活动名称

宅急送 项目第十二天 项目总结

宅急送 项目第十二天 项目总结

4. 项目开发中异常处理策略

4.1. 异常可以配置友好错误页面

4.1.1. 配置 web.xml

<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>

4.1.2. 在struts.xml 配置错误页面

<global-exception-mappings>
<exception-mapping result="error" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>
<!-- 配置全局结果集 -->
<global-results>
<result name="error">/error.jsp</result>
</global-results>

问题: 只是使用 友好页面,控制用户感受,进行异常处理,不够灵活和细粒度

4.2. 使用代理机制,struts2定义拦截器控制异常

public class MyExceptionInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
String result = null;
try {
result = invocation.invoke();
} catch (Exception e) {
// 进行异常的捕获和处理
result = "error";
}
return result;
}
}
<interceptor-stack name="privilegeStack">
<interceptor-ref name="myexception"></interceptor-ref> <!-- 异常拦截器配置到最前端 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="login"></interceptor-ref>
<interceptor-ref name="privilege"></interceptor-ref>
</interceptor-stack>

在异常拦截器,发生异常后,进行细节处理 (记录日志、发送邮件)

4.3. 在企业开发中,如果 Service 层 和 DAO层 ,发生异常,是 catch 和 throws ?

能够解决异常,进行捕获解决
如果异常无法解决,是否直接抛出? 否定

举例 DAO层发生SQL异常, 抛给Service 层,Service层根本无能力解决
企业会 先catch 这个异常, 转换为一个业务运行异常 抛出

MoneyNotEnoughException 余额不足异常

当抛出具体业务异常, 上层更知道如何处理 !

4.4. 服务器对于ajax和非ajax 请求,处理方式不同

非ajax请求 ,返回 错误页面
ajax 请求,返回 json 异常信息

根据 请求头信息

宅急送 项目第十二天 项目总结

来分辨是否为 ajax 请求

// 判断是否为ajax请求
if (ServletActionContext.getRequest().getHeader("X-Requested-With") != null) {
// ajax 请求
Map<String, Object> map = new HashMap<String, Object>();
map.put("result", "failure");
map.put("msg", "修改密码失败," + e.getMessage());
ActionContext.getContext().put("map", map);
result = "errorjson";
} else {
result = "error";
}

配置 json 结果页面

<result name="errorjson" type="json">
<param name="root">map</param>
</result>

5. 二级缓存在项目中的应用

5.1. 分析二级缓存应用

问题: 什么是二级缓存 ? 二级缓存是如何存储的? 二级缓存如何使用 ?
Hibernate 提供一级缓存(Session 范围)和二级缓存(SessionFactory 范围), 一级缓存内置,

SessionFactory 缓存两部分内容 ,一部分 cfg文件、hbm文件配置内容 (命名查询) , 另一部分 数据缓存 (需要配置,引用外部框架 )

二级缓存, 支持 EhCache 、OSCache 、JBOSS Cache

Session 的缓存,只能在一个线程中使用 ,在开发中,将Session与线程绑定, 一个线程对应一个Session ,

Session 中数据不能在多次用户请求,不同用户请求 不能共享, 使用SessionFactory 缓存,实现多个用户,多次请求之间 共享数据 。

二级缓存,存储以对象散装数据 存储的,访问二级缓存的数据,查询条件 是 id !

查询缓存 和 二级缓存区别? 二级缓存缓存整个对象属性数据,查询条件是id, 查询缓存可以缓存任何查询结果,查询条件 是 SQL语句

二级缓存使用 :
1、 引入 jar包
2、引入配置文件
3、 在hibernate开启二级缓存
4、 配置二级缓存提供商
5、配置缓存策略

BOS项目 应用场景: 每个用户登录,需要将用户具有角色和权限查询出,保存到Session中 ,用户具有角色信息会重复, 角色对应权限信息 也会重复

5.2. 在项目中使用二级缓存

以EhCache 为例

5.2.1. 导入 jar 包

宅急送 项目第十二天 项目总结

使用 maven 导入 ehcache的jar 包

<!-- 导入二级缓存 -->
<!-- ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.5.0</version>
</dependency>
<!-- 导入二级缓存提供商 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>3.6.10.Final</version>
</dependency>

5.2.2. 引入EhCache配置文件

从 jar 包中找
将 jar 中 ehcache-failsafe.xml 复制 项目 resources 目录, 改名 ehcache.xml

5.2.3. 开启二级缓存

配置 applicationContext-common.xml

<!-- 开启二级缓存 -->
<prop key="hibernate.cache.use_second_level_cache">true</prop>

5.2.4. 配置二级缓存提供商

<!-- 配置二级缓存实现(提供商) -->
<prop key="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</prop>

5.2.5. 配置对哪些数据缓存?

配置数据 缓存并发策略
可以在 cfg文件统一配置,可以在hbm文件配置

缓存分为类级别和集合级别的缓存
配置 Role.hbm.xml

<!-- 关联 -->
<set name="functions" table="role_function">
<cache usage="read-write"/>
<key column="role_id"></key>
<many-to-many class="cn.itcast.bos.domain.auth.Function" column="function_id"></many-to-many>
</set>

集合级别的缓存,依赖类级别的缓存

配置Function.hbm.xml

<class name="cn.itcast.bos.domain.auth.Function" table="auth_function">
<cache usage="read-write"/>

5.3. 准备测试数据

两个角色 role1 role2 ,三个用户 aaa bbb ccc
aaa 和 bbb 属于 role1,ccc 属于role2
role1 和 role2 权限由交叉

5.4. 配置log4j 只打开二级缓存日志,关闭其他日志

log4j.rootLogger=OFF, stdout
log4j.logger.org.hibernate.cache=debug

5.5. 二级缓存的性能监控

<!-- 开启hibernate 二级缓存性能监控 -->
<prop key="hibernate.generate_statistics">true</prop>

通过SessionFactory API 获得 监控参数

// 统计对象
Statistics statistics = sessionFactory.getStatistics();

System.out.println("命中次数:" + statistics.getSecondLevelCacheHitCount() + ", 丢失次数:" + statistics.getSecondLevelCacheMissCount());

其它

课前资料

宅急送 项目第十二天 项目总结

里面有大量相关技术的资料

补充资料

宅急送 项目第十二天 项目总结

ExtJS前端

宅急送 项目第十二天 项目总结

FCKEditor在线HTML编辑器

宅急送 项目第十二天 项目总结

jfreechart报表图表

宅急送 项目第十二天 项目总结

maven

宅急送 项目第十二天 项目总结

quartz定时任务快速入门

宅急送 项目第十二天 项目总结

服务器负载性能

宅急送 项目第十二天 项目总结

索引

宅急送 项目第十二天 项目总结

课后资料

宅急送 项目第十二天 项目总结

实例流程图查看原理

宅急送 项目第十二天 项目总结

二级缓存使用分析

宅急送 项目第十二天 项目总结

二级缓存存储结构

宅急送 项目第十二天 项目总结

课程视频内容

宅急送 项目第十二天 项目总结

宅急送 项目第十二天 项目总结