重点:
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());
其它
课前资料
里面有大量相关技术的资料