164
1. BSTEK Development Framework2(BDF2) Home . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.1 概述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.1.1 1.项目创建与配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1.2 2.BDF2-ORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.1.2.1 2.1.使用缓存 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.1.2.2 2.2.发送消息 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.1.3 3.BDF2-CORE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.1.3.1 3.1.替换登录页面 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.1.3.2 3.2.主界面的选择与配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.1.3.3 3.3.替换用户 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 1.1.3.4 3.4.替换部门 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 1.1.3.5 3.5.替换岗位 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 1.1.3.6 3.6.URL权限控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 1.1.3.7 3.7.组件权限控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 1.1.3.8 3.8.Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 1.1.3.9 3.9.单点登录相关 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 1.1.3.10 3.10.获取登录用户信息 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 1.1.4 4.BDF2-JOB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 1.1.4.1 4.1.Job定义与配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 1.1.4.2 4.2.JOB集群 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 1.1.5 5.BDF2-JBPM4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 1.1.5.1 5.1.创建流程模版 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 1.1.5.2 5.2.流程模版的在线配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 1.1.5.3 5.3.任务处理页面组件的控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 1.1.5.4 5.4.配置通用工具栏 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 1.1.5.5 5.5.任务到达提醒 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 1.1.5.6 5.6.任务过期处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 1.1.5.7 5.7.流程与业务的结合 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 1.1.6 6.BDF2-JASPERREPORTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 1.1.6.1 6.1.报表定义与配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 1.1.6.2 6.2.在业务页面中使用报表 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 1.1.6.3 6.3.配置JasperReports字体 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 1.1.7 7.BDF2-UPLOADER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 1.1.8 8.BDF2-WEBSERVICE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 1.1.8.1 8.1.Webservice原理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 1.1.8.2 8.2.定义XSD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 1.1.8.3 8.3.根据XSD编写Javabean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 1.1.8.4 8.4.编写Endpoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 1.1.8.5 8.5.配置XSD及Endpoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 1.1.8.6 8.6.SoapUI调用Webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 1.1.8.7 8.7.为Webservice添加Security认证 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 1.1.8.8 8.8.编写Webservice时的注意事项 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 1.1.9 9.BDF2-WEBSERVICE-CLIENT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 1.1.10 10.BDF2-RAPIDO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 1.1.10.1 10.1.实体 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 1.1.10.2 10.2.组件 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 1.1.10.3 10.3.页面 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 1.1.10.4 10.4.动作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 1.1.10.5 10.5.实体映射 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 1.1.10.6 10.6.元数据 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 1.1.11 11.BDF2-PROFILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 1.1.12 12.BDF2-AUTHORITYDELEGATION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 1.1.13 13.BDF2-COMPONENTPROFILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 1.1.14 14.BDF2-EXPORT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 1.1.15 15.BDF2-IMPORT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 1.1.16 16.BDF2-SWFVIEWER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 1.1.17 17.BDF2-DBCONSOLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

  • Upload
    dothuy

  • View
    215

  • Download
    0

Embed Size (px)

Citation preview

Page 1: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1. BSTEK Development Framework2(BDF2) Home . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.1 概述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.1.1 1.项目创建与配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.1.2 2.BDF2-ORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

1.1.2.1 2.1.使用缓存 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101.1.2.2 2.2.发送消息 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

1.1.3 3.BDF2-CORE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121.1.3.1 3.1.替换登录页面 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151.1.3.2 3.2.主界面的选择与配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191.1.3.3 3.3.替换用户 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221.1.3.4 3.4.替换部门 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301.1.3.5 3.5.替换岗位 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311.1.3.6 3.6.URL权限控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321.1.3.7 3.7.组件权限控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361.1.3.8 3.8.Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361.1.3.9 3.9.单点登录相关 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371.1.3.10 3.10.获取登录用户信息 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

1.1.4 4.BDF2-JOB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441.1.4.1 4.1.Job定义与配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471.1.4.2 4.2.JOB集群 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

1.1.5 5.BDF2-JBPM4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531.1.5.1 5.1.创建流程模版 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551.1.5.2 5.2.流程模版的在线配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571.1.5.3 5.3.任务处理页面组件的控制 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661.1.5.4 5.4.配置通用工具栏 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671.1.5.5 5.5.任务到达提醒 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711.1.5.6 5.6.任务过期处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751.1.5.7 5.7.流程与业务的结合 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

1.1.6 6.BDF2-JASPERREPORTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771.1.6.1 6.1.报表定义与配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791.1.6.2 6.2.在业务页面中使用报表 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821.1.6.3 6.3.配置JasperReports字体 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

1.1.7 7.BDF2-UPLOADER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881.1.8 8.BDF2-WEBSERVICE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

1.1.8.1 8.1.Webservice原理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1031.1.8.2 8.2.定义XSD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1041.1.8.3 8.3.根据XSD编写Javabean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051.1.8.4 8.4.编写Endpoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071.1.8.5 8.5.配置XSD及Endpoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1081.1.8.6 8.6.SoapUI调用Webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101.1.8.7 8.7.为Webservice添加Security认证 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121.1.8.8 8.8.编写Webservice时的注意事项 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

1.1.9 9.BDF2-WEBSERVICE-CLIENT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141.1.10 10.BDF2-RAPIDO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

1.1.10.1 10.1.实体 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1181.1.10.2 10.2.组件 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211.1.10.3 10.3.页面 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1241.1.10.4 10.4.动作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251.1.10.5 10.5.实体映射 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1311.1.10.6 10.6.元数据 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

1.1.11 11.BDF2-PROFILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1331.1.12 12.BDF2-AUTHORITYDELEGATION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1371.1.13 13.BDF2-COMPONENTPROFILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1401.1.14 14.BDF2-EXPORT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1441.1.15 15.BDF2-IMPORT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1511.1.16 16.BDF2-SWFVIEWER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1571.1.17 17.BDF2-DBCONSOLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

Page 2: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

BSTEK Development Framework2(BDF2) Home

Complete these tasks to get started

- Click in the top right of this screen to customize your Space home pageEdit this home page Edit - Click the button in the header to get startedCreate your first page Create

- Click in the left panel to update space details and logoBrand your Space Configure Sidebar - Click in the left sidebar to update permissions and give others accessSet permissions Space Tools

Recent space [email protected]

3.9.单点登录相关 updated less than a minute ago view change

6.3.配置JasperReports字体 updated Jul 04, 2013 view change

5.3.任务处理页面组件的控制 updated Jul 04, 2013 view change

5.2.流程模版的在线配置 updated Jul 04, 2013 view change

5.4.配置通用工具栏 updated Jul 04, 2013 view change

Space contributors

(less than a minute ago)[email protected] (13 days ago)[email protected] (34 days ago)[email protected] (37 days ago)[email protected] (58 days ago)[email protected]

概述 BDF2与BDF1相比,是一套全新开发的企业应用开发框架,它继承并改进了BDF1中提供了相关功能,通过提供一系列的工具,降低开发人员的使用门槛,更为重要的是,BDF2中所有模块,皆以Dorado7Addon形式存在,以于一些需要在页面中展现的元素,诸如报表展现、用户个性化、数据导出等,全部以标准Dorado7组件形式提供。

在BDF2,允分利用接口替换机制,真正实现各功能模块之间的松耦合,对于BDF1中用户广为诟病的初始项目创建,为解决这个问题,BDF2中提供了一个项目的在线创建向导,用户可在项目创建时,根据项目情况灵活选择需要使用的BDF2功能模块,同时创建的项目支持Maven与标准的Dynamic web project两种格式,这样给不同需求的用户提供了更多的选择。如下图所示:

Welcome to your new space!Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it showsrecent space activity, but you can customize this page in anyway you like.

Page 3: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

如我们所看到的,利用该向导创建项目,我们需要做的就是根据项目情况,选择要使用的BDF2功能模块即可。该向导的URL地址如下:

http://bsdn.org/projects/bdf/deploy/bdf2-new-project-wizard/view.Wizard.d

选择好要用到的模块后,这个向导会帮助我们自动计算这些模块所依赖的第三方jar包,在我们选择好需要的项目类型后,以ZIP包形式下载,用户在拿到空上ZIP格式的项目包后,解压导入Eclipse中即可。在这里我们推荐用户使用Maven类型的项目,对于这种类型的项目,首先向导生成的速度是很快的,然后生成的ZIP文件较小,最后就是Maven项目的一系列相比传统Dynamic webproject项目的优势啦;相反,如果您选择项目类型为Dynamic web project项目,项目下载就需要一些时间。

创建好项目后,如果您需要BDF2各模块最新版或源码及JavaDoc,您可以到bsdn提供的Nexus库中下载取得,bsdn的nexus库地址为http://,输入bdf2关键字就可以搜索到所有的BDF2各模块信息。nexus.bsdn.org

对于目前而言,BDF2所拥有的模块为15个左右,严格来说是15个左右Dorado7的Addon,将来可能还会有新的BDF2模块,下图就像我们展示BDF现有各模块之间依赖关系。

Page 4: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

如上图所示,其中的权限下放管理模块(BDF2-AUTHORITYDELEGATION)依赖于BDF2-CORE模块,其它也是类似,总体来看,因为这些模块都需要数据库支持,所以它们都需要依赖BDF2-ROM模块,多数模块除了与ORM模块依赖外,相互之间都不存在依赖关系。在BDF2中,不再提供各个模块的建表SCHEMA,而是通过配置Hibernate Session实现自动创建表结构功能,后面会有介绍。

相比BDF1,BDF2因为是基于标准的Dorado7Addon模式开发,所以其提供的所有允许用户覆盖的属性都可以定义到dorado-home/configure.properties文件中实现,同时BDF2中提供了更为简单及方便的方式替换框架中的用户、部门及岗位信息。

1.项目创建与配置

之间我们在概述中提到,为简化我们创建BDF2项目,我们提供了一个在线的BDF2项目创建向导,通过该向导,我们只需要根据项目情况选择要使用的模块即可完成项目的创建工作。这个在线创建项目的向导地址如下:

http://bsdn.org/projects/bdf/deploy/bdf2-new-project-wizard/view.Wizard.d

Page 5: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

通过该向导,选择好我们需要使用的BDF2模块,再选择我们需要使用的项目类型:Maven Project或者Dynamic webproject,点击“生成BDF2项目”按钮,系统在计算好项目依赖后就会提示我们下载创建好的项目,这里需要特别指出的是,如果您选择的项目类型为Maven Project,那么服务端基本不需要做太多工作,项目立马就可以下载;相反,如果您选择的是Dynamic webproject,那么服务端就需要做一些工作,特别是计算项目依赖的第三方jar,所以相比Maven Project服务端计算的时间要长,同时因为Dynamicweb project项目的第三方Jar是放在项目当中,所以生成的项目体积也相对较大。这里,我们推荐使用MavenProject,利用Maven来管理我们的项目,相比传统的管理方式优势太多了,大家可以去Google一下。

创建创建好之后,我们可以通过下面的URL来了解如何将在线创建的项目导入到我们的Eclipse当中:

http://bsdn.org/projects/bdf/deploy/bdf2-new-project-wizard/doc.html

在这篇文档当中最后提供到数据源的配置,默认我们通过在线向导创建的项目当中采用的是HSQL数据库,它使得我们创建的项目导入到Eclipse当中立马就可以运行。当然实际的项目我们不会采用HSQL数据库,所以我们需要修改如文档当中得到的数据源配置信息,如下图所示:

在上面的截图当中,一共有三个Spring的bean:第一个bean的ID是dataSource,也就是我们需要配置的连接目标数据库的数据源信息,可以看到这个bean默认采用的是apache的dbcp连接池,当然如果您有其它连接池可以选择,做对应的修改即可,如果您就采用apache的dbcp,那么需要修改其下的url、driverClassName、username及password三个属性,将其改成你目标数据库对应的相关属性信息,关于Apache的DBCP,您可以通过下面的链接了解:

http://commons.apache.org/proper/commons-dbcp/

需要特别指出的是,如果采用其它数据库,或连接池的话,不要忘记加上对应的Jar文件或Maven所需要的dependency信息(如果是Maven项目的话)。

第二个Bean是个匿名bean,也就是没有为其指定ID,当然依赖Spring的规则,在这个bean初始化时会为其自动添加一个ID,具体细节,您可以Spring相关规范。这个匿名bean的parent属性指向一个ID为bdf2.dataSourceRegister的bean,也就是说当前的匿名bean是一个与ID为bdf2.dataSourceRegister的bean相同类型的bean,它有三个property,第一个就是指定它要采用的数据源连接池的ID,这里我们用的是第一个ID为dataSource的bean;第二个property是为这个数据源起一个名字,因为BDF2是支持多数据源的,支持多数据源在运行时动态切换的,所以需要为每个数据源定义一个名字,比如这里采用的是mysql作为其名称;第三个属性是用于定义当前数据源是否为默认数据源,当然也是因为多数据源功能所以才需要这个属性。

第三个Bean它的parent是bdf2.sessionFactory,同样表示其与bdf2.sessionFactory是同一类型的bean,从名字可以看出,这个bean用于配置Hibernate的SessionFactory,这个bean从平台角度来看是非常重要的,前面提到过,BDF2不再为各个模块提供建表的SCHEMA,创建表的工作将由Hibernate完成,所以配置好SessionFactory显示尤为重要。这里要着重指出的是这个bean的下面两个属性:一个是entityInterceptor

Page 6: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

属性,这个属性一定要设置成这里的bdf2.unByteCodeProxyInterceptor,这样才能保证Dorado7提交上来的JavaBean能被Hibernate的Session处理(Dorado7对提交上来的Bean做了代理处理,如果不加这个Bean,HibernateSession无法处理这种被代理过的对象);另一个就是hibernateProperties属性下的hibernate.hbm2ddl.auto属性,熟悉Hibernate的程序员都知道它的作用,这里设置成update,就表示系统在初始化时会自动将Session中的实体对象与数据库中表进行比对,如果不存在就自动创建对应的表,这样就可以省掉我们为BDF2各模块创建表的时间。当然关于HibernateSessionFactory的配置还有很多,有兴趣的程序员可以去Hibernate官网查找一下,所有属性这个Bean都是支持的。

数据源配置好之后(不管是采用默认的HSQL还是重新定义的数据源),就可以将项目启动查看运行效果啦。

如果您使用的是一个Maven Project,那么可以采用jetty:run来启动服务,但如果您采用的是传统的Dynamic WebProject,那么就可以直接使用J2EE版Eclipse中提供的server,配置一个Tomcat运行服务即可,下图中我采用的就是J2EE版Eclipse中提供的server来运行服务:

服务启动后,访问我们的应用,可以看到如下图所示的主页面:

当前页面显示的内容,实际上是项目根下index.jsp中定义的内容,如上图所示,这个页面主要是引导我们在第一次运动项目时需要做的工作。第一次运行项目,所有表系统已经帮助我们自动创建完成了,但系统里还没有用户,所以在登录之前我们需要创建一系统管理员账号:点击上图中的第2项,创建一个新公司的系统管理员账号,点击之后我们可以看到如下图所示的界面:

Page 7: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

可以看到,在注册系统管理员时还需要我们注册一些公司信息,这是因为BDF2支持SAAS模式运营的系统,通过这里的公司ID,将不同公司ID下的用户、权限等相关信息隔离开来;当然如果采用BDF2做单公司应用,这里的公司ID意义就不大了。

注册完成之后,系统会提示我们登录,我们就可以采用刚注册的账号登录,登录成功之后,可以看到一个空的,没有任何菜单项的主界面,如下图所示:

这时我们需要初始化系统菜单,初始化系统菜单方法比较简单,在我们系统系统成功之后,可以回到最初看到的那个index.jsp页面,点击其中的第3项:初始化系统菜单,因为我们已登录,所以可以点击这个链接,点击链接会新开一个页面,在菜单初始化完成之后,系统会有如下提示:

Successful generating system menu

这就表示系统菜单初始化完成了,再次回到登录成功之后的主界面,就可以看到如下图所示的带有导航菜单的主界面了:

Page 8: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

可以看到生成的菜单只有一些诸如菜单管理之类的基本信息及权限管理两部分,这是因为我们当前项目当中只有一个BDF2-CORE模块,所以只会产生这些导航菜单,相应的,如果把其它模块加到项目当中,再次初始化系统菜单就可以看到其它模块附带页面的URL菜单。

到这里为止,BDF2项目的创建与基本就完成了,接下来我们来深入到具体模块了解提供的功能。

2.BDF2-ORM

BDF2-ORM模块有两个,分别是BDF2-ORM-HIBERNATE3与BDF2-ORM-HIBERNATE4,如同ORM名字一样,BDF2-ORM模块主要提供针对数据库持久化的相关操作,这其中BDF2-ORM-HIBERNATE3是支持Hibernate3,而BDF2-ORM-HIBERNATE4则支持Hibernate4,值得注意的是这两个版本当中提供的针对Hibernate、Jdbc以及JPA的API完全一样,只是在编译的时候针对Hibernate部分不太一样,但对于我们程序员来说,看到的API则完全相同,BDF2当中所有涉及到ORM的操作全部都依赖BDF2-ORM-HIBERNATE3,也就是说默认都是基于Hibernate3编译的,当然如果你需要使用Hibernate4版本,可以到我们的nexus.bsdn.org上下载,如果您使用Maven,那么可以通过添加classier属性,将classier属性设置为hibernate4即可,例如:

classier示例

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-core</artifactId> <version>2.0.0</version> <classier>hibernate4</classier></dependency>

上面的代码当中,如果去掉classier标记,那么将采用基于Hibernate3编译的bdf2-core包。在BDF2目前提供的各模块当中,除BDF2-JBPM4、BDF2-EXPORT两模块之外,其它各模块皆分Hibernate3与Hibernate4两不同编译版本,当然默认采用Hibernate3对于我们项目来说已经足够了。

要使用BDF2-ORM模块,需要在Spring环境当中配置数据源,具体配置方法 ,当然如果未使参见项目创建与配置中关于配置数据源部分的描述用BDF2-CORE模块(项目创建与配置当中是使用了BDF2-CORE模块的),那么需要将配置的datasources.xml文件import到WEB-INF/dorado-home目录下的context.xml当中,否则这个datasources.xml文件将不会被Spring初始化,需要注意的是,默认的datasources.xml文件中添加了BDF2提供的namespaceschema定义,如果没有BDF2-CORE模块,需要将这个定义信息删除,因为只有BDF2-CORE中才能解析这个namespaceschema,否则会出现异常。

数据源配置好之后,我们就可以使用BDF2-ORM模块当中提供的三个实现与数据库交互的基类了,它们是:JdbcDao、HibernateDao及JpaDao,顾名思义,JdbcDao是采用JDBC方式与数据库交互;HibernateDao采用Hibernate;JpaDao则是采用的是JPA规范实现(基于Hibernate实现的JPA规范)。这三个DAO基类当中,提供了大量针对数据库操作的方法,其中最多的是查询方法,以paging开头的query方法主要实现分页查

Page 9: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

询,它可以直接处理Dorado7中的Page对象,当前不同DAO要求输入的查询语句也不相同,比如JDBC中的query方法要求的是标准的SQL,Hibernate则是HQL,JPA则是JPQL;如果需要对数据库中数据进行更新插入动作,那以在JdbcDao中我们可以通过获取Spring提供的JdbcTemplate实现,在HibernateDao当中则通过获取Session实现(Spring3.2已不推荐使用HibernateTemplate),JpaDao则是获取EntityManager实现。

值得一提的是这三个DAO类都继承自AbstractDao,这个DAO当中提供了用于解析Dorado7提交上来的基于表格过滤栏的查询操作的名为Criteria对象的方法(我们知道Dorado7支持基于表格过滤栏的查询操作):

解析过滤栏查询对象的方法

/** * @param criteria 要解析的目标Criteria对象 * @param useParameterName 在接装查询条件时是否采用参数名 * @param alias 别名字符串 * @returnParseResult对象,其中包含解析生成SQL拼装对象以及查询条件的值对象Map,其中key为查询字段名,value为具体条件值 */ protected ParseResult parseCriteria(Criteria criteria,boolean useParameterName,String alias)

可以看到返回的是一个ParseResult对象,如果调用时给出的第一个Criteria参数为空,那么返回的ParseResult对象也相应为空。除此之后,我们如果直接使用Hibernate来进行查询,那么还可以直接调用HibernateDao当中提供的buildDetachedCriteria方法将表格过滤栏查询对象直接转换成Hibernate所需要的DetachedCriteria对象实现数据的快速查询操作,具体大家可以参见源码或javadoc(两者皆可以到http://nexus.bsdn.org上下载)。

在项目当中,无论我们项目采用哪种类型的DAO,我们都希望都在这个DAO外再包一层,把这个包好的DAO给具体的业务开发人员使用,因为标准的DAO当中包含太多的方法,可能我们的项目当中只需要用到其中的几种就行,大多数是用不到的,所以对DAO做个facade是最佳操作。

我们知道,BDF2的ORM是支持多数据源的,在每个DAO当中就可以实现数据源的切换,所以我们在DAO中看到很多方法都可以指定采用哪个数据源(调用方法时指定一个数据源名称),如果我们采用了不需要指定数据源的方法,那么系统会采用默认的数据源与数据库进行交互。在AbstractDao当中有个名为getModuleFixDataSourceName的方法,它的作用就是固化当前DAO采用的数据源名称的,除非你手工指定数据源名称,否则系统将采用这个数据源,而不再使用默认数据源(当然有可能你固化的数据源就是默认数据源)。一般来说,如果你需要让你的某个业务模块可以采用某个固定数据源,就可以覆盖这个方法,比如我们的CoreJdbcDao就允许采用某个固定的数据源名称,其代码如下:

固定数据源示例

public abstract class CoreJdbcDao extends JdbcDao { @Override protected String getModuleFixDataSourceName() { return Configure.getString("bdf2.coreDataSourceName"); }}

实际上,BDF2当中各模块固定数据源就是采用这个方法实现的。对于扩展自这三个DAO的类,我们需要做的就是将其直接配置到Spring环境当中,不用注入或依赖其它任何对象,系统会自动为这这些扩展自三个DAO的实现类填充诸如数据源之类信息的。

除了上述这种通过调用代码来指定要采用的数据源名,BDF2中还允许你通过ContextHolder中的setCurrentDataSourceName方法来为当前线程中未指定数据源的方法固化一个数据源,这样就可以实现数据源的动态切换,比如利用这种方法让某个人采用某个数据源等,具体这里就不再展开,大家可以发挥一下想象。

ORM模块允许用户覆盖的属性如下表所示:

Page 10: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

属性名 类型 默认值 描述

bdf2.terracottaServer String 空 用于分布式缓存的terracottaserver的地址,多个地址用逗号分隔,多在集群时使用,以保证各集群实例缓存一致,默认为空,表示不采用集群缓存。

bdf2.smtpHost String 空 系统提供的Email发送器发送Email时采用的SMTP服务地址。

bdf2.smtpIsAuth boolean true 系统提供的Email发送器发送Email时采用的SMTP服务是否需要验证。

bdf2.smtpUser String 空 系统提供的Email发送器发送Email时采用的SMTP服务需要验证时的用户名。

bdf2.smtpPassword String 空 系统提供的Email发送器发送Email时采用的SMTP服务需要验证时的密码。

bdf2.defaultSenderEmailAddres

String 空 系统提供的Email发送器发送Email时采用的发送Email地址。

bdf2.systemTempDir String 空 BDF2中存放临时文件的目录,默认采用的是java.io.tempdir属性对应的值。如果我们需要指定服务器上某个文件夹作为临时文件存放目录,那么可以覆盖该属性,同时指定的目录一定要真实存在,否则会出现错误。

2.1.使用缓存

BDF2默认使用的是ehcache实现的缓存功能,对于我们的应用来说,同样可以使用BDF2提供的缓存功能来缓存需要业务数据,从而降低访问数据频率,减轻数据库服务器压力。要在业务当中使用BDF2中的缓存工具类,我们只需要获取BDF2当中提供的ApplicationCache接口的实现类实例就行,因为这个接口默认配置在Spring当中,所以如果您的业务类也配置在Spring当中,那么只需要注入ApplicationCache接口实现类的bean即可,ApplicationCache接口实现类的bean id为“bdf2.applicationCache”;如果需要在一个非Springbean的业务类中获取这个接口实现类的bean,可以通过下面的代码实现:

获取ApplicationCache接口实现类对应的bean引用

ApplicationCache applicationCache=ContextHolder.getBean(ApplicationCache.BEAN_ID);

ApplicationCache接口源码如下:

Page 11: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

ApplicationCache接口源码

package com.bstek.bdf2.core.cache;/** * @author Jacky.gao * @since 2013-5-21 */public interface ApplicationCache { public static final String BEAN_ID = "bdf2.applicationCache"; /** * 根据指定的key,从缓存当中获取一个对象 * @param key 缓存当中对象的key值 * @return 返回缓存当中与给定key对应的对象值,如果对象不存在,就返回null */ Object getCacheObject(String key); /** * 从临时缓存当中获取一个被临时缓存对象,<br> * 默认情况下,位于临时缓存中对象生命周期为1800秒,也就是半小时 * @param key 缓存当中对象的key值 * @return 返回缓存当中与给定key对应的对象值,如果对象不存在,就返回null */ Object getTemporaryCacheObject(String key); /** * 将一个对象放入缓存当中,同时如果缓存当中有存在相同key的对象,则进行覆盖 * @param key 对象的key * @param obj 具体对象 */ void putCacheObject(String key, Object obj); /** * 将一个对象放入临时缓存当中,同时如果缓存当中有存在相同key的对象,则进行覆盖,<br> * 默认情况下,位于临时缓存中对象生命周期为1800秒,也就是半小时 * @param key 对象的key * @param obj 具体对象 */ void putTemporaryCacheObject(String key, Object obj); /** * 从缓存当中移除一个对象 * @param key 要移除的对象的key值 */ void removeCacheObject(String key); /** * 从临时缓存当中移除一个对象 * @param key 要移除的对象的key值 */ void removeTemporaryCacheObject(String key);}

接口方法都有注释,且比较简单,这里就不再进一步介绍了。在实际应用当中,如果我们采用BDF2做的项目需要部署到集群环境下,那么就需要我们项目当中采用的缓存能支持集群,对于BDF2当中的Ehcache来说,默认我们已经添加了对Ehcache TerracottaServer的支持(Terracotta是一款分布式缓存服务器,详细信息 ),我们需要做就是在WEB-INF\dorado-home\configure.prope点击此处了解rties文件当中添加名为bdf2.terracottaServer参数,指定Terracotta Server所在的IP即可(多个用逗号分隔),这样就可以借助TerracottaServer,实现分布式缓存。

实际上,对于分布式缓存,除了使用TerracottaServer外,我们还可以使用Memcached,Memcached也是一款开源的分布式缓存服务器,具体 ,听说其功能很强大,有兴趣的程点击此处了解序员可以试用一下。如果我们想实现Memcached来作为我们的分布式缓存服务器,那么也比较简单,方法就是利用 实现BDF2当中Memcached

Page 12: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

的ApplicationCache接口,并将其配置到Spring当中,并且配置时,Spring的beanid为“bdf2.applicationCache”,这样就可以覆盖BDF2提供的ApplicationCache接口的默认实现类了。

2.2.发送消息

BDF2当中提供两个消息发送器,可以实现发送BDF2的站内消息及发送Email消息。

要发送Email消息我们首先要获取EmailSender类的实例,因为EmailSender类是配置在Spring当中,所以如果您的类也需要配置到Spring当中的话,我们只需要将名为“bdf2.emailSender”的bean注入即可,同样,如果您的业务类不是配置在Spring当中,您可以通过下面的代码获取EmailSender类实例:

EmailSender实例获取

EmailSender emailSender=ContextHolder.getBean(EmailSender.BEAN_ID);

通过这个EmailSender类,可以实现Email消息的发送,同时还可以指定发送时需要添加的附件等。在使用这个EmailSender类时,我们还需要设置好bdf2.smtpHost、 、 、bdf2.smtpIsAuth bdf2.smtpUser bdf2.smtpPassword及bdf2.defaultSenderEmailAddres属性,关于这些属性的含义,请参考ORM模块允许用户覆盖的属性列表说明。

除了发送Email之外,BDF2还提供了一个可以用于发送BDF2站内消息的消息发送器InternalMessageSender,同样,该类配置在Spring当中,它的bean的id为“bdf2.internalMessageSender”,根据需要我们可以通过注入或ContextHolder的getBean方法获取。具体这里就不再展开了。

3.BDF2-CORE

之所以将该模块称之为CORE,这是因为在这个模块当中包含框架中最常用的一些功能,比如权限。在BDF2当中,我们没再把权限单独作为一个模块来处理,这也是和BDF1最大的不同之处,这是因为权限是一个框架的核心功能,很多功能的展开都需要权限作为底层支撑,所以,在BDF2当中,我们将权限放在CORE模块当中,作为其最重要的功能之一。

除了权限之外,在CORE模块当中,还涵盖了用户登录、主框架页面、站内消息等一系列琐碎且重要的功能点。在之前我们提到,BDF2中所有模块皆以Dorado7Addon模式开发,和BDF1类似,BDF2各个模块都提供了可供外部覆盖的属性,以改变框架的某些默认功能(比如默认登录页面、默认主框架页面等)。因为BDF2是标准的Dorado7 Addon架构,所以如果需要覆盖这些属性,我们只需要在WEB-INF/dorado-home/configure.propertie

,这点与标准Dorado7属性覆盖方式相同。文件中为这些属性定义新的值即可

在CORE模块当中,我们提供了下面这些属性,通过重定义这些属性值就可以改变CORE模块某些功能的默认动作:

属性名 数据类型 默认值 描述

bdf2.disableChangePasswordShortcutRegister

boolean false 是否禁用主框架右上角修改用户密码的快捷图标,默认false,表示不禁用,如果为true,那主框架右上角修改用户密码的快捷图标就看不到了。

bdf2.logoutSuccessURL String /bdf2.core.view.response.LogoutSuccess.d

用户成功退出系统之后显示的提示页面。

bdf2.diableEmailMessageSender

boolean false 是否禁用系统提供的Email方式消息发送器。

Page 13: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.loginSuccessAlwaysUseDefaultTargetUrl

boolean false 登录成功之后是否总是显示默认的登录成功页面,默认值为false,表示在用户未登录情况下,访问某个需要登录才能访问的页面,会自动跳转到登录页面,一旦登录成功,就会自动跳转到用户在未登录前要访问的页面;如果设置为true,那么就在登录成功后就显示默认的登录成功页面。

bdf2.userRegisterMainWizardPath

String /bdf2.core.view.register.RegisterWizard.d

如之前所描述的,在用户第一次使用系统时需要注册一个系统管理员账号,这里就是设置这个注册页面的URL的。

bdf2.rememberMeLoginFailUrl

String /bdf2.core.view.response.RememberLoginFail.d

通过rememberme方式登录失败后显示的页面。

bdf2.disableSeeMessageShortcutRegister

boolean false 是否禁用主框架右上角查看系统内消息的快捷图标,默认false,表示不禁用,如果为true,那主框架右上角 的快查看系统内消息捷图标就看不到了。

bdf2.casLoginUr String /cas.login.d 当采用CAS进行SSO登录时,设置CASServer的登录页面的URL地址,很明显,默认值是乱写的。

bdf2.useCaptchaForLogin boolean true 登录时是否显示验证码,这个在默认的登录页面当中可以看到,如果设置为false,那么登录时就用不输入验证码。

bdf2.mainFrame.welcomePath

String 空 登录成功之后,跳转到主界面后中间工作区显示的欢迎页,如果为空,那么就不显示欢迎页。

bdf2.mainFrame.welcomeIcon String 空 登录成功之后,跳转到主界面后中间工作区显示的欢迎页页签的图标。

bdf2.mainFrame.welcomeTitle

String 空 登录成功之后,跳转到主界面后中间工作区显示的欢迎页页签的文字。

bdf2.disabledGenerateSystemMenuController

String false 是否禁用系统菜单初始化功能,这个在之前有提到,我们通过这个功能实现项目第一次创建时导航菜单的自动生成,如果值为true,那么就不能使用了。

bdf2.loginProcessUrl String /security_check_ 登录页中包含用户名、密码信息的表单的提交地址。

bdf2.rememberMeLoginCookieKey

String bdf.app.remember.me.key 通过RememberMe方式登录后,用于加密用户信息的key。

bdf2.loginSuccessDefaultTargetUrl

String /bdf2.core.view.frame.main.MainFrame1.d

登录成功之后默认显示的主界面。

Page 14: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.coreDataSourceName String 空 CORE模块使用的数据源名称,因为BDF2支持多数据源,同时还支持为每个模块指定一个固定数据源,所以每个模块都会有类似这样一个属性,如果为空,则表示采用默认数据源。

bdf2.logoutURL String /security_logout_ 登录成功之后,退出系统的URL,同样这个URL也是虚拟的。

bdf2.fixedCompanyId String 空 当前BDF2应用与某个固定的公司ID绑定,这样无论谁登录这个系统看到的都是与这个固定的公司ID关联的信息。

bdf2.disableSendMessageShortcutRegister

boolean false 是否禁用主框架右上角发送系统内消息的快捷图标,默认false,表示不禁用,如果为true,那主框架右上角 系统内消息的快发送捷图标就看不到了。

bdf2.diableInternalMessageSender

boolean false 是否禁用框架提供的系统内消息发送器。

bdf2.disableLogoutShortcutRegister

boolean false 是否禁用主框架右上角退出系统的快捷图标,默认false,表示不禁用,如果为true,那主框架右上角 的快捷图标就看不退出系统到了。

bdf2.mainFrameTopView.registerWidth

int 90 登录成功之后主界面框架上部放置单个快捷图标的位置宽度。

bdf2.disableFetchMessageCountShortcutRegister

boolean false 是否禁用成功之后主界面框架右下角显示系统内未读消息数通知的快捷提示功能。

bdf2.mainFrameTopView String bdf2.core.view.frame.main.FrameTop

登录成功之后主界面框架上部放置Banner及快捷图标的位置的页面,也就是说这上部实际上有一个页面动态拼装而成,如果需要,您可以把它换成您自己的页面。

bdf2.mainFrameBanner String dorado/res/icons/bdf-logo.png

登录成功之后主界面框架上部放置Banner图片的地址。

bdf2.basicRealmName String bstek 如果采用basic方式登录的话realname值。

bdf2.useForwardFormLogin boolean false 登录时是否采用forward方式跳转到到登录页面。默认为false,表示采用redirect方式。

bdf2.accessDeniedPage String /bdf2.core.view.response.AccessDenied.d

当用户访问一个他无权限访问的页面时显示的提示页面。

bdf2.disabledSystemRegister String false 是否禁用系统第一次使用时用到的那个系统管理员注册页面。

bdf2.loginFailureToTargetUrlUseForward

boolean false 登录失败后跳转到失败显示页面采用的跳转方式,false表示采用redirect方式,否则采用forward方式。

Page 15: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.mainFrameMenuExpanded

boolean true 登录成功之后主界面左边导航菜单是否全部展开。

bdf2.loginFailureTargetUrl String /bdf2.core.view.frame.Login.d 登录失败后显示的页面,这里采用的是登录页面,也就是登录失败还是跳转到登录页面。

bdf2.inheritParentDeptPermission

boolean false 用户进行权限验证时是否继承其所在部门的父部门权限,默认不继承。

bdf2.securityMaximumSessions

int 1 一个用户名最大登录会话限制,默认为1,表示同一时刻只能有一个会话,否则后登录的会将前面登录的会话踢除。如果为-1,表示无限制。

bdf2.enableComponentPermissionWithoutURL

boolean false 是否允许在进行组件权限判断时不考虑其所在页面,默认为false,表示组件要与页面关联才行。

bdf2.useConservativeAuthorityStrategy

boolean false 是否采用保守权限策略,默认为false,表示不采用保守权限策略,所有页面如果没有分配权限,大家皆可访问;如果为true,那么所有页面必须经过授权才能访问。

bdf2.generateCaptchaUrl String /generate.captcha 登录时采用的验证码的URL。

bdf2.formLoginUrl String /bdf2.core.view.frame.Login.d 登录页面的URL。

bdf2.sessionExpiredUrl String /bdf2.core.view.response.SessionExpired.d

用户登录会话过期显示的提示页面。

bdf2.forceHttpsFormLogin boolean false 是否强制采用HTTPS方式登录。

bdf2.dataSourceContextLocation

String home:datasources.xml 默认用于配置系统数据源的Spring配置文件的地址,默认就是我们在WEB-INF/dorado-home目录下看到的datasources.xml文件。

bdf2.disableShowLoginInfoShortcutRegister

boolean false 登录成功之后主界面框架左下角看到的登录人信息是否禁用,默认不禁用。

bdf2.authenticationType String form 登录认证方式,默认支持三种方式,分别是form(本地的一个登录页面)、basic(浏览器内置的登录窗口)及cas(CASSSO登录)

bdf2.controllerSuffix String action BDF2提供的实现com.bstek.bdf2.core.controller.IController接口机制就可以定义一个Controller请求时其URL的后缀,默认为action,比如内置查看系统属性XML列表的properties.list.action等。

bdf2.useRememberMeForLogin

boolean true 登录时是否显示记住登录信息的复选框。

Page 16: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

3.1.替换登录页面

BDF2提供的默认登录页面如下:

可以用简单和简陋两个词来形容,之所以这样,是因为我们在设计之初就考虑程序员在使用的时候可能90%以上都会用自己的登录页面将其替换,所以就没做那么花哨,总的来说,BDF2当中很多功能用户都可以替换,替换的方法和理论多数都是一样的,那就是通过在WEB-INF/dorado-ho

替换登录页面就是采用这种套路。me/configure.propertie文件中覆盖对应的属性值,

当用户在访问一个需要用户信息的页面时,系统就会自动跳转到登录页面,要求用户登录,这个页面地址取自一个名为“ ”bdf2.formLoginUrl的属性值,这个值默认为/bdf2.core.view.frame.Login.d,也就是上图展示的页面。同样,如果我们要采用自己的登录页面,方法就是在WEB-IN

,重新为F/dorado-home/configure.propertie文件中 bdf2.formLoginUrl属性指定一个新的URL即可。

比如,我们这里为bdf2.formLoginUrl属性指定的URL为/login.jsp,同时设置 (登录失败后bdf2.loginFailureTargetUrl属性值也是/login.jsp,这就表示我们将采用web应用根目录下的一个名为login.jsp的页面作为系统的跳转的页面,这里表示登录失败后还是跳转到我们这里的login.jsp)

登录页面,该页面代码如下:

login.jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>用户登录</title></head><body><h1>登录</h1><form action="security_check_" method="post"> 用户名:<input type="text" name="username_"><br> 密码:<input type="password" name="password_"><br> <input type="submit" value="登录"></form></body></html>

熟悉HTML的程序员都知道,这个页面非常简单,只有一个form表单,这个form表单action属性值为"security_check_",表示我们的表单将提交到这个URL,之前我们提过,登录处理页面的URL实际上由一个名为 的属性决定,而这个属性的默认值为bdf2.loginProcessUrl /security_check,所以如果你修改了这个属性,那么这里的action值就应该是你修改后的值;最后设置form的method属性为post,这个很重要,对于BDF2来_

说,处理登录登录,只接受post过来的请求,也就是说如果你不为form设置method属性(默认为get)或设置method属性值为get,这个处理登录请求的URL是不会处理的,原因很简单,因为get方式请求你的username与password就会被拼装在URL后面,毫无安全性可言了。

Page 17: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

在这个form表单里,我们设置了两个文本域:一个是用户名,一个是密码。这里需要注意的是用户名文本域的name值一定要是 ,密username_码一定要是 ,否则处理登录的URL将无法获取提交的用户名、密码信息。password_

再次运行系统,选择登录,就可以看到我们刚才定义的登录页面login.jsp,输入注册的用户名,密码,发现登录不了,同时也没有任何错误消息,为了知道究竟出了什么样的错误,我们需要进一步修改我们的login.jsp,添加登录失败后的错误消息显示功能,修改好的login.jsp代码如下:

new login.jsp

<%@page import="org.apache.commons.lang.StringUtils"%><%@page import="org.springframework.security.web.WebAttributes"%><%@page import="com.bstek.bdf2.core.context.ContextHolder"%><%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>用户登录</title></head><body><h1>登录</h1><%!private String getAuthenticationExceptionMessage(){ Exceptionexp=(Exception)ContextHolder.getHttpSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); if(exp==null){ exp=(Exception)ContextHolder.getRequest().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); } if(exp!=null){ return exp.getMessage(); } return null; }%><%String error=getAuthenticationExceptionMessage();if(StringUtils.isNotEmpty(error)){ out.println("Login Error:"+error); }%><form action="security_check_" method="post"> 用户名:<input type="text" name="username_"><br> 密码:<input type="password" name="password_"><br> <input type="submit" value="登录"></form></body></html>

Page 18: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

我们添加了从环境中取错误消息的代码块,具体这里就不再解释了。再次运行输入用户名密码,点击登录,可以看到如下图所示的带提示错误消息的登录页面。

从错误消息当中我们得知,原来登录是需要验证码的,如果我们希望登录不要检测验证码该怎么办呢?方法还是添加属性,我们在WEB-INF/dor添加一名为ado-home/configure.propertie文件中 bdf2.useCaptchaForLogin属性,通过之前的属性列表中描述我们得知,它的作用就是决定

在登录时是否检查验证码的,它默认值为true,表示需要检查,所以才出现上图的错误,这里我们设置bdf2.useCaptchaForLogin=false,再次运行应用,输入账号信息登录,可以看到能正常进行主界面啦。

这个时候,可能有些程序在想,如果我在登录的时候需要验证码又该如何处理呢?

如果登录的时候,需要启用验证码,那就要在你的登录页面中添加验证码,从前面的属性描述表中我们得知,生成验证码图片的地址由名为bdf2的属性决定,这个属性默认值为.generateCaptchaUrl /generate.captcha,所以可以修改我们的登录表单,添加验证码图片,修改后的表单代码

如下:

添加验证码

<form action="security_check_" method="post"> 用户名:<input type="text" name="username_"><br> 密码:<input type="password" name="password_"><br> 验证码:<input type="text" name="captcha_"><imgsrc="generate.captcha.action?width=150&height=60"><br> <input type="submit" value="登录"></form>

上述代码当中,我们添加了一个名为loginCaptch的文本域,同时添加了一个用于显示验证码图片的img标记,这个img的src属性值为:generate.captcha.action?width=120&height=50&key=loginCaptch,这个地址当中generate.captcha为bdf2.generateCaptchaUrl的属性指定,需要注意的是它以.action结尾,后面我们会提到,这个.action结尾的URL是BDF2中提供的了一种简单实用的Controller的URL地址,URL后面有两个参数分别用于指定图片的宽度和高度,修改我们之前设置的 。再次运行我们的系统,点击登录,可以bdf2.useCaptchaForLogin属性值为true看到如下效果的登录页面:

Page 19: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

输入用户名、密码及验证码就可以成功登录了。

某些情况下,我们可能需要在登录的时候让在浏览器的cookie当中记录我们的登录信息(这个功能在某些论坛或购物网站中出现的频率比较高),BDF2同样提供了这种功能的支持,再次修改我们的登录表单,添加登录记忆功能,修改好的表单代码如下:

添加自动登录功能的登录表单

<form action="security_check_" method="post"> 用户名:<input type="text" name="username_"><br> 密码:<input type="password" name="password_"><br> 验证码:<input type="text" name="captcha_"><imgsrc="generate.captcha.action?width=150&height=60"><br> 自动登录:<input type="checkbox" name="remember_me_"><br> <input type="submit" value="登录"></form>

我们添加了一个checkbox,并且它的名字是"remember_me_",这样在登录的时候勾上这个checkbox,登录成功以后,关掉浏览器,然后再打开浏览器,输入主界面地址,可以看到系统不再弹出登录页面,直接成功登录了。

好了,登录页面的扩展功能就是这些,其它的诸如美化的工作程序员可以自己考虑灵活添加,这里就不再赘述了。

3.2.主界面的选择与配置

不管我们是采用自定义的登录界面,还是BDF2提供的默认登录界面,登录成功之后都会进入一个名为bdf2.core.view.frame.main.MainFrame1.d的主操作界面,如下图所示:

Page 20: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

在上图当中,我们将主界面划分成四个部分,这四个部分分别用于显示不同的内容:第一部分,实现上它是通过一个subviewHolder对象导入的,也就是说是一个独立view页面,这个view由bdf2.mainFrameTopView属性决定,在属性描述当中我们知道,它的默认值为bdf2.core.view.frame.main.FrameTop,实现使用当中,如果你觉得第一部分整体与你要求不符,那么可以通过修改bdf2.mainFrameTopView属性将它换成你自己的页面(必须是一个Dorado7的view页面),至于宽高,主界面会自动适应的。同时,如果您只是想修改第一部分显示的banner,那么可以通过覆盖bdf2.mainFrameBanner属性实现。除此之外,如果你还需要在第一部分右上角的增加一些快捷图标,那么可以通过实现IFrameShortcutActionRegister接口达到目的,该接口代码如下:

IFrameShortcutActionRegister接口源码

public interface IFrameShortcutActionRegister { void registerToFrameTop(Container container); void registerToStatusBar(Container container); boolean isDisabled(); int order();}

可以看到,这个接口有四个需要我们实现的方法,其中第一个registerToFrameTop方法就是供我们注册一个快捷图标到第一部分右上角的,第二个方法registerToStatusBar则是注册信息到第四部分底部状态栏的,第三个方法决定这个类是否启用的,最后一个是决定顺序的。我们来看看默认提供的那个用于退出系统的实现类吧:

Page 21: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

实现类示例

public class LogoutFrameShortcutActionRegister implements IFrameShortcutActionRegister { @Value("${bdf2.disableLogoutShortcutRegister}") private boolean disabled; public void registerToFrameTop(Container container) { SubViewHolder subviewHolder=new SubViewHolder(); subviewHolder.setSubView("bdf2.core.view.frame.main.register.logout.Logout"); container.addChild(subviewHolder); } public void registerToStatusBar(Container container) { } public void setDisabled(boolean disabled) { this.disabled = disabled; } public boolean isDisabled() { return disabled; } public int order() { return 5; }}

系统还提供了其它的实现,大家在编写这个实现类的时候可以参考。

对于系统提供的默认注册器,默认都是启用的,如果需要禁用,那么可以通过下面这些属性实现。

bdf2.disableSendMessageShortcutRegister

boolean false 是否禁用主框架右上角发送系统内消息的快捷图标,默认false,表示不禁用,如果为true,那主框架右上角发送系统内消息的快捷图标就看不到了。

bdf2.disableLogoutShortcutRegister

boolean false 是否禁用主框架右上角退出系统的快捷图标,默认false,表示不禁用,如果为true,那主框架右上角退出系统的快捷图标就看不到了。

还有其它一些,大家可以 。参考属性描述中关于相关属性的介绍

除了bdf2.core.view.frame.main.MainFrame1.d这个主界面,BDF2默认还提供了bdf2.core.view.frame.main.MainFrame2.d及bdf2.core.view.frame.main.MainFrame3.d两个备选的主界面,它们的效果如下:

Page 22: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

这三种主界面各有特点,大家可以根据自己的喜好及项目要求要进行选择,如果要修改登录成功之后默认显示的主界面,方法是覆盖bdf2.loginSuccessDefaultTargetUrl这个属性值,它的默认值是/bdf2.core.view.frame.main.MainFrame1.d。

3.3.替换用户

BDF2中允许用户替换其中的用户、部门及岗位信息,而且这种替换可以根据需要只替换其中的用户信息或只替换其中的部门信息或只替换其中的岗位信息,或者全部替换掉。替换后的用户、部门及岗位信息可以来自用户定义的任何源,比如其它数据库、LDAP、WebService或其它系统。接下来我们就来介绍如何替换系统中采用的用户信息。

为了演示替换操作,我们新建了一张用户表名为demo_user的表,它的结构比较简单,只有三个字段,结构图如下:

用户表创建好之后,接下来我们就可以编写代码了,首先第一步,我们需要实现AbstractUser抽象类,定义我们自己的用户实体对象,其代码如下:

Page 23: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

DemoUser类源码

import java.util.List;import java.util.Map;import com.bstek.bdf2.core.business.AbstractUser;import com.bstek.bdf2.core.business.IDept;import com.bstek.bdf2.core.business.IPosition;import com.bstek.bdf2.core.model.Group;import com.bstek.bdf2.core.model.Role;public class DemoUser extends AbstractUser { private static final long serialVersionUID = 4348475318698431153L; private String username; private String cname; private String password; private List<IDept> depts; private List<IPosition> positions; private List<Role> roles; private List<Group> groups; public String getCname() { return cname; } public String getEname() { return cname; } /** * 当前用户是否为系统管理员,如果这个属性返回true,那么就表示当前用户为系统管理员, * 所有权限对其都不起作用,反之则不然。 * */ public boolean isAdministrator() { return false; } /** * 当前用户的手机号 * */ public String getMobile() { return "13122112211"; } /** * 当前用户的email地址 * */ public String getEmail() { return "[email protected]"; } /** * 当前用户所在部门 * */ public List<IDept> getDepts() { return depts; } /** * 当前用户所拥有的岗位 * */ public List<IPosition> getPositions() { return positions; } public void setPositions(List<IPosition> positions) { this.positions = positions;

Page 24: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

} public void setDepts(List<IDept> depts) { this.depts = depts; } /** * 当前用户所拥有的角色,权限需要使用 * */ public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } /** * 当前用户所在群组 * */ public List<Group> getGroups() { return groups; } public void setGroups(List<Group> groups) { this.groups = groups; } /** * 用户的其它元数据信息,如果我们的应用用不到,可直接返回null * */ public Map<String, Object> getMetadata() { return null; } public String getPassword() { return password; } public String getUsername() { return username; } /** * 判断当前用户是否可用,可以在用户来源信息中判断这里取值,如果返回false,那么该用户将不能登录 */ public boolean isEnabled() { return true; } /** * 当前用户所在公司ID,如果当前系统只给一家公司使用,这里返回一个固定值即可, * 如果是基于SAAS系统,那么这里需要根据公司信息返回一个值 * */ public String getCompanyId() { return "bstek"; } public void setUsername(String username) { this.username = username; } public void setCname(String cname) { this.cname = cname; } public void setPassword(String password) {

Page 25: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

this.password = password; }}

在上面的代码当中,对于AbstractUser当中的一些重要方法我们做了描述,AbstractUser当中需要我们实现的方法都是必须的,而且都不能返回null(getMetadata方法除外),否则系统运行时可能会产生空指针的异常,开发当中,我们已尽量将AbstractUser类中需要实现的方法减到最少。

编写好我们自定义的用户实体对象后,接下来我们需要实现IUserService接口,在这个接口当中定义了如何获取用户信息等相关操作,这个接口的源码如下:

IUserService接口源码

package com.bstek.bdf2.core.service;import java.util.Collection;import org.springframework.security.core.userdetails.UserDetailsService;import com.bstek.bdf2.core.business.IUser;import com.bstek.dorado.data.provider.Criteria;import com.bstek.dorado.data.provider.Page;/** * @since 2013-1-18 * @author Jacky.gao */public interface IUserService extends UserDetailsService { public static final String BEAN_ID="bdf2.userService"; /** * 分页加载用户数据 * @param pageDorado7分页对象,其中包含pageNo,pageSize,分页后的数据也填充到这个page对象当中,该参数不可为空 * @param companyId 要加载哪个companyId下的用户信息,该参数不可为空 * @param criteria Dorado7条件对象,可从中取到相应的条件值,该参数可为空 */ void loadPageUsers(Page<IUser> page,String companyId,Criteria criteria); /** * 加载指定部门ID下的用户信息 * @param deptId 隶属的部门ID,该参数不可为空 * @return 返回取到的用户集合 */ Collection<IUser> loadUsersByDeptId(String deptId); /** * 检查用户密码是否正确,如果不正确返回错误消息,如正确则返回null * @param username 用户名 * @param password 要检查未加密的密码 * @return 不正确返回错误消息,如正确则返回null */ String checkPassword(String username,String password); /** * 修改指定用户的密码信息 * @param username 用户名 * @param newPassword 新密码 */ void changePassword(String username,String newPassword);

Page 26: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

/** * 注册一个系统管理员账号 * @param username 用于登录的用户名 * @param cname 中文名 * @param ename 英文名 * @param password 密码 * @param mobile 手机号 * @param email 电子邮件 * @param companyId 所在公司ID */ void registerAdministrator(String username,String cname,String ename,String password,Stringemail,String mobile,String companyId); /** * 根据用户名,实现化一个空的用户对象供系统使用,实例化的用户对象,只需要将给定的用户名填充进去即可 * @param username 用户名 * @return 实例化后的用户对象

Page 27: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

*/ IUser newUserInstance(String username);}

接口中每个方法都有详细的解释,我们需要看理解它,接下来,我们来编写基于DemoUser的IUserService接口实现类,来从之前定义的demo_user表中获取用户信息,实现类代码如下:

IUserService接口实现类示例

Page 28: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

import java.sql.ResultSet;import java.sql.SQLException;import java.util.Collection;import java.util.Collections;import java.util.List;import org.springframework.jdbc.core.RowMapper;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UsernameNotFoundException;import com.bstek.bdf2.core.business.IUser;import com.bstek.bdf2.core.orm.jdbc.JdbcDao;import com.bstek.bdf2.core.service.IUserService;import com.bstek.dorado.data.provider.Criteria;import com.bstek.dorado.data.provider.Page;public class DemoUserService extends JdbcDao implements IUserService { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { String sql="select username,password,cname from demo_user where username=?"; List<IUser> users=this.getJdbcTemplate().query(sql, new Object[]{username}, new UserMapper()); if(users.size()==0){ throw new UsernameNotFoundException("User ["+username+"] is not exist!"); } return users.get(0); } public void loadPageUsers(Page<IUser> page, String companyId, Criteria criteria) { String sql="select username,password,cname from demo_user where username=?"; Collection<IUser> users=this.getJdbcTemplate().query(sql,new UserMapper()); page.setEntities(users); } public Collection<IUser> loadUsersByDeptId(String deptId) { return Collections.EMPTY_LIST; } public String checkPassword(String username, String password) { return null; } public void changePassword(String username, String newPassword) { } public void registerAdministrator(String username, String cname, String ename, String password, String email, String mobile, String companyId) { } public IUser newUserInstance(String username) { DemoUser user=new DemoUser(); user.setUsername(username); return user; } class UserMapper implements RowMapper<IUser>{ public DemoUser mapRow(ResultSet rs, int rowNum) throws SQLException { DemoUser user=new DemoUser(); user.setCname(rs.getString("cname")); user.setPassword(rs.getString("password")); user.setUsername(rs.getString("username")); return user; } }}

Page 29: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

在这个接口实现当中,我们采用的是JdbcDao实现从demo_user表中获取用户信息,这其中我们保持checkPassword、changePassword与registerAdministrator三个方法为空,那么对应页面当中用户密码修改及注册系统管理员两块功能就不能正常使用,您可以去实现一把。

再次,我们还需要实现IFrameworkService接口,这个接口比较简单,其中只有一个方法需要我们实现,这个方法的作用是处理用户登录时用户密码验证的,我们的实现类代码如下:

IFrameworkService实现类示例

import org.springframework.security.authentication.BadCredentialsException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.AuthenticationException;import com.bstek.bdf2.core.business.IUser;import com.bstek.bdf2.core.service.IFrameworkService;public class DemoFrameworkService implements IFrameworkService { public void authenticate(IUser user, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { DemoUser u=(DemoUser)user; String pwd=(String)authentication.getCredentials(); if(!u.getPassword().equals(pwd)){ throw new BadCredentialsException("The password is invalid"); } }}

可以看到,代码比较简单,就是判断用户密码是否正确,如果不正确抛出一个BadCridentialsException即可。

上述三个接口实现完成之后,最后,我们需要将实现的DemoUserService及DemoFrameworkService配置到Spring当中,以替换系统自带的IUserService及IFrameworkService的默认实现类,首先我们来看如何配置DemoUserService实现类。

打开位于WEB-INF/dorado-home目录下的datasources.xml文件,在其中添加如下配置:

DemoUserService配置示例

<bean id="demoUserService" class="ext.DemoUserService"></bean> <bdf:user-service ref="demoUserService"/>

第一行,我们将DemoUserService配置成一个bean,其id为demoUserService ,第二行我们利用BDF2提供的namespacescheam将系统默认的IUserService实现用demoUserService bean替换,当然如果您没有引入BDF2提供的namespacescheam,可以用下面的配置来代码上面两行配置信息:

DemoUserService另一种配置

<bean id="bdf2.userService" class="ext.DemoUserService"></bean>

可以看到,这种配置更新简单,只需要一行就可以搞定,但前提是这个bean的ID必须是bdf2.userService。接下来我们看看如何配置DemoFrameworkService类,同样的方法,在datasources.xml当中添加下面两行配置:

Page 30: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

DemoFrameworkService配置

<bean id="demoFrameworkService" class="ext.DemoFrameworkService"></bean> <bdf:framework-service ref="demoFrameworkService"/>

同样,如果您没有引入BDF2提供的namespace scheam,可以用下面的一行替代上述配置:

DemoFrameworkService另一种配置

<bean id="bdf2.frameworkService" class="ext.DemoFrameworkService"></bean>

需要注意的是其ID必须为bdf2.frameworkService。

上述操作完成之后,就可以在demo_user中插入一个用户,启动我们应用进行登录了。

3.4.替换部门

BDF2中允许用户替换其中的用户、部门及岗位信息,而且这种替换可以根据需要只替换其中的用户信息或只替换其中的部门信息或只替换其中的岗位信息,或者全部替换掉。替换后的用户、部门及岗位信息可以来自用户定义的任何源,比如其它数据库、LDAP、WebService或其它系统。接下来我们就来介绍如何替换系统中采用的部门信息。

替换部门与替换用户的操作基本一样,不同的地方是替换部门需要实现IDept与IDeptService两个接口,而替换用户则需要实现两个接口与一个抽象类(IUserService,IFrameworkService与AbstractUser),这里实现IDept接口,实现上相当于之前实现AbstractUser功能是一样的,都是为了实现一个我们自己的部门实体对象,这里要实现的IDeptService相当于我们在用户当中实现的IUserService接口作用是一样的,我们来看看IDept接口内容:

IDept接口源码

public interface IDept extends ICompany{ String getId(); String getName(); String getParentId(); IDept getParent(); List<IUser> getUsers();}

相比AbstractUser类,IDept接口定义的方法较少,再来看看IDeptService接口:

IDeptService

public interface IDeptService { IDept newDeptInstance(String deptId); List<IDept> loadUserDepts(String username); IDept loadDeptById(String deptId); List<IDept> loadDeptsByParentId(String parentId,String companyId);}

定义的几个方法都比较简单,这里就不再多解释了,需要注意的是,无论我们实现IDept接口还是实现IDeptService接口,要保证所有方法都不返回null,否则可能会产生错误。写好这两个接口的实现类后,接下同样是配置到我们的datasources.xml当中,配置示例如下:

Page 31: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IDeptService实现类配置

<bean id="demoDeptService" class="ext.DemoDeptService"></bean> <bdf:dept-service ref="demoDeptService"/>

同样,上述的两行配置也可以被下面的一行配置取代:

IDeptService实现类另一种配置

<bean id="bdf2.deptService" class="ext.DemoDeptService"></bean>

需要注意的是,上述配置当中,bean的ID只能是bdf2.deptService。

这些工作完成后,替换系统中部门的工作也就完成了。

3.5.替换岗位

BDF2中允许用户替换其中的用户、部门及岗位信息,而且这种替换可以根据需要只替换其中的用户信息或只替换其中的部门信息或只替换其中的岗位信息,或者全部替换掉。替换后的用户、部门及岗位信息可以来自用户定义的任何源,比如其它数据库、LDAP、WebService或其它系统。接下来我们就来介绍如何替换系统中采用的岗位信息。

替换岗位与替换部门的操作基本一样,不同的地方是替换岗位需要实现IPosition与IPositionService两个接口,而替换部门则需要实现IDept与IDeptService接口,我们来看看IPosition接口内容:

IPosition接口源码

public interface IPosition extends ICompany{ String getId(); String getName(); List<IUser> getUsers();}

相比IDept类,IPosition接口定义的方法较少,再来看看IPositionService接口:

Page 32: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IPositionService

public interface IPositionService { public static final String BEAN_ID="bdf2.positionService"; IPosition newPositionInstance(String positionId); List<IPosition> loadUserPositions(String username); IPosition loadPositionById(String positionId); /** * 分页加载岗位数据 * @param pageDorado7分页对象,其中包含pageNo,pageSize,分页后的数据也填充到这个page对象当中,该参数不可为空 * @param companyId 要加载哪个companyId下的岗位信息,该参数不可为空 * @param criteria Dorado7条件对象,可从中取到相应的条件值,该参数可为空 */ void loadPagePositions(Page<IPosition> page,String companyId,Criteria criteria);}

定义的几个方法都比较简单,这里就不再多解释了,需要注意的是,无论我们实现IPosition接口还是实现IPositionService接口,要保证所有方法都不返回null,否则可能会产生错误。写好这两个接口的实现类后,接下同样是配置到我们的datasources.xml当中,配置示例如下:

IPositionService实现类配置

<bean id="demoPositionService" class="ext.DemoPositionService"></bean> <bdf:dept-service ref="demoPositionService"/>

同样,上述的两行配置也可以被下面的一行配置取代:

IPositionService实现类另一种配置

<bean id="bdf2.positionService" class="ext.DemoPositionService"></bean>

需要注意的是,上述配置当中,bean的ID只能是bdf2.positionService。

这些工作完成后,替换系统中岗位的工作也就完成了。

3.6.URL权限控制

权限是框架的核心,BDF2中的权限是以SpringSecurity3.1为基础实现,使用时以角色为中心,将资源(URL或组件)与角色绑定,再将各种类型的成员(用户、部门、岗位、群组)放置到角色当中,在放置成员到角色的过程当中还可以设置是否授权,从而实现权限加减功能。如下图所示:

Page 33: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

从上图当中,可以看到URL权限在操作过程当中以角色为中心,首先创建角色,然后将角色与需要进行权限管控的URL关联(如上图中的URL1,URL2),接下来将角色成员放到角色当中,BDF2中允许的角色成员有三类,分别是:用户、岗位、部门和群组,且成员在放置到角色中时还可以设置该成员在这个角色中的权限(能不能访问这个角色所拥有的资源)。

以上图为例,用户A在角色A中的权限为允许访问,那么用户A就可以访问角色A中的URL1和URL2两个资源,同样岗位A在角色A中的权限也是允许访问,那么所有岗位是岗位A的用户都可以访问角色A中的资源,也就是URL1和URL2;而部门A和群组A在角色A中的权限为禁止访问,也就是说隶属于部门A和群组A的用户都不能访问角色A中的资源(URL1和URL2)。

BDF2当中,角色的四种类型的成员在进行权限计算的时候是有优先级的,如上图所示,权限最先计算的是用户,然后是岗位,接下来是部门,最后是群组,如果在用户层面已经明确取得当前用户对某资源的访问权限(允许或拒绝),那么就不再计算后面的岗位、部门和群组,同样如果在用户这个层面没有得到某资源的访问权限,就会用这个用户所在的岗位进行计算...,依次类推。还以上图为例,假如用户A隶属于部门A,但不在岗位A与群组A当中,那么用户A在角色A中的权限又是什么呢?通过上面描述的规则,那么用户A对角色A中的两个URL资源是允许访问,因为用户A直接与角色A绑定,且访问权限是允许,所以权限在计算时在用户层面就可以得到答案,而不再向下进行计算,所以用户A可以访问角色A中两个URL资源。

上述角色成员计算的规则其实就是SpringSecurity中投票器的计算规则,利用这个规则我们就可以实现权限的加减。举个例子,假如有一个部门X,其中有1000个人,这1000人都可以对资源X进行访问,现在这个部门来了个新同事X,因为其还处于试用期,所以还不能访问资源X,这时在进行权限设置时就可以利用权限的加减法实现,具体做法是:创建一个角色,比如角色X,在这个角色当中放置资源X,然后将部门X与这个角色X关联且访问权限为允许,同时再把这个新同事X与这个角色X关联,访问权限为拒绝,这样就可以实现我们需要的权限功能,这也是权限加减功能最好的应用示例,如下图所示:

Page 34: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

在BDF2-CORE模块当中,在我们执行了初始化菜单之后,登录系统就可以在左边导航区域看到 菜单,如下图所示:权限管理相关

在权限管理菜单组当中,我们可以找到针对角色、角色资源、角色成员及群组维护的菜单项,在进行URL权限管理时,首先我们需要添加相关角色( 中添加),然后通过 需要权限管理的URL与角色关联起来,最后利用 把成员(用户、岗位、部门及群组)角色维护 URL权限维护将 角色成员维护放到这个角色当中,角色维护效果页面如下:

Page 35: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

角色添加好之后,再进到 页,将需要管控的URL与这个角色关联起来,效果如下图所示:URL权限维护

可以看到,这里在建立角色与URL关联的操作过程当中,可以看到URL我们只需要勾选就行,如果其中不存在你需要管控的URL,那么,可以在菜单维护页中把需要的URL添加进去即可,如果添加的URL不想成为导航菜单,只需要将 设置为否即可。是否用于导航

最后,就是将成员放置到角色当中,如下图所示:

Page 36: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

在上图所示,无论是添加用户还是岗位还是部门还是群组成员,都可以设置这个成员在当前角色中的权限(是否授权)。

3.7.组件权限控制

组件权限角色URL权限控制基本一样,也需要创建角色,然后将成员放置到角色当中,成员还是URL权限当中介绍的用户、岗位、部门及群组四个,并且在进行权限计算的时候采用投票器优先级原则,最后将角色与组件关联起来,需要注意的是组件是位于URL页面当中,所以选择组件时需要先选择这个组件所在的URL页面,如下图所示:

如上图所示,BDF2在进行组件授权时,会根据选中的页面,自动对当前页面中所拥有的组件进行解析,形成一棵组件树,在这棵组件树当中,所有可设置权限的组件前面都会有个复选框,而没有复选框的组件表示它不能进行权限设置(因为这些组件没有设置ID或name或caption或其它一些可以标识这个组件的属性,所以不允许对其设置权限)。

所有的组件在设置权限时都可以选择要设置它的哪种权限:读权限还是写权限(也就是页面中显示的读操作与写操作),对于Dorado7中组件来说,大多数都有读和写两种类型的权限可供控制,比如Button的读权限就是我们能不能看到它,写权限就是能不能点击这个Button,诸如此类。

3.8.Controller

BDF2中提供了一个类似于Spring MVC或Struts MVC的Controller(实际上Dorado7的Controller利用的就是SpringMVC实现的),但相比Spring MVC或StrutsMVC,BDF2中提供的Controller功能更为简单,可控度及灵活性也更高,之前我们提到的用于在系统第一次使用初始化系统菜单时访问的http://l

地址,其中的generate.system.menu.action就是利用BDF2中Cocalhost:8080/bdf2-dynamic-web-project/generate.system.menu.actionontroller功能提供。

要定义一个BDF2中的Controller,我们需要编写一个IController接口的实现类,这个接口源码如下:

Page 37: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IController接口源码

package com.bstek.bdf2.core.controller;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * 实现该接口,并将类注册到Spring就可以通过指定的URL访问执行其中的execute方法体 * @author Jacky.gao * @since 2013-2-21 */public interface IController { String getUrl(); void execute(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletException; boolean anonymousAccess(); boolean isDisabled();}

这个接口定义了四个方法,第一个getUrl方法就是用于定义这个Controller访问地址的,比如之前的generate.system.menu.action,它的getUrl方法返回值就是/generate.system.menu,可以看到URL定义时要以/开头,同时后面不能添加.action;第二个方法execute就是我们业务方法执行的地方;第三个方法anonymousAccess就是这个Controller是否允许用户在未登录的情况下访问,返回false,表示允许未登录访问,否则必须要登录才能访问这个Controller;最后一个就是是否禁用这个Controller,为false就可以正常访问,否则就不能访问。

值得注意的是,Controller定义好之后,访问的地址是getUrl方法返回的字符串+"."+"action",这里的.action是BDF2Controller访问的默认URL后缀,这个值由 属性决定,这个属性值默认为action,所以我们访问Controller的地址为:getbdf2.controllerSuffixUrl方法返回的字符串+"."+"action",当然一般情况下,这个属性值是没必要修改的。

IController接口实现类编写完成之后,需要将其配置到Spring当中,变成一个标准的Spring(不要指定这个bean的ID),这样我们就可以像访问generate.system.menu.action这个Controller一样访问我们新定义的Controller了。

3.9.单点登录相关

在BDF2-CORE当中,默认就提供了对CASSSO支持,如果我们已经有了现成的,并且已在CAS客户端Server上配置好相关证书信息,那么就可以通过修改BDF2-CORE模块中的相关属性,快速将BDF2应用接入到当前的CAS Server当中,具体需要修改的属性如下:

属性名 类型 默认值 描述 示例

bdf2.casLoginUrl String /cas.login.d 当采用CAS进行SSO登录时,设置CASServer的登录页面的URL地址

bdf2.casLoginUrl=https://www.bstek.com:8443/cas-server/login

bdf2.casServerUrl String /cas.server 设置CASServer的URL地址

bdf2.casServerUrl=https://www.bstek.com:8443/cas-server

bdf2.casClientServerUrl

String http://localhost:8080/bdf2-test

设置要采用CASSSO认证的客户端应用的地址

bdf2.casClientServerUrl=http://localhost:8080/bdf2-test

Page 38: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.logoutSuccessURL

String /bdf2.core.view.response.LogoutSuccess.d

主框架右上角退出系统快捷图标点击时,退出系统成功后跳转的地址,这里设置为CASSSO的logout,表示在系统内部退出完成(销毁Session之类操作完成)之后,再跳转到CASSSO的logout进行SSO的登出操作。

bdf2.logoutSuccessUR=L https://www.bstek.c

om:8443/cas-server/logout

bdf2.authenticationType

String form 这个属性目标支持两个值,一个就是默认的form,表示采用BDF2系统提供的登录表单登录;另一个就是cas,表示采用CASSSO登录。

bdf2.authenticationType=cas

这里需要强调的是BDF2中对于CASSSO的支持,我们做了完善的功能测试,以保证其不会有问题,所以如果您要使用这一功能,请确保您的CASServer配置正确,确保部署BDF2应用的客户端Server对于 CAS Server证书配置正确,这样才能保证能把BDF2中CASSSO支持用起来。一旦出现问题,多数都是您的环境配置问题,与BDF2对CAS SSO支持无关,一句话,您一定要能熟练使用CASServer来构建SSO环境,不能一知半解,边猜边做。

如果您需要采用其它的登录方式,这种登录既非系统提供的表单登录,也非CAS的SSO(可能是其它类型的SSO),那么就需要通过下面的两种方式实现。

,就是从BDF2-CORE-1.0.1开始提供的通过实现IRetrivePreAuthenticatedUser方式实现,该接口的源码如下所示:第一种方法

IRetrivePreAuthenticatedUser接口源码

package com.bstek.bdf2.core.security;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.bstek.bdf2.core.business.IUser;/** * @author Jacky.gao * @since 2013年7月5日 * 获取通过其它方式已经登录的用户信息,比如通过SSO等 */public interface IRetrivePreAuthenticatedUser { /** *根据给出的request与response对象,取出当前已通过其它途径预认证的IUser对象,如果返回null表示预认证未通过,系统将不会处理 * @param request * @param response * @return 返回已被预认证通过的IUser对象 * @throws ServletException */ IUser retrive(HttpServletRequest request,HttpServletResponse response) throws ServletException;}

可以看到,这个接口当中只有一个retrive方法,该方法的作用就是要接口实现类返回当前已预认证通过的IUser接口实现类对象。该接口编写完成后需要配置到Spring环境当中,作为一个标准的Spring

Page 39: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

Bean,系统会自动检测到该接口实现类,这样用户未登录的情况下访问某个需要登录才能访问的页面时会自动调用这个接口实现类,返回已认证用户信息,从而完成用户自动登录的动作。下面的是一个非常简单的IRetrivePreAuthenticatedUser接口实现类,其源码如下:

测试IRetrivePreAuthenticatedUser接口实现类

package test;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;import com.bstek.bdf2.core.business.IUser;import com.bstek.bdf2.core.model.DefaultUser;import com.bstek.bdf2.core.security.IRetrivePreAuthenticatedUser;import com.bstek.bdf2.core.service.IDeptService;import com.bstek.bdf2.core.service.IGroupService;import com.bstek.bdf2.core.service.IPositionService;@Componentpublic class TestRetrivePreAuthenticatedUser implements IRetrivePreAuthenticatedUser { @Autowired @Qualifier(IDeptService.BEAN_ID) private IDeptService deptService; @Autowired @Qualifier(IPositionService.BEAN_ID) private IPositionService positionService; @Autowired @Qualifier(IGroupService.BEAN_ID) private IGroupService groupService; public IUser retrive(HttpServletRequest request, HttpServletResponse response) throws ServletException { //从其它源读取登录信息,比如某些硬件卡中读取登录信息等 DefaultUser user=new DefaultUser("admin"); user.setCompanyId("bstek"); //为登录成功的用户设置所在部门、岗位及群组信息 user.setDepts(deptService.loadUserDepts(user.getUsername())); user.setPositions(positionService.loadUserPositions(user.getUsername())); user.setGroups(groupService.loadUserGroups(user.getUsername())); //为登录成功的用户设置所在部门、岗位及群组信息结束 return user; }}

就是实现ISecurityInterceptor接口。第二种方法

在BDF2当中,除了通过IRetrivePreAuthenticatedUser接口实现获取预认证的登录用户外,还可以通过实现名为ISecurityInterceptor接口,获取取预认证的登录用户,完成用户登录认证。该接口的实现类,同样也需要配置到Spring环境当中,该接口的源码如下:

Page 40: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

ISecurityInterceptor接口源码

package com.bstek.bdf2.core.security;import org.springframework.security.web.context.HttpRequestResponseHolder;/** * 一个供开发人员使用的在登录、认证之前或之后或失败后需要进行业务处理的接口,<br> *开发人员可以根据需要,有选择的覆盖该类中的某个方法,比如需要在用户登录前进行一些处理,那么就可覆盖其中的beforeLogin方法,<br> *依次类推,使用时,将实现类配置到spring当中即可,系统运行时会自动扫描该抽象类实现的存在,如果有就会加载处理 * @author jacky.gao * @since 2013-1-22 */public interface ISecurityInterceptor { /** * 用户登录系统之前进行的处理动作 * @param holder 一个用于包装HttpRequest/HttpResponse的对象 */ void beforeLogin(HttpRequestResponseHolder holder); /** * 用户登录系统成功之后进行的处理动作 * @param holder 一个用于包装HttpRequest/HttpResponse的对象 */ void loginSuccess(HttpRequestResponseHolder holder); /** * 用户登录系统认证失败时需要处理的动作 * @param holder 一个用于包装HttpRequest/HttpResponse的对象 */ void loginFailure(HttpRequestResponseHolder holder); /** * 用户在访问系统资源时(比如访问某URL),系统安全模块对用户进行授权之前需要处理的动作 * @param holder 一个用于包装HttpRequest/HttpResponse的对象 */ void beforeAuthorization(HttpRequestResponseHolder holder); /** * 用户在访问系统资源时(比如访问某URL),系统安全模块对用户进行授权成功之后需要处理的动作 * @param holder 一个用于包装HttpRequest/HttpResponse的对象 */ void authorizationSuccess(HttpRequestResponseHolder holder); /** * 用户在访问系统资源时(比如访问某URL或某模块),系统安全模块对用户进行授权失败之后需要处理的动作 * @param holder 一个用于包装HttpRequest/HttpResponse的对象 */ void authorizationFailure(HttpRequestResponseHolder holder);}

对于我们上述预认证需求,就可以通过编写这个接口的实现类来实现。因为这个接口当中包含的方法较多,一般情况下,我们只需要实现这个接口当中的一个方法即可,所以我们可以通过扩展系统当中提供的已实现了ISecurityInterceptor接口的SecurityInterceptorAdapter类来实现,这个类中提供了关于ISecurityInterceptor接口所有方法的空实现,所以我们在扩展这个类时只需要覆盖需要的方法即可。比如我们下面的实现类:

Page 41: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

DemoSecurityInterceptor类源码

package test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.security.web.context.HttpRequestResponseHolder;import org.springframework.stereotype.Component;import com.bstek.bdf2.core.context.ContextHolder;import com.bstek.bdf2.core.model.DefaultUser;import com.bstek.bdf2.core.security.SecurityInterceptorAdapter;import com.bstek.bdf2.core.service.IDeptService;import com.bstek.bdf2.core.service.IGroupService;import com.bstek.bdf2.core.service.IPositionService;@Componentpublic class DemoSecurityInterceptor extends SecurityInterceptorAdapter { @Autowired @Qualifier(IDeptService.BEAN_ID) private IDeptService deptService; @Autowired @Qualifier(IPositionService.BEAN_ID) private IPositionService positionService; @Autowired @Qualifier(IGroupService.BEAN_ID) private IGroupService groupService; @Override public void beforeAuthorization(HttpRequestResponseHolder holder) { if(ContextHolder.getLoginUser()==null){ //表示未登录 //从其它源读取登录信息,比如某些硬件卡中读取登录信息等 DefaultUser user=new DefaultUser("admin"); user.setCompanyId("bstek"); //为登录成功的用户设置所在部门、岗位及群组信息 user.setDepts(deptService.loadUserDepts(user.getUsername())); user.setPositions(positionService.loadUserPositions(user.getUsername())); user.setGroups(groupService.loadUserGroups(user.getUsername())); //为登录成功的用户设置所在部门、岗位及群组信息结束 //这里的IUser应该是从其它源里读取到的经过认证的合法的用户对象,再转换成IUser对象实例 //接下来需要将这个user对象放置到session当中及Spring Security的环境当中,以告诉系统已成功登录 this.registerLoginInfo(user, holder); } }}

上述代码当中比较关键的是最后一句,这个registerLoginInfo方法位于SecurityInterceptorAdapter类当中,它可以把认证的用户对象放到系统环境当中,用以标明用户已登录。

还有一种情况,那就是可能我们的系统当中存在多种登录方式,可能根据用户访问的URL后面的参数来决定跳转到哪个登录页面(我也不清楚什么时候会有这种变态需求),如果是这样,上面的代码就需要调整成下面的样子:

两种获取预认证用户的方法比较综合比较上述两种方法,我们推荐使用第一种,第一种方法就是为获取已认证的用户,实现自动登录而准备的,所以它看起来更加自然,与系统的结合性也更好。

Page 42: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

修改后的SecurityInterceptor实现类

package test;import org.springframework.security.web.context.HttpRequestResponseHolder;import org.springframework.stereotype.Component;import com.bstek.bdf2.core.context.ContextHolder;import com.bstek.bdf2.core.security.SecurityInterceptorAdapter;@Componentpublic class DemoSecurityInterceptor extends SecurityInterceptorAdapter { @Override public void beforeAuthorization(HttpRequestResponseHolder holder) { if(ContextHolder.getLoginUser()==null){ //表示未登录 String loginType=holder.getRequest().getParameter("loginType"); if(loginType!=null && loginType.equals("abc")){ throw new MyLoginException(); } if(loginType!=null && loginType.equals("def")){ throw new MyLogin1Exception(); } } }}

这里的MyLoginException代码如下:

MyLoginException

package test;public class MyLoginException extends RuntimeException {}

MyLogin1Exception代码与上述基本一样,这里不再罗列了。

从上述的代码中可以看到,一旦发现要采用某种登录方式,我们就抛一个特定异常(比如MyLoginException等),接下来我们就需要来编写一个IExceptionHandler接口实现类,用于捕获我们之前抛出的异常,一旦捕获,我们可以进行页面跳转等相关我们需要的操作(比如跳转到我们需要的登录页面等),我们的IExceptionHandler接口实现类代码如下:

Page 43: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

ExceptionHandler实现类

package test;import org.springframework.security.web.context.HttpRequestResponseHolder;import org.springframework.stereotype.Component;import com.bstek.bdf2.core.exception.IExceptionHandler;@Componentpublic class DemoExceptionHandler implements IExceptionHandler { public void handle(HttpRequestResponseHolder holder, Throwable exception) { try{ if(exception instanceof MyLoginException){ holder.getResponse().sendRedirect("/login.jsp"); } if(exception instanceof MyLogin1Exception){ holder.getResponse().sendRedirect("/login.html"); } }catch(Exception ex){ throw new RuntimeException(ex); } } public boolean support(Throwable exception) { return ((exception instanceof MyLoginException) || (exception instanceof MyLogin1Exception)); }}

上述代码比较简单,这里就不再解释了。同样这个实现编写完成之后需要配置到Spring环境当中。利用这么一种机制,大家可以发挥想象,类似的需求都可以通过这一功能机制实现,具体就不再啰嗦了。

3.10.获取登录用户信息

对于Dorado7项目来说,可能需要在两个地方获取登录用户信息:一个是Java代码当中,另一个就是在Dorado7的View当中。我们先来看看如何在Java代码当中获取登录用户信息。

之前的内容当中我们提到过BDF2当中的ContextHolder对象,在这个ContextHolder对象当中,提供了一系列的静态方法,通过这个方法我们可以取到当前正在使用的数据源名称(如果有设置的话),根据一个Bean的Id取到一个配置在Spring环境当中的Bean,或者取到当前登录的用户名。ContextHolder对象所包含的静态工具方法如下表所示:

方法名 描述

public static String getBdfTempFileStorePath() 获取BDF2系统使用的系统临时目录的绝对路径,这里获取到的值由bdf2.systemTempDir属性决定,具体参见对bdf2.systemTempDir属性描述

public static WebApplicationContext getApplicationContext() 获取当前系统的Spring的WebApplicationContext对象。

public static <T> T getBean(String beanId) 根据id找到指定的Spring Bean对象。

public static HttpServletRequest getRequest() 获取当前线程正在使用的HttpServletRequest对象。

public static HttpServletResponse getResponse() 获取当前线程正在使用的HttpServletResponse 对象。

public static HttpSession getHttpSession() 获取当前用户的 对象。HttpSession

public static IUser getLoginUser() 获取当前登录的用户对象,如果用户未登录,这里将返回null。

Page 44: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

public static String getLoginUserName() 获取当前登录的用户对象,如果用户未登录,这里将产生NullPointException异常,所以通常使用getLoginUser方法判断用户是否登录,而不要直接使用这个方法。

了解了ContextHolder对象之后,您可能已经明白,在Java代码当中获取登录用户的方法就是调用ContextHolder.getLoginUser()方法,这个方法返回的是一个IUser的接口对象,这个接口我们之前有过详细的描述,通过返回的接口对象,我们可以获取当前登录用户的各种信息。

某些时候,您可能需要在Dorado的View开发当中调用当前登录用户的信息,这个时候您可以采用BDF2当中提供的Dorado7EL表达式来实现。BDF2当中提供了三个Dorado7EL表达式,分别用户于获取登录用户对象信息、登录的用户的用户名以及登录时产生的错误信息,如下表所示:

EL表达式名称 说明 示例

loginUser 返回当前登录用户对象,实际上就是调用ContextHolder.getLoginUser()方法

JS当中:varloginUsername="${loginUser.getUsername()}";

View组件属性当中:${loginUser.getUsername()}

loginUsername 返回当前登录用户的用户名 JS当中:var loginUsername="${loginUse}";rname

View组件属性当中:${ }loginUsername

authenticationExceptionMessage 登录时产生的错误信息 JS当中:var loginUsername="${authentic}";ationExceptionMessage

View组件属性当中:${authenticationExce}ptionMessage

4.BDF2-JOB

BDF2中的JOB是指在应用服务端定义一些可以周期性执行的动作,这就是JOB。BDF2中的JOB是在扩展QUARTZ的基础之上实现,但在编写及管理JOB时并不需要我们具备QUARTZ的相关知识,BDF2-JOB模块提供了完备的JOB编写、配置及运行控制功能,可最大限度降低JOB编写、配置及管理的复杂度。

要使用BDF2-JOB模块,如果您采用的是Dynamic WebProject这种传统类型的Web应用项目,那就比较费劲了,我们需要自己到http://nexus.bsdn.org上找到BDF2-JOB模块需要jar,再下载相关依赖的jar,再放置到我们的Dynamic Web Project类型的项目当中;或者您可以到我们提供的在 当中,选择项目类型为Dynamic线项目创建向导WebProject,再勾选我们需要的BDF2-JOB模块,最后点击下载,这样创建的项目中既包含BDF2-JOB模块的jar也包含这个模块在运行是所需要的第三方jar。但如果您是采用Maven来管理我们的项目,那么添加BDF2-JOB模块就非常简单,我们需要做的就是打开pom.xml文件,在其中添加下面的dependency。

BDF2-JOB模块的dependency

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-job</artifactId> <version>2.0.0</version></dependency>

当前是2.0.0版本,后续随着功能的增加,版本号应该就会大于2.0.0。

项目配置好之后,接下就可以在eclipse中运行项目,启动项目时,可以看到在控制台会出现下面的异常:

Page 45: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

Job模块运行时抛出的异常

Caused by: java.lang.RuntimeException: Job module need a [com.bstek.bdf2.job.service.IJobDataService]interface implementation at com.bstek.bdf2.job.view.calendar.CalendarMaintain.afterPropertiesSet(CalendarMaintain.java:102) atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1545) atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483) ... 23 more

从异常的提示消息可以看出,使用BDF2-JOB模块,我们需要编写一个com.bstek.bdf2.job.service.IJobDataService接口的实现类,并将其配置到Spring当中,这个接口类的源码如下:

IJobDataService接口源码

package com.bstek.bdf2.job.service;import java.util.List;import com.bstek.bdf2.job.model.JobDefinition;/** * @author Jacky.gao * @since 2013-3-10 */public interface IJobDataService { String getCompanyId(); List<JobDefinition> filterJobs(List<JobDefinition> jobs);}

可以看到这个接口有两个方法:第一个getCompanyId方法需要我们返回当前采用的公司ID,同样这个是为基于SAAS模式的应用服务的,如果您的应用采用的是非SAAS模式,那么在这个方法里返回一个固定的字符串即可;第二个方法是filterJobs,它的作用是根据系统信息对当前要加载的Job进行过滤,当然如果您没什么需要过滤的,那么直接返回系统给出的job就行。下面是这个接口的简单实现:

Page 46: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IJobDataService实现类示例

package test;import java.util.List;import com.bstek.bdf2.job.model.JobDefinition;import com.bstek.bdf2.job.service.IJobDataService;public class TestJobDataService implements IJobDataService { @Override public List<JobDefinition> filterJobs(List<JobDefinition> jobs) { return jobs; } @Override public String getCompanyId() { return "bstek"; }}

IJobDataService接口的实现类编写好之后,需要将其配置到Spring环境当中,让其成为一个标准的SpringBean,在配置时不需要为这个Bean指定ID。

再次启动我们的项目,可以发现项目已经可以正常启动了。

在BDF2-JOB模块当中,提供了针对JOB定义与管理,JOB运行服务监控等相关功能,这些功能都是通过可视化页面来实现的,所以如果您在使用BDF2-JOB模块时,又使用了BDF2-CORE模块,那么可以在登录之后访问generate.system.menu.action这个用于初始化菜单的URL,因为我们加入了BDF2-JOB模块,所以这个模块下包含的URL会自动创建出来,创建好的导航菜单如下图所示:

到这里,JOB模块的安装与配置就完成了,下面我们来看看JOB模块提供了哪些属性允许我们覆盖:

属性名 类型 默认值 描述

bdf2.scanJobCronExpression String 0 0/10 * * * ? 利用JOB模块管理JOB时,系统每隔多长时间对调整的JOB进行扫描加载,这里默认是每天从第0分钟开始,每隔10分钟扫锚一次变更信息,也就是第0分钟扫描、第10分钟扫描、第20分钟扫描、第30分钟扫描、 、第40分钟扫描

......第50分钟扫描、第0分钟扫描

bdf2.jobDataSourceName String 空 Job模块采用的数据源名称,默认为空,表示采用默认数据源。

Page 47: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.schedulerConfigPropertiesFile

String classpath:com/bstek/bdf2/job/config/bdf2.quartz.properties

Quartz启动时相关配置参数文件所在位置,一般情况我们是不需要覆盖这个属性,额外定义属性文件的。

bdf2.jobApplicationName String 空 Job模块所在应用的名称,该属性主要为用于JOB集群模块(bdf2-job-daemon)服务。

bdf2.runJobInCurrentInstance

boolean true 当前项目是否运行JOB,默认值为true,表示运行JOB,否则则不运行,这个属性同样是为JOB集群模块(bdf2-job-daemon)服务。单实例意义不大。

bdf2.jobThreadCount int 10 JOB服务在运行时准备多少个线程来运行用户配置的JOB,默认值为10,差不多可以运行100个JOB左右,按这个标准,您可以根据自己项目中JOB的数量来灵活修改这个属性值,一般情况下,可以满足多数JOB需求,不用修改。

4.1.Job定义与配置

BDF2-JOB模块中在定义JOB时,我们遵循标准的Quartz定义JOB的方法,那就是实现org.quartz.Job接口,这样对于那些已经在使用Quartz来定义JOB的用户,就可以无缝将自己经编写好的JOB迁移到BDF2-JOB模块当中来。

下面的代码当中向我们展示了一个实现了org.quartz.Job接口具体JOB实现类:

测试JOB实现类

package test;import java.util.List;import java.util.Map;import org.quartz.Job;import org.quartz.JobDataMap;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import com.bstek.bdf2.core.orm.jdbc.JdbcDao;public class TestJob extends JdbcDao implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap jobDataMap=context.getJobDetail().getJobDataMap(); String targetUser=jobDataMap.getString("targetUser"); String sql="select * from BDF2_USER where USERNAME_=?"; List<Map<String,Object>> users=this.getJdbcTemplate().queryForList(sql, new Object[]{targetUser}); if(users.size()>0){ System.out.println("用户"+targetUser+"的中文名为:"+users.get(0).get("CNAME_")); } }}

从代码中可以看到,这个JOB在运行时从JobDataMap里取出一个用户的用户名参数targetUser,再用这个用户名到数据库中查询这个用户对应的详细信息,如果用户存在就在系统控制台中打印出这个用户的CNAME_属性,也就是它的中文名。

Page 48: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

JOB实现类编写好之后,需要将其配置到Spring当中,配置方法如下:

JOB实现类配置

<bean id="testJob" class="test.TestJob"></bean>

JOB编写配置完成之后,接来就可以启动我们的工程,利用BDF2提供的在线配置界面将JOB添加到服务当中。

登录系统,进入“任务调度”菜单,点击其下的“JOB定义”菜单项,如下所示:

点击工具栏上的“添加”按钮,在弹出的窗口中,为新的JOB定义一个名称,在“ ”列中,点击选择我们之前配置的JOB的ID,可使用的BeanId以看到在弹出的窗口当中,就有我们之前在Spring当中配置的那个testJob对象,如下图所示:

JOB选择完成后,我们就需要定义这个JOB该如何运行,也就是配置下面的“Cron表达式”,点击按钮可以看到一个图形化的Cron表达式配置界面,可以看到这个界面功能是非常完善的,它可以完成95%以上的简单或复杂的Cron表达式配置,如下图所示:

Page 49: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

我们这里定义的是让这个JOB从第0秒开始,每隔10秒运行一次,定义完成之后,点击其下方的“生成Cron表达式”按钮,便可生成具体的Cron表达式,同时点击“解析表达式”按钮,可以看到这个Cron表达式最近50次的具体运行时间。

因为我们这个testJob还需要一个名为targetUser的参数,所以在完成JOB本身定义之后,我们还需要为这个JOB定义一个名为targetUser的JOB运行参数,如下图所示:

这样,JOB的所有定义工作都完成了,我们点击工具栏上的“启动任务”按钮,就可以将JOB添加的待运行的JOB队列当中。

之前我们提到,JOB默认的扫描时间为0分钟、10分钟、20分钟......,所以看到点击工具栏上的“启动任务”按钮后,JOB状态为启动中,一旦服务器端时间到达0分钟、10分钟、20分钟......,JOB就会被扫描到,并开始运行。JOB每次运行,系统都会记录它的运行结果:成功还是失败,如果失败还会取出失败的异常并存储下来,如下图所示:

同时每次JOB运行后,我们都可以在控制台看到输出的targetUser用户对应的中文名(如果这个用户存在的话)。

从配置界面当中可以看到,在配置JOB运行时,还可以排除某些运行日期,比如将周末或节假日排除等,对于排除日期的定义是在“节假日设定”

Page 50: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

页面当中完成的,如下图所示:

从图中可以看到,在定义节假日时可以选择具体类型,目前支持四种类型:星期中的某些天是指一个星期中的某此天;月份中的某些天,是以月份中的天为单位来确定日期;具体日期则是选择具体的年月日;每天都经历的日期,则是让我们选择月份与具体的天,比如定义五一劳动节或十一国庆节等。定义好节假日后,就可以将需要排除的节假日加入到JOB当中,这样一旦时间是这些节假日,JOB就不会执行,否则则执行。

4.2.JOB集群

标准的BDF2-JOB模块是不支持JOB集群的,如果我们的包含JOB的应用需要集群部署,同时又需要JOB能够集群环境下依次运行,那么我们可以将BDF2当中提供的BDF2-JOB-DAEMON模块加入到项目当中,这样我们的项目中的JOB在运行时就支持集群了。

BDF2-JOB-DAEMON模块的JAR,我们可以到http://nexus.bsdn.org上下载,或从我们提供的在线项目向导中选择BDF2-JOB-DAEMON模块后再下载,同样,如果您采用的是Maven来管理您的项目,那么只需要将下面这段dependency加入到我们项目的pom.xm中即可:

JOB-DAEMON模块依赖的添加

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-job-daemon</artifactId> <version>2.0.0</version></dependency>

打开WEB-INF/dorado-home目录下的configure.properties文件,在其中添加一名为bdf2.jobApplicationName的属性,这个属性的值决定当前JOB应用的名称,比如我们这里配置为bdf2.jobApplicationName=jobtestapp,表示当前包含JOB的应用名称为jobtestapp;同时还需要添加bdf2.runJobInCurrentInstance参数,将其值设置成false,表示JOB模块自己不启动服务,服务是否启动交由job-daemon模块负责。项目准备好之后,就可以启动了,包含BDF2-JOB-DAEMON模块的项目启动时我们可以在控制台看到如下所示的异常:

Page 51: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

包含BDF2-JOB-DAEMON模块的项目启动时产生的异常信息

Caused by: java.lang.RuntimeException: You need config a named [jobtestapp.jobInstanceName] systemproperty when use bdf2-job-daemon module! atcom.bstek.bdf2.job.daemon.detection.InstanceDetection.afterPropertiesSet(InstanceDetection.java:97) atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1545) atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483) ... 23 more

从异常信息的描述来看,在使用BDF2-JOB-DAEMON模块前我们需要在配置一个名为jobtestapp.jobInstanceName的系统参数,也就是在当前运行的应用服务器当中配置一个JVM参数,可以看到这个参数名采用的是bdf2.jobApplicationName属性值+".jobInstanceName"的命名方式,这个参数配置好之后,在JAVA代码当中可以通过System.getProperty(propertyKey)来获取,对于在应用服务器当中配置一个JVM参数,对于不同应用服务器配置方法都不相同,所以在配置时我们需要查询该应用服务器手册,了解配置方法。

我们这里采用的是Eclipse WTP中提供的Tomcat7作为应用服务器,它的配置方法是首先双击应用服务器名称,打开配置界面,如下图所示:

如上图所示,打开配置窗口之后,我们需要单击上图中用红色框标记的链接,在弹出的窗口中添加如下图所示的配置信息即可。

Page 52: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

如上图所示,我们将这个名为jobtestapp.jobInstanceName的变量值定义为"jacky",变量配置完成之后,启动我们的应用,系统还是会抛出一个异常提示,如下所示:

异常提示消息

Caused by: java.lang.RuntimeException: Use [bdf2-job-daemon] module you need define a named[bdf2.jobDaemon.instanceNames] property! at com.bstek.bdf2.job.daemon.detection.InstanceDetection.initJobDetail(InstanceDetection.java:73) at com.bstek.bdf2.job.daemon.detection.InstanceDetection.startDaemonJob(InstanceDetection.java:49) atcom.bstek.bdf2.job.daemon.detection.InstanceDetection.afterPropertiesSet(InstanceDetection.java:99) atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1545) atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483) ... 23 more

异常提示我们使用JOB-DAEMON模块我们还需要配置一个名为bdf2.jobDaemon.instanceNames的参数,这个参数的作用是告诉系统当前应用的集群中有多少个实例,各个实例的实例名又是什么,注意这里要填写的实例名就是我们之前在JVM环境中配置的名为jobInstanceName的变量值,比如我们刚定义的"jacky"。需要注意的是这里定义的实例名一定是一个以上,只有一个以上的实例才能算是集群。这里在定义多个实例名时要以逗号分隔,同时定义的实例名顺序,也就决定了集群JOB在运行时的顺序。打开WEB-INF\dorado-home下的configure.properties文件,在其中添加名为bdf2.jobDaemon.instanceNames属性,值为“jacky,tom”,如下图所示:

Page 53: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

我们将bdf2.jobDaemon.instanceNames属性值定义为“jacky,tom”,那就表示在集群当中名为jacky的实例上部署的JOB应用最先运行,一旦名为jacky的实例上部署的应用中JOB服务停了,那么实例名为tom上的JOB应用会将运行JOB的任务接过来继续运行,依次类推。

JOB-DAEMON模块除了可以提供集群JOB服务的运行管理之外,它还可以提供单应用模式下,JOB服务守护的作用。默认JOB-DAEMON所起的是集群环境下JOB服务的心跳检测功能,如果我们需要它在单应用模式下来守护我们的JOB服务,那需要在configure.properties文件当中添加一个名为bdf2.jobDaemon.detectionMode的属性,并将其值设置为daemon,这个属性默认值为heartbeat,表示为集群环境下的JOB提供心跳检测服务,一旦改成daemon,那它只能为单应用中的JOB服务了,这样,一旦这个应用当中的JOB服务挂了,这个守护线程会马上检测到,并自动将JOB服务重启。当然一般情况下,对于单应用环境中,JOB服务的守护不是很有必要。

5.BDF2-JBPM4

BDF2-JBPM4是基于jBPM4流程引擎开发,在这个模块当中提供了操作流程常用的API、流程模块的管理与配置、流程模块的在线调试等众多常用的流程功能。同样将BDF2-JBPM4模块添加到我们的项目当中我们需要到nexus.bsdn.org上下载BDF2-JBPM4模块相关jar包,或者到我们的项目在线创建向导中勾选BDF2-JBPM4模块,然后下载项目;如果您采用的是Maven,那么只需要在您项目的pom.xml当中添加BDF2-JBPM4模块的依赖即可,如下所示:

BDF2-JBPM4模块所需要的依赖

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-jbpm4</artifactId> <version>2.0.0</version></dependency>

值得注意的是,BDF2-JBPM4模块在运行时依赖BDF2-JOB模块,所以如果您是非Maven结构项目,在下载BDF2-JBPM4模块的Jar后,你还需要将BDF2-JOB模块及其所依赖的Jar下载下来,当然如果您采用的是Maven来管理项目,那么,在添加BDF2-JBPM4模块的依赖后,Maven会帮我们自动下载相关依赖信息,比如依赖的BDF2-JOB模块及相关第三方Jar等。

Jar包配置完成之后,就可以运行我们的项目了。项目启动时,我们可能会见现下面的异常:

Page 54: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

BDF2-JBPM4模块启动时可能会出现的异常

Caused by: org.jbpm.api.JbpmException: no 'bdf2.jbpm4.transactionManagerBean' in currentenvironment at org.jbpm.pvm.internal.env.EnvironmentImpl.getFromCurrent(EnvironmentImpl.java:232) at org.jbpm.pvm.internal.env.EnvironmentImpl.getFromCurrent(EnvironmentImpl.java:218)

解决办法比较简单,我们需要在configure.properties文件当中添加一名为bdf2.jbpm4.transactionManager的属性,这个属性的值为当前环境当中正在使用的Spring的TransactionManager的bean的ID,如果您是通过向导创建的项目,在datasources.xml当中,已存在一个beanid为bdf2.jdbcTransactionManager的TransactionManager对象,所以我们在configure.properties文件当中添加bdf2.jbpm4.transactionManager=bdf2.jdbcTransactionManager即可,再次启动我们的工程,可以看到工程已经可以正常启动了。

在BDF2-JBPM4模块当中,也提供了一些用于管理的可视化界面,所以,如果您在使用BDF2-JBPM4模块时,也同时使用了BDF2-CORE模块,那么可以在登录之后访问generate.system.menu.action这个用于初始化菜单的URL,创建好的导航菜单如下图所示:

BDF2-JBPM4模块提供了下面这些属性允许我们的开发人员对其进行覆盖。

属性名 类型 默认值 描述

bdf2.jbpm4.transactionManager

String 空 指定jBPM4流程引擎要采用的TransactionManager的bean的ID

bdf2.jbpm4.listTodoTaskUrl String bdf2.jbpm4.view.todo.TodoTaskMaintain.d

在有BDF2-CORE模块的前提下,进入主界面之后,会自动提示是否有流程中的待办任务,如果有点击会进入到这个属性指定的URL页面查看并处理待办任务

为什么要定义bdf2.jbpm4.transactionManager属性呢jBPM4在运行是时,需要当前环境当中有事务包裹,这样才能保证流程操作的完整性,所以我们在使用jBPM4的时候就开放出这么一个名为bdf2.jbpm4.transactionManager属性,让我们来选择当前使用的transactionManager。

Page 55: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.jbpm4.defaultProcessUsername

String bdf2 默认的流程处理人的用户名

bdf2.jbpm4.dataSourceName String 空 流程模块采用的数据源,为空表示采用默认数据源

bdf2.jbpm4.disablePromoterAssignment

boolean false 是否禁用系统提供的基于流程发起人的任务分配器。如果为true,那么在任务分配人选择当中就看不到流程发起人这个任务分配器。

bdf2.jbpm4.disableSpecifyUserAssignment

boolean false 是否禁用系统提供的通过指定一个用户作为流程处理人的任务分配器,同样,如果禁用就看不到它了。

bdf2.jbpm4.disableFetchTodoTaskCountShortcutRegister

boolean false 在有BDF2-CORE模块的前提下,进入主界面之后,会自动提示是否有流程中的待办任务,提示有多个待办任务,如果将这个属性设置为true,那么就不再有这个提示功能。

bdf2.jbpm4.scanTaskReminderJobCronExpression

String 0 33 * * * ? 实现任务到达周期提醒及任务过期提醒JOB扫描的周期扫描时间,默认为每小时的第33分钟扫描一次。

bdf2.jbpm4.disableAnonymousDeployProcess

boolean false 是否禁用不登录就可部署流程功能,这个主要是为画流程模版的IDE直接部署流程功能服务的,如果为true,那么用户只能将流程模版导出,然后到系统提供的“配置与监控”当中部署流程模版。

bdf2.jbpm4.disabledCompleteTaskToolbarContentProvider

boolean false 是否禁用可以为通用工具栏提供完成任务功能的组件提供者

bdf2.jbpm4.disabledJumpNodeToolbarContentProvider

boolean false 是否禁用可以为通用工具栏提供跳转任务节点功能的组件提供者

bdf2.jbpm4.disabledSeeProcessImageToolbarContentProvider

boolean false 是否禁用可以为通用工具栏提供查看流程图功能的组件提供者

bdf2.jbpm4.genericTaskToolBarId

String 空 通用工具栏组件内容要输出目标工具栏的ID,如果为空,那么系统将自动创建工具栏,否则采用该属性指定的id对应的工具栏。

5.1.创建流程模版

要创建流程模版,在BDF2当中有两种选择:一种是利用传统的基于Eclipse插件开发的流程模版设计器实现;另外一种就是BDF2提供的在网页当中设计我们的流程模版。我们推荐使用后者,也就是在网页当中创建我们的流程模版,这样好处多多,既可以省掉下载安装Eclipse插件的麻烦,还可以实现网页当中直接编辑修改已设计好的流程模版。

要在网页当中创建流程模版,我们首先把启动应用,然后登录打开“配置与监控”页面,如下图所示,我们可以在工具栏上看到“新建流程模版”的按钮。

Page 56: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

点击“新建流程模版”的按钮,在弹出的窗口当中就可以设计我们需要的流程模版了,如下图所示:

设计好流程模版后,点击工具栏上的部署按钮就可以将其部署到当前的BDF2应用当中;同时还可以通过工具栏上打开流程模版图标,打开所有以前利用该网页版流程设计器设计的流程模版。

如果您不选择使用在线流程模版设计器,而青睐于传统的Eclipse插件版设计器,那么首先需要下载并安装流程模版的设计器,您可以选择到Jboss官网下载并安装jBPM4最新版的流程设计器(官网地址:http://www.jboss.org/jbpm),也可以选择BDF团队提供的Eclipse插件集,在这个插件集当中就包含一款重新设计的jBPM4的流程设计器,它的下载地址如下:

ftp://bsdn03.bsdn.org/pub/projects/bdf/bdf-plugins-1.0.1.zip

无论是下载哪款jBPM4流程设计器,一旦下载到本地,需要做的就是解压并将其放到eclipse所在目录的dropins目录下即可,重启Eclipse就可以在新建菜单当中找到jBPM4的流程模版设计器,设计器本身的使用不在本教程讨论范围内,关于jBPM4各个节点的作用及用法,您可以到Jboss官网了解。

这里需要注意的是如果您采用Jboss官网提供的流程设计器,为了让设计好的流程模版在部署到BDF2环境中后,能实现针对人工任务节点的众多在线配置,需要我们在设置流程模版添加人工任务时,将任务分配方式改成assignment-handler,对应的分配类为com.bstek.bdf2.jbpm4.context.GenericTaskAssignmentHandler;当然如果您采用的是BDFTeam提供的插件来设计jBPM4流程模版,那么就不需要修改了,因为所以添加到画板上的人工任务节点默认分配方式就是assignment-handler,且对应的分配类就是com.bstek.bdf2.jbpm4.context.GenericTaskAssignmentHandler。下图是我们用BDFTeam提供的插件来设计jBPM4流程模版,这是一个典型的费用报销流程:

网页版设计器使用注意事项在使用网页版流程设计器设计流程模版时,如果需要修改节点或连接线名称,可以通过双击的方式实现,如果修改节点的其它属性可以通过右键--》属性编辑,在弹出的窗口当中修改确定即可。同时如果需要修改流程模版的名称及key属性,同样可以在流程模版空白处右键--》属性编辑。

Page 57: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

如果您采用Jboss官网提供的流程设计器,设计好jBPM4流程模版后,要部署到BDF2应用当中,我们需要手工将包含流程定义的XML文件及对应的流程图片文件一起导出成一个zip文件,然后通过前面提到的管理与监控将流程模版上传;如果您采用的是BDFTeam提供的设计器,那比较简单,我们切换到“部署”标签页(如上图所示),在页面当中下半部分,输入正确的服务端的IP、端口号及contextPath我们就可以将这个流程模版部署到BDF2应用当中,当然上半部分还提供了流程模版导出功能,它同样是将包含流程定义的XML文件及对应的流程图片文件一起导出成一个zip文件,导出后的zip文件可通过下面的在线管理界面上传:

值得注意的是,这里上传的流程模版,一定是一个由xml及对应的流程图片文件构成的zip格式文件,否则流程将不能正确上传并部署。

5.2.流程模版的在线配置

无论是通过哪种方式设计好流程模版并部署到BDF2系统当中,一旦部署成功之后,接下来就可以对流程模版进行配置了,这里需要注意的是,一般情况下,我们在设计流程模版时(也就是在画流程图的时候)不需要对任务节点做太多配置(唯一要做的就是为其赋一个正确的名称),所以关于任务节点的配置都是在BDF2当中提供的 当中实现。配置与监控

在BDF2当中提供的 当中,选中要配置的流程模版,点击工具栏上的“节点配置”按钮,系统会弹出一个dialog,并在所有的任务节配置与监控点上添加链接,点击这个链接会弹出与这个任务节点相关的配置信息,如下图所示:

Page 58: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

从弹出的窗口当中可以看出,对于任务节点,我们可以在线配置的内容有四块:第一个就是当前任务节点对应处理表单页面,它可以是一个标准的doradoview(比如aa.bb.cc.Demo.d),也可是一个标准的JSP;第二个是配置任务的处理人,也就是任务分配,就是决定在这个节点上产生的任务由谁来处理;第三个是定义任务在产生以后如何提醒任务处理人(发站内消息、邮件或其它);第四个任务过期就是可以给任务一个处理时间,一旦超过个时间还没有人处理,那么就通知任务处理人或做些其它动作。

我们首先来看看“任务分配”页中的配置内容,如下图所示:

点击“添加”在弹出的窗口当中选择我们采用的任务分配器,但可以看到弹出窗口是空的,这是因为在选择任务分配器前,我们还需要将要采用的任务分配器加载到系统当中,具体做法就是点击菜单导航中的“任务分配管理”,在其中添加我们要采用的任务分配器,如下图所示:

Page 59: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

从上图当中可以看出,任务分配方式列表中,默认系统只提供了两种,对于用户来说,一般情况下,我都都需要定义自己的任务分配方式,如果需要定义,那么我们可以实现一个IAssignmentProvider接口的实现类,这个接口源码如下:

Page 60: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IAssignmentProvider接口源码

package com.bstek.bdf2.jbpm4.view.assignment.provider;import java.util.Collection;import org.jbpm.api.model.OpenExecution;import com.bstek.bdf2.jbpm4.model.AssignmentInfo;import com.bstek.bdf2.jbpm4.model.PrincipalDef;/** * @author Jacky.gao * @since 2013-3-23 */public interface IAssignmentProvider { /** * @return 返回配置页面所在的URL */ String getUrl(); /** * @return 返回配置类型的ID */ String getTypeId(); /** * @return 返回与配置类型ID对象的Name描述 */ String getTypeName(); /** * @param assignmentDefId 任务分配定义的ID * @return 根据任务分配定义的ID返回与之对应的任务分配信息 */ Collection<AssignmentInfo> getAssignmentInfos(String assignmentDefId); /** * 删除指定分配定义下的分配定义信息 * @param assignmentDefId 分配定义信息的ID */ void deleteAssignmentInfos(String assignmentDefId); /** * @param assignmentDefId 任务分配定义的ID * @param execution 当前流程实例执行上下文对象 * @return 根据任务分配定义的ID返回所有与之对应的任务处理人的PrincipalDef集合 */ Collection<PrincipalDef> getTaskPrincipals(String assignmentDefId,OpenExecution execution); /** * @return 是否禁用 */ boolean isDisabled();}

实际上,我们看到的系统当中默认提供的两种任务分配方式:流程实例发起人与指定一个用户,它们都是通过实现IAssignmentProvider接口,然后配置到Spring当中才出现在上图中的下接列表当中的。所以如果我们要定义自己的,同样要实现IAssignmentProvider接口。

如果不想系统默认提供的流程实例发起人出现在列表中,那么可以将bdf2.jbpm4.disablePromoterAssignment属性设置成true,同样对于指定一个用户的任务分配方式,如果不需要那么就将bdf2.jbpm4.disableSpecifyUserAssignment属性设置成true即可。

Page 61: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

实际上,BDF2默认提供的两种任务分配方式:流程实例发起人与指定一个用户,就是给我们对如何编写IAssignmentProvider接口实现类提供了两种不同类型的示例。对于流程实例发起人这种任务分配方式,在使用不需要界面配置,所以它的实现类相对简单,其源码如下:

PromoterAssignmentProvider类源码

package com.bstek.bdf2.jbpm4.view.assignment.provider.impl.promoter;import java.util.ArrayList;import java.util.Collection;import java.util.List;import org.apache.commons.lang.StringUtils;import org.jbpm.api.model.OpenExecution;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import com.bstek.bdf2.jbpm4.model.AssignmentInfo;import com.bstek.bdf2.jbpm4.model.PrincipalDef;import com.bstek.bdf2.jbpm4.service.IBpmService;import com.bstek.bdf2.jbpm4.view.assignment.provider.IAssignmentProvider;/** * 流程发起人任务分配方式 * @author Jacky.gao * @since 2013-3-23 */@Componentpublic class PromoterAssignmentProvider implements IAssignmentProvider { @Value("${bdf2.jbpm4.disablePromoterAssignment}") private boolean disabled; @Autowired @Qualifier(IBpmService.BEAN_ID) private IBpmService bmpService; public String getUrl() { return null; } public String getTypeId() { return "promoter"; } public String getTypeName() { return "流程实例发起人"; } public Collection<AssignmentInfo> getAssignmentInfos(String assignmentDefId) { List<AssignmentInfo> info=new ArrayList<AssignmentInfo>(); AssignmentInfo a=new AssignmentInfo(); a.setName("流程实例发起人"); a.setValue("从流程实例中取名为["+IBpmService.PROCESS_INSTANCE_PROMOTER+"]的变量值,这个变量值表示流程实例发起人用户名"); info.add(a); return info; } public Collection<PrincipalDef> getTaskPrincipals(String assignmentDefId,OpenExecution execution) { List<PrincipalDef> usernames=new ArrayList<PrincipalDef>();

Page 62: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

String username=(String)execution.getVariable(IBpmService.PROCESS_INSTANCE_PROMOTER); if(StringUtils.isEmpty(username)){ throw new RuntimeException("Variable not found ["+IBpmService.PROCESS_INSTANCE_PROMOTER+"]in current process instance"); } PrincipalDef p=new PrincipalDef(); p.setId(username); usernames.add(p); return usernames; } public void deleteAssignmentInfos(String assignmentDefId){ //do nothing } public boolean isDisabled(){

Page 63: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

return disabled; }}

但对于指定一个用户这种任务分配方式,因为涉及到页面,相比之下就复杂一些,我们来看看它的源码:

SpecifyUserAssignmentProvider类源码

package com.bstek.bdf2.jbpm4.view.assignment.provider.impl.specifyuser;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.List;import java.util.Map;import org.hibernate.Session;import org.jbpm.api.model.OpenExecution;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import com.bstek.bdf2.jbpm4.Jbpm4HibernateDao;import com.bstek.bdf2.jbpm4.model.AssignmentInfo;import com.bstek.bdf2.jbpm4.model.InternalAssignment;import com.bstek.bdf2.jbpm4.model.PrincipalDef;import com.bstek.bdf2.jbpm4.view.assignment.provider.IAssignmentProvider;/** * 指定一个用户作为任务处理人 * @author Jacky.gao * @since 2013-3-23 */@Componentpublic class SpecifyUserAssignmentProvider extends Jbpm4HibernateDao implementsIAssignmentProvider { @Value("${bdf2.jbpm4.disableSpecifyUserAssignment}") private boolean disabled; public String getUrl() { return "bdf2.jbpm4.view.assignment.provider.impl.specifyuser.SpecifyUserProvider"; } public String getTypeId() { return "specify_user"; } public String getTypeName() { return "指定一个用户"; } public Collection<AssignmentInfo> getAssignmentInfos(String assignmentDefId) { String hql="from "+InternalAssignment.class.getName()+" where assignmentDefId=:assignmentDefId"; Map<String,Object> map=new HashMap<String,Object>(); map.put("assignmentDefId", assignmentDefId); List<InternalAssignment> internals=this.query(hql, map); List<AssignmentInfo> infos=new ArrayList<AssignmentInfo>(); for(InternalAssignment internal:internals){ AssignmentInfo info=new AssignmentInfo(); info.setName("一个用户"); info.setValue(internal.getName()); infos.add(info);

Page 64: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

} return infos; } public Collection<PrincipalDef> getTaskPrincipals(String assignmentDefId,OpenExecution execution) { String hql="from "+InternalAssignment.class.getName()+" where assignmentDefId=:assignmentDefId"; Map<String,Object> map=new HashMap<String,Object>(); map.put("assignmentDefId", assignmentDefId); List<InternalAssignment> internals=this.query(hql, map); List<PrincipalDef> result=new ArrayList<PrincipalDef>(); for(InternalAssignment internal:internals){ PrincipalDef def=new PrincipalDef(); def.setId(internal.getValue()); result.add(def); } return result; } public void deleteAssignmentInfos(String assignmentDefId){ String hql="delete "+InternalAssignment.class.getName()+" where assignmentDefId=:assignmentDefId"; Map<String,Object> map=new HashMap<String,Object>(); map.put("assignmentDefId", assignmentDefId); Session session=this.getSessionFactory().openSession(); try{ session.createQuery(hql).setString("assignmentDefId", assignmentDefId).executeUpdate(); }finally{ session.flush(); session.close(); } } public boolean isDisabled() { return disabled; }

Page 65: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

}

可以看到,指定一个用户这种任务分配方式当中用到的页面是通过实现类当中的getUrl方法返回的,这个返回的页面是bdf2.jbpm4.view.assignment.provider.impl.specifyuser.SpecifyUserProvider,实际上这是一个标准的dorado7view文件,我们可以在BDF2-JBPM4源码包中找到bdf2.jbpm4.view.assignment.provider.impl.specifyuser包下的名为SpecifyUserProvider.view.xml文件,具体页面中的实现大家可以参考这个view文件,值得注意的是,这里的getUrl方法返回的页面只能是一个标准的dorado7view,而不能是JSP或其它,因为返回的URL会被系统采用dorado7中的subviewholder引入到父页面当中,而subviewholder中的页面只能是标准的dorado7 view。

定义一个任务分配方式,确认后,再次打开任务节点配置界面,就可以看到定义的任务分配方式,如下图所示:

任务处理人配置好之后,就可以利用这个配置与监控对流程模版进行测试了,通过点击工具栏上的“创建实例”按钮就可以创建一个新的流程实例,以这个流程模版为例,新的流程实例产生后,流程将会流转到初审节点,右上边的任务列表中也会出现这个初审节点上产生的任务,点击其上的“查看流程图按钮”,可以看到当前流程实例所处位置,如下图所示:

Page 66: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

5.3.任务处理页面组件的控制

在业务流程开发过程当中,对于某种类型的业务流程,可能我们需要将所有的任务节点处理页面配置成同一样,但需要在不同任务处理下,处理页面某些组件显示略微有些不同。比如对于一个普通的审批流程,有若干个审批任务节点,对应的审批任务处理页面工具栏上有一排按钮,可能在第一个审批任务当中只需要看到其中的一个按钮,第二个审批处理页面中只需要看到其中的两个或三个按钮,诸如此类。这些不同的审批任务页面除了这些需要显示的按钮不同外,其它全部功能都一样,这个时候就可以开发一个页面,在工具栏上放置好所有审批任务需要的按钮及相关功能,然后利用这里要讨论的任务处理页面组件的控制功能就可以轻松实现了。

任务处理页面组件的控制也位于节点属性配置的窗口当中,只要我们配置好任务节点对应的处理页面的URL,切换到“页面组件控制”标签页就可以看到当前页面下所有的组件,我们需要做的就是对需要进行隐藏或只读的组件打勾就行,如下图所示:

配置完成后,一旦对应的任务节点产生任务,我们通过BDF2提供的待办任务列表进入到任务处理页面,就可以看到配置的相应组件被隐藏或变成

Page 67: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

只读。

5.4.配置通用工具栏

在使用BDF2提供在流程模版在线配置时,可以看到它还为我们提供了一个通用工具栏配置的功能,利用这个通用工具栏配置,可以实现在我们配置的流程处理页面中动态插入BDF2中提供的通用工具栏,在这个通用工具栏配置当中还可以定义工具栏的位置,位置可以选择在流程业务页面的上部或下部,或者在我们的业务页面当中指定的工具栏中插入所配置的工具栏组件。

对于这个通用工具栏里工具里的组件,我们提供了一个名为IToolbarContentProvider接口,通过实现这个接口,并将接口实现类配置到Spring当中,可以为我们的通用工具栏提供所需要的工具栏组件,该接口的源码如下:

IToolbarContentProvider接口

package com.bstek.bdf2.jbpm4.view.toolbar;

/** * @author Jacky.gao * @since 2013-6-3 * 用于为流程中任务处理页面的通用工具栏提供具体内容 */public interface IToolbarContentProvider { /** * 返回包含要放置到通用工具栏上的具体内容所在的具体dorado7的view名称, *比如bdf2.jbpm4.view.toolbar.impl.completetask.CompleteTaskToolbarContentProvider,这个系统默认提供的用于完成任务的内容提供者页面 * @return 返回一个具体view的名称,不包含.d */ String getView(); /** * @return 返回能代表当前这个提供者的key,一个有意义的字符串,比如SimpleCompleteTask */ String key(); /** * @return 返回一段描述信息,用于说明这个提供者作用 */ String desc(); /** * @return 返回这个提供者是否被禁用,返回true,那么这个提供者将不能使用 */ boolean isDisabled();}

默认BDF2提供了三个IToolbarContentProvider接口的实现类,如下表所示:

实现类名 key desc isDisabled

CompleteTaskToolbarContentProvider

SimpleCompleteTask 直接完成任务 false

JumpNodeToolbarContentProvider

SimpleJumpToOtherTaskNode

直接跳转到其它任务节点 false

SeeProcessImageToolbarContentProvider

SeeProcessImage 查看流程图 false

有了这三个默认的实现类,所以我们在配置流程模版的通用工具栏时默认就可以看到这三个工具栏组件提供者,如下图所示:

Page 68: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

一旦我们在配置当中使用了通用工具栏,并且为这个通用工具栏添加了所需要的工具栏组件,那么就可以在我们的业务处理页面当中看到这个通用工具栏及其相关组件,如下图所示:

BDF2默认提供的三个工具栏组件功能较为简单,在实际使用当中,真正有价值的工具栏组件还需要与我们的流程业务结合起来,所以需要我们自己通过编写IToolbarContentProvider接口实现类来自定义我们自己的工具栏组件。

关于IToolbarContentProvider接口,前面已经有过介绍,在这个接口当中,比较重要的就是其中的getView方法,这个方法要求我们返回一个view的名称,在这个view当中就包含了需要放置到我们提供的通用工具栏的组件,比如一个toolbarButton等。为什么要求我们工具栏组件提供者要返回一个view呢?

因为我们在任务业务页面当中放置的工具栏组件一般实现的功能比较复杂,比如点击一个ToolBarButton弹出一个Dialog,同时在Dialog里又显示其它内容或通过一个Action与后台交互,对于这种比较复杂的组件,通过一个具体的View页面来定义相对要简单许多,所以我们这里IToolbarContentProvider接口提供了一个getView方法,要求返回一个view的具体名称,这样系统在渲染我们定义的任务处理页面时,会根据传递过来的taskId参数查找当前页面是否配置了通用工具栏,如果配置了,那就取出配置的相应组件提供者,通过调用这些组件提供者的getView方法返回的view名称,动态创建一个Dorado7的View对象,将这个View对象根包含的所有组件复制到用户所配置业务流程处理页面当中,在复制组件时将排除ToolBar组件,所以我们在定义自定义工具栏组件对应的view页面时一定要在view的根下添加一个toolbar组件,把需要放置通用工具栏上的组件放置在这个toolbar组件下,这样这个toolbar组件下的内容将会被复制到我们的通用工具栏当中。

我们以系统默认提供的CompleteTaskToolbarContentProvider为例,先来看看这个通用工具栏组件提供者的源码:

Page 69: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

CompleteTaskToolbarContentProvider源码

package com.bstek.bdf2.jbpm4.view.toolbar.impl.completetask;

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import com.bstek.bdf2.jbpm4.service.IBpmService;import com.bstek.bdf2.jbpm4.view.toolbar.IToolbarContentProvider;import com.bstek.dorado.annotation.Expose;/** * @author Jacky.gao * @since 2013-6-3 */@Component("bdf2.jbpm4.completeTaskToolbarContentProvider")public class CompleteTaskToolbarContentProvider implements IToolbarContentProvider { @Value("${bdf2.jbpm4.disabledCompleteTaskToolbarContentProvider}") private boolean disabled; @Autowired @Qualifier(IBpmService.BEAN_ID) private IBpmService bpmService; public String getView(){ return "bdf2.jbpm4.view.toolbar.impl.completetask.CompleteTaskToolbarContentProvider"; } public String key() { return "SimpleCompleteTask"; } public String desc() { return "直接完成任务"; } public boolean isDisabled() { return disabled; } @Expose public void completeTask(String taskId){ bpmService.completeTaskById(taskId); }}

可以看到这个用于完成任务的工具栏组件提供者采用的view页面名称为“bdf2.jbpm4.view.toolbar.impl.completetask.CompleteTaskToolbarContentProvider”,我们再来看看这个View,如下图所示:

Page 70: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

可以在这个页面view的根下定义了一个toolbar,在这个toolbar当中添加了可以出现在通用工具栏下的ToolBarButton,再来看看这个View页面的源码:

View源码

<?xml version="1.0" encoding="UTF-8"?><ViewConfig> <Arguments/> <Context/> <Model/> <View> <AjaxAction id="ajaxActionCompleteTask"> <ClientEvent name="onSuccess">dorado.MessageBox.alert(&quot;操作成功&quot;);&#xD;var win=window.parent;&#xD;if(win){&#xD; var taskListUrl=&quot;${configure.getString(&quot;bdf2.jbpm4.listTodoTaskUrl&quot;)}&quot;;&#xD; win.open(taskListUrl,&quot;_self&quot;);&#xD;}</ClientEvent> <Property name="parameter">${request.getParameter(&quot;taskId&quot;)}</Property> <Propertyname="service">bdf2.jbpm4.completeTaskToolbarContentProvider#completeTask</Property> <Property name="confirmMessage">真的要完成当前任务吗?</Property> </AjaxAction> <ToolBar> <ToolBarButton> <Property name="icon">url(skin>common/icons.gif) -180px -120px</Property> <Property name="caption">完成任务</Property> <Property name="action">ajaxActionCompleteTask</Property> </ToolBarButton> </ToolBar> </View></ViewConfig>

Page 71: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

需要特别注意的是,因为通用工具栏组件对应的View页面里的所有组件将会复制到我们的业务流程处理页面当中,所以在定义组件ID时要小心不要与业务流程处理页面当中组件出现重名,另外,对于通用工具栏组件对应的View页面,系统只会复制View节点下面的组件,而不会处理Arguments、Context及Model节点,所以在定义这个View页面时,我们需要将所有的信息都放在这个View节点下,如果要使用DataType,那么需要将这个DataType定义成全局的,而不能定义到Model节点变成私有的,否则在加载这个组件时将出现找不到DataType的错误。

对于我们的业务流程处理页面,如果页面布局比较复杂,或者希望将通用工具栏放置到指定位置(而不一定是页面顶部或底部),这个时候就可以在我们的业务页面当中添加一个工具栏,并将这个工具栏的ID设置为bdf2.jbpm4.genericTaskToolBarId属性的值,这样我们所定义的所有组件都会输出到用户指定的这个工具栏之上。

通过上述内容介绍,相应您已经对BDF2-JBPM4模块当中提供的这个可配置的通用工具栏有了比较深入的认识,其实在Dorado7环境下,在开发业务流程处理页面时,我们除了可以通过配置的方式为处理页面添加通用工具栏外,我们还可以在开发业务流程处理页面时利用Dorado7提供的页

,更为灵活的定制我们的业务流程处理页面。与我们的通用工具栏相比,Dorado7提供的页面模版功能更为灵活,但需要我们在开发业面模版功能务流程处理页面时采用这个模版,所以他们二者各有优劣,我们可以根据项目情况及我们个人喜好灵活选择。

5.5.任务到达提醒

在之前配置任务节点时,我们提到,在配置窗口当中有四块内容可供配置,其中最后两块是任务提醒与任务过期。我们先来看看任务提醒配置页,如下图所示。

提醒基本可分为两大类,既提醒一次及周期性提醒,因为都是可视化配置,比较简单,这里就不再赘述了。这里需要重点提出的是提醒时采用的消息模版及发送提供消息的方法,对于消息模版的选择,我们需要在ORM模块中提供的消息模版维护中定义,如下图所示:

Page 72: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

可以看到,在定义消息模版时,我们需要选择类型,比如上图中的 这个类型,一旦选择好类型后,在模版的名称及内容定义两个jBPM4消息模版地方我们就可以使用这个模版提供的相关变量,比如上图中的jBPM4消息模版的相关变量,实际上,消息模版类型及相关变量定义,都是通过一个接口的实现类定义的,这个接口名叫IMessageVariableRegister,下面是其源码 :

IMessageVariableRegister接口源码

package com.bstek.bdf2.core.business;import java.util.Collection;import com.bstek.bdf2.core.model.MessageVariable;/** * @author Jacky.gao * @since 2013-3-24 */public interface IMessageVariableRegister { String getTypeId(); String getTypeName(); Collection<MessageVariable> getMessageVariables();}

我们来看看 这个类型的接口实现类源码 :jBPM4消息模版

实现类示例

package com.bstek.bdf2.jbpm4.context;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.List;import java.util.Map;import org.jbpm.api.TaskService;import org.jbpm.api.task.Task;import com.bstek.bdf2.core.model.MessageVariable;import com.bstek.bdf2.jbpm4.service.IBpmService;/**

Page 73: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

* @author Jacky.gao * @since 2013-3-25 */public class DefaultJbpm4MessageVariableRegister extends AbstractJbpm4MessageVariableRegister{ private IBpmService bpmService; private String taskName="taskName"; private String principal="principal"; private String taskCreateDate="taskCreateDate"; public Collection<MessageVariable> getMessageVariables() { List<MessageVariable> result=new ArrayList<MessageVariable>(); MessageVariable var=new MessageVariable(); var.setName(IBpmService.BUSINESS_ID); var.setDesc("业务数据ID"); result.add(var); var=new MessageVariable(); var.setName(taskCreateDate); var.setDesc("任务创建时间"); result.add(var); var=new MessageVariable(); var.setName(principal); var.setDesc("任务处理人"); result.add(var); var=new MessageVariable(); var.setName(taskName); var.setDesc("任务名称"); result.add(var); var=new MessageVariable(); var.setName(IBpmService.PROCESS_INSTANCE_PROMOTER); var.setDesc("流程实例发起人"); result.add(var); return result; } public Map<String, String> fetchMessages(Task task,String assignee) { SimpleDateFormat sd=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); TaskService taskService=bpmService.getTaskService(); Map<String, String> map=new HashMap<String,String>(); map.put(principal, assignee); map.put(taskName, task.getName()); map.put(taskCreateDate, sd.format(task.getCreateTime())); map.put(IBpmService.BUSINESS_ID,(String)taskService.getVariable(task.getId(),IBpmService.BUSINESS_ID)); map.put(IBpmService.PROCESS_INSTANCE_PROMOTER,(String)taskService.getVariable(task.getId(),IBpmService.PROCESS_INSTANCE_PROMOTER)); return map; } public IBpmService getBpmService() { return bpmService; } public void setBpmService(IBpmService bpmService) {

Page 74: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

this.bpmService = bpmService; }}

上面这个实现类是扩展自AbstractJbpm4MessageVariableRegister类的,下面是这个类源码:

AbstractJbpm4MessageVariableRegister类源码

public abstract class AbstractJbpm4MessageVariableRegister implementsIJbpm4MessageVariableRegister { public String getTypeId() { return TYPE; } public String getTypeName() { return "jBPM4消息模版"; }}

到这里,您应该明白了如果添加自己的消息模版类型以及如何为jBPM4消息模版添加新的变量。

介绍完消息模版,接下来我们来看看发送提醒消息方法,点击配置按钮,可以看到如下图所示的消息发送器列表:

从上图当中可以看到,默认采用的是系统提供的email及站内消息两种消息发送器,我们可以选择一个,也可以两个都选择。如果我们有自己的消息发送方法,比如发送手机短信,那么我们只需要编写一个IMessageSender接口的实现类,并将其配置到spring当中即可,实际上系统默认提供的email及站内消息两种消息发送器就是通过实现该接口定义的,该接口源码如下:

Page 75: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IMessageSender接口源码

package com.bstek.bdf2.core.message;/** * @author Jacky.gao * @since 2013-3-28 */public interface IMessageSender { String getSenderId(); String getSenderName(); void send(MessagePacket message); boolean isDisabled();}

接口比较简单,这里就不再解释了,如果您实在不理解,可以参考系统默认提供的email及站内消息两种消息发送器实现类(EmailMessageSender及InternalMessageSender)。

值得注意的是,一旦我们配置了任务提醒功能,那么在完成任务时,我们一定要记得调用IBpmService接口中的cancelTaskReminder,不然任务虽然完成了,但任务提醒还会继续运行。

5.6.任务过期处理

任务过期处理与任务到达提醒类似,唯一不同的地方是任务过期后,除了可以发送消息外,还允许用户自定义其它类型的动作,关于任务过期之后发送消息,这里就不描述了,因为和任务提醒发消息是一样的,我们着重来看看过期后如何进行动作的自定义。如下图所示:

可以看到系统提供了默认自定义处理器只有一个,从其bean的id定义来看,这个bean对任务过期不作任何操作,所以我们必须要自己定义,自己定义的方法就是实现ITaskOverdueProcessor接口,该接口源码如下:

Page 76: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

ITaskOverdueProcessor接口源码

package com.bstek.bdf2.jbpm4.job;/** * @author Jacky.gao * @since 2013-4-26 */public interface ITaskOverdueProcessor { void process(String taskId);}

接口只有一个方法,非常简单,只有一个任务id的参数,实现好接口后,我们需要做的就是将实现类配置到spring当中即可(BDF2中几乎所有的接口实现类都是通过配置到spring当中,利用spring的类发现机制实现),这样在界面中自定义处理器中就可以看到我们自定义的处理器的bean的id。

默认情况下,系统对任务过期天数的计算是以自然日为基础的,如果您需要除去节假日或其它一些日期,那么你可以实现ICalculateOverdueTaskReminder接口,自己决定当前任务是否过期,这个接口的源码如下:

ICalculateOverdueTaskReminder接口源码

package com.bstek.bdf2.jbpm4.job.reminder;import java.util.Date;/** * @author Jacky.gao * @since 2013-4-26 */public interface ICalculateOverdueTaskReminder { /** * 根据业务环境计算当前日期下是否执行任务过期提供动作 * @param overdueDays 任务过期天数 * @param createDate 任务的创建日期 * @return 返回true表示执行任务过期提供动作,false表示不执行 */ boolean calculateOverdue(int overdueDays,Date createDate);}

接口的实现类编写完成之后,同样需要将其配置到Spring环境当中。

同样,一旦我们配置了任务过期消息提醒功能(非自定义处理器方式),那么在完成任务时,我们一定要记得调用IBpmService接口中的cancelTaskReminder,不然任务虽然完成了,但任务提醒还会继续运行。

5.7.流程与业务的结合

IBpmService接口是BDF2-JBPM4模块当中提供的操作jBPM4流程引擎的核心API,这个接口的实现类配置在Spring当中,我们可以通过IBpmService接口中提供的静态常量去访问这个IBpmService接口的实现类,示例代码如下:

IBpmService实例获取方法

IBpmService bpmService=ContextHolder.getBean(IBpmService.BEAN_ID);

Page 77: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IBpmService接口的实现类配置在Spring中的beanid为"bdf2.jbpm4.bpmService",所以对于也需要配置在Spring中的类来说,可以通过注入的方式完成对IBpmService接口的实现类实例的获取。

在我们的业务代码当中,获取到IBpmService接口的实现类实例之后,就可以在需要的时候操作我们的业务流程,比如开始流程、完成任务、节点跳转等。在BDF2-JBPM4模块当中,如果我们需要在与流程绑定的业务页面当中查询当前流程状态图,所以可以利用BDF2-JBPM4模块当中提供的用于显示流程图状态,名为Jbpm4ProcessImage的Dorado7组件来完成,要在我们的view的工具栏当中看到这个组件,我们需要更新Dorado7 IDE规则,因为BDF2应用环境较为复杂,所以在更新Dorado7 IDE规则时要采用在线更新方式(具体可以去Dorado7IDE官方教程了解),更新完成之后,打开一个view文件,就可以在左边工具栏中的BDF2组下看到名为Jbpm4ProcessImage的Dorado7组件,如下图所示:

这个组件的用法非常简单,需要我们设置的就是其中的taskId属性,它用于接受当前的任务ID。一般来说,需要用到这个组件的地方都是具体的任务处理页面,而这些任务处理页面都是通过待办任务列表进入的,在待办任务列表当中,点击某个任务的处理页面的链接时,会在链接后面加上taskId参数,所以对于Jbpm4ProcessImage组件的taskId属性,我们一般都是通过Dorado7EL表达式从request中到名为taskId的参数,比如${request.getParameter("task")}。需要意思的是这个Jbpm4ProcessImage组件实际上会创建一个iFrame,所以我们需要将这个组件放在一个容器当中,比如Dialog中,这样在需要的看的时候将这个dialog显示出来即可。

6.BDF2-JASPERREPORTS

BDF2-JASPERREPORTS模块主要是为Jasperreport报表提供一个基于WEB的运行环境,利用该模块,可以实现报表在WEB环境中导出、在线显示、在线打印等功能,同时我们还提供了一个Jasperreport报表的在线管理页面,这样,即使项目处理上线期,我们也可以在线下通过IReport之类的Jasperreport报表开发工具,将报表设计好,在线上传到我们的项目当中,并可以为其配置在采用的数据源(JDBC还是JAVABEAN或者MAP),同时可以将Jasperreport报表在线导出或实现预览打印等相关功能。

同样,传统项目如果需要BDF2-JASPERREPORTS模块,那么我们需要到nexus.bsdn.org上下载或到我们的在线项目向导中选择并下载,如果是Maven项目,要添加BDF2-JASPERREPORTS模块支持,我们只需要在项目的pom.xml当中添加如下dependency:

Page 78: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

BDF2-JASPERREPORTS模块所需要的依赖

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-jasperreports</artifactId> <version>2.0.0</version></dependency>

添加完BDF2-JASPERREPORTS模块后,就可以运行项目,BDF2-JASPERREPORTS模块当中包含一个用于在线上传、管理报表的页面,所以,如果您的项目当中包含BDF2-CORE模块,您可以在登录之后,访问generate.system.menu.action这个用于初始化菜单的URL,创建好的导航菜单如下图所示:

BDF2-JASPERREPORTS模块当中允许覆盖的参数如下表所示:

属性名 类型 默认值 描述

bdf2.jasperreports.dataSourceName

String 空 BDF2-JASPERREPORTS模块采用的数据源,为空表示采用默认数据源

bdf2.jasperreports.disabledCsvExporter

boolean false 是否禁用系统默认提供的导出CVS格式报表的处理器,默认为false,采用可以将报表导出成csv格式

bdf2.jasperreports.disabledDocxExporter

boolean false 是否禁用系统默认提供的导出Docx格式报表的处理器,默认为false,采用可以将报表导出成Docx格式

bdf2.jasperreports.disabledHtmlExporter

boolean false 是否禁用系统默认提供的导出Html格式报表的处理器,默认为false,采用可以将报表导出成csv格式

Page 79: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.jasperreports.disabledJrpxmlExporter

boolean false 是否禁用系统默认提供的导出Jrpxml格式报表的处理器,默认为false,采用可以将报表导出成Jrpxm格式,这里注意注意的是,对于lJrpxml格式,我们提供了一个swf,它可以解析Jrpxml格式,并将其通过flash格式在网页当中直接显示出来,也就是说,一旦我们选择Jrpxml格式报表,系统默认会采用一个swf格式文件显示报表内容,同时利用这个swf还可以实现报表的在线打印。

bdf2.jasperreports.disabledPdfExporter

boolean false 是否禁用系统默认提供的导出Pdf格式报表的处理器,默认为false,采用可以将报表导出成 格式Pdf

bdf2.jasperreports.disabledPptxExporter

boolean false 是否禁用系统默认提供的导出Pptx格式报表的处理器,默认为false,采用可以将报表导出成 格Pptx式

bdf2.jasperreports.disabledRtfExporter

boolean false 是否禁用系统默认提供的导出Rtf格式报表的处理器,默认为false,采用可以将报表导出成Rtf格式

bdf2.jasperreports.disabledXlsExporter

boolean false 是否禁用系统默认提供的导出Xls格式报表的处理器,默认为false,采用可以将报表导出成Xls格式

6.1.报表定义与配置

开发一个Jasperreport报表,我们需要下载IReport之类Jasperreport报表开发工具,其下载地址如下:http://community.jaspersoft.com/project/ireport-designer,关于如何利用IReport来开发一个报表,这个已超出BDF2范畴,关于IReport的使用,网上教程很好,大家可以去搜索一下。

报表设计完成之后,接下来需要将其编译成一个jasper文件,这个利用IReport就可以完成,有了jasper文件后,我们就可以将其在线上传到BDF2中提供的报表维护当中,如下图所示:

在上传报表文件时,如果我们选择非jasper报表文件,系统会抛出一个异常,同时上传好报表之后,我们还可以选择当前报表采用的数据源,目前在BDF2当中,允许用户为报表提供三种类型的数据源:第一种就是JDBC,这种方式也是最为常用的一种,选择这种之后,我们就可以在下面的数据源选择当中选择当前报表要采用的数据源名称(来自BDF2所支持的多数据源机制);第二种和第三种分别是Map与JavaBean,一旦我们选择

Page 80: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

这两种类型的数据源,那么在下面的数据来源选择的列表当中,我们只能选择所有实现了IReportDataProvider接口且配置到Spring当中的bean来作为当前报表的数据源,IReportDataProvider接口代码如下:

IReportDataProvider接口源码

package com.bstek.bdf2.jasperreports.service;import java.util.Collection;/** * @author Jacky.gao * @since 2013-5-12 */public interface IReportDataProvider { Collection<?> getData();}

可以看到接口非常简单,只有一个方法,这个方法只要求我们返回一个对象的集合即可,目前这个对象可以同Map或JavaBean。如果我们在配置时采用的数据源类型为Map,那要求对应的getData方法返回的必须是一个Map的集合,如果选择的是JavaBean则必须返回一个包含报表中使用的field的JavaBean的集合,比较容易理解。

报表定义完成之后,我们可以点击工具栏上的“预览报表”按钮,在弹出的窗口当中,可以选择要预览的报表类型,可以看到目前支持下面几种类型的报表:

对于报表的预览,你可以选择jrpxml或html两种类型格式。HTML格式比较简单,就是将报表内容转换成一个HTML网页,可以在线显示;而对于jrpxml,它会将报表内容生成一个XML格式,这里需要特别指出的是,我们提供了一个可以解析这个XML的swf,解析好的内容可以保证与报表完成一致,并可以实现在网页当中直接显示。同时在这个SWF当中,我们还提供了一个打印功能,可以将当前显示的报表直接输出到打印机打印,如下图所示:

Page 81: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

利用这个SWF打印的时候,它不会打印图片,所以如果您的报表当中含有图片,利用这个SWF打印功能是不会被打印出来的,但就因为其不会打印图片,所以我们可以利用这个功能实现套打,具体大家可以想象一下。

实际上,对于上面的报表的右上角,是有一张图片的,但因为我们没有定义图片,所以显示的报表右上角是空的,下面这张图向我们展示了定义这个报表时图片的定义方式:

可以看到,在设计报表时,对于用到的图片,我们采用一个parameter来代替,这个parameter的名字为IMGJERRY,所以在将报表配置到BDF2当中时,我们需要将要采用的目标图片作上传到系统当中,并且将其名字命名为IMGJERRY,这样我们的报表生成时就可以看到图片啦,如下图所示:

Page 82: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

再次采用jrpxml格式来预览我们的报表,可以看到如下所示效果:

实际上,不仅仅是图片可以这样做,对于报表设计当中常用的子报表也可以采用类似的方式实现,具体这里就不再赘述了。

6.2.在业务页面中使用报表

前面,我们了解了如何将一个编译好的报表文件上传到BDF2系统当中,介绍如何配置报表运行时所需要的参数及相关资源文件,同时演示了如何对上传上来的报表进行预览操作等,这里我们来看看如何在我们的业务页面当中使用一个已经编译好的或一个已上传到BDF2系统当中的报表。

在BDF2-JASPERREPORTS模块当中,为了方便大家将报表应用到我们的业务界面当中,我们提供了一个名为JasperreportsExporter标准的Dorado7组件,它可以实现将定义的报表(已经编译好的或一个已上传到BDF2系统当中的报表)导出成我们需要的各种格式。要在view当中看到这个组件,当然就是更新规则,采用在线方式更新规则,更新完成之后,打开一个view文件,可以在右侧工具栏中看到如下图所示的JasperreportsExporter组件图标:

Page 83: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

JasperreportsExporter组件可以设置的属性如下表所示:

属性名 类型 默认值 描述

fileSource String uploadedFile 要使用的报表来源,可选值有两个,分别是uploadedFile和file。uploadedFile表示文件来自于我们通过 中定义的报表定义与配置报表;file则表示一个位于服务端某目录下的一个jasper文件。

reportFile String 空 要使用的报表文件,这个属性的值为fileSource属性决定。当fileSource属性值为uploadedFile时,这个属性值必须是一个通过报表定义与配置中定义的报表的ID,而不能是其它;如是fileSource属性值为file时,这里定义的是一个位于服务端某目录下的一个jasper文件地址,比如D:\report\testReport.jasper

dataSourceType String Jdbc 有三个值可选,分别是:Jdbc、Map和JavaBean,这个属性的作用是定义当前报表采用的数据源类型。当fileSource属性值为uploadedFile时将忽略这个属性值。

Page 84: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

dataSourceProvider String 空 根据dataSourceType属性的值,来决定采用哪个数据源,如果dataSourceType值为Jdbc,那么这个属性需要选择一个数据源名称;如果dataSourceType值为Map或JavaBean这里需要定义一个实现了IReportDataProvider接口且配置到Spring当中的bean的id。详情见报表定义与配置中相关描述。当fileSource属性值为uploadedFile时将忽略这个属性值。

Page 85: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

parameter dorado.util.Map 空 用于设置报表设计中需要使用的参数信息。某些情况下,我们的报表当中用到的参数可能需要从当前业务页面当中获取,通过这个参数就可以实现该功能,在下面的测试JS代码当中,我们定义的报表需要外部传入一个名为jobId的参数以决定报表需要展示的业务数据的数量,那么我们的代码就可以通过下面的方式动态填充一个jobId参数:

报表组件

通过JS填

充参数示

varreport=view.id("jasperreportsExporter1");var p=newdorado.util.Map();p.put("jobId","bbb");report.set("parameter",p);report.execute();

当然,除了像上述那样进行JS动态配置外,还可以直接通过IDE为该组件的parameter参数赋值,具体方法与Dorado7其它组件的parameter赋值 相同,赋值方法完成后,同样可以实现上述JS功能。

在IDE中为组件定义参数时注意事项当采用报表组件来展现我们定义在报表管理界面当中上传的报表,且需要在IDE当中重新定义对应的报表所使用的参数 ,那么需要将该时报表在报表管理界面当中定义的对应参数删除(如果有定义的话),否则我们在IDE当中定义的报表参数将不起作用。

Page 86: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1. 2. 3.

报表定义完成之后,要将报表导出成我们需要的格式,还 。需要执行JasperreportsExporter组件execute方法

6.3.配置JasperReports字体

在使用Jasperreports时,经常会遇到无法在PDF格式报表当中显示中文,这时需要我们修改报表中文字的下面三个属性实现:

Font : 宋体Pdf Font Name : STSong-LightPdf Encoding :Identity-H (Unicode with horizontal writing)

还有一种情况,可能我们在本地测试的情况下报表显示正常,一旦部署到BDF2当中可能出现类似下面的字体找不到的异常:

这种情况对于将应用部署到Linux操作系统环境下的应用特别常见,一旦您遇到这种问题,那么我们需要做的就是利用iReport将缺少的字体打包并放置我们应用的classpath环境下即可。iReport字体打包操作如下:

首先打开IReport的工具/选项/Fonts,点击Install Font:

点击浏览按钮,选择字体库的ttf文件。

字体找不到异常net.sf.jasperreports.engine.util.JRFontNotFoundException: Font 'Arial' is not available to the JVM.

Page 87: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

点击下一步,如果当前字体库是有4个字体库组成分别是默认字体,粗体,斜体,粗斜体,则在该步还需选择粗体,斜体和粗斜体的字体库。

点击完成,实现字体库的导入操作,这样在IReport设计器中就可以使用这些字体了,如果需要在web工程使用该字体库,则可以选择已配置的字体库(可以是多个),点击右侧的Export asextension按钮,将这些字体库导出成jar,放到classpath下使用,这样web应用就可以使用这些字体库,解决了中文问题。

Page 88: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

7.BDF2-UPLOADER

在BDF2-UPLOADER模块当中,为我们提供了一个将文件从浏览器上传到服务端的工具,该工具以标准的Dorado7组件形式存在,利用标准的HTML及JAVASCRIPT实现文件的选择与上传(未采用任何其它第三方控件,比如flash,activex等),兼容所有主流浏览器(IE、FF、Chrome等),默认提供了将文件保存到服务端某文件夹或服务端数据表中两种类型的文件存储机制。

要使用BDF2-UPLOADER模块,我们可以到nexus.bsdn.org上下载最新的BDF2-UPLOADER模块的jar,或者可以到我们提供的在线创建项目中选择BDF2-UPLOADER模块并下载即可;同样,如果您采用的是Maven来管理项目,那么只需要将BDF2-UPLOADER模块的依赖信息加向导

到我们的pom.xml当中即可:

BDF2-UPLOADER模块的依赖

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-uploader</artifactId> <version>2.0.0</version></dependency>

添加好BDF2-UPLOADER模块之后,就可以启动我们的项目。

注意实际上,BDF2-JASPERREPORTS模块就依赖BDF2-UPLOADER模块,所以在之前我们介绍BDF2-JASPERREPORTS模块中,在上传jasper格式报表及相关资源,用的就是BDF2-UPLOADER模块提供的上传功能。

Page 89: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

我们知道BDF2-JASPERREPORTS模块提供了一个标准的Dorado7组件来让我们实现上传功能,所以启动工程后,要在我们view的工具栏当中看到这个上传组件,需要更新Dorado7规则(在线更新方式),更新完成之后,打开一个view,可以在工具栏当中看到如下图所示组件图标。

这个名为“RichUploader”的组件就是BDF2-UPLOADER模块提供的上传组件,将这个组件添加到view当前,运行这个view可以看到这个组件就是一个标准的HTML的Button,如下图所示:

因为RichUploader上传组件就是一个标准的HTML的Button,所以在Dorado7View当中使用空上组件时,我们就可以当其是一个Dorado7的button,可以像操作Dorado7的button一样,将其放在一个容器当中,放在一个表单当中,或者放在一个FormElement的container当中,对于RichUploader我们需要注意下面这几个属性:

Page 90: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

属性名 类型 默认值 描述

allowFileTypes String 空 允许用户上传哪些类型的文件,默认为空,表示不限制类型。

allowMaxFileSize int 空 允许用户上传文件的最大尺寸,单位是byte,默认为空,表示不限制尺寸。

caption String 空 上传按钮的标准,如上图当中的“ ”。上传测试

processor String 空 文件上传到服务端时采用哪个文件上传处理器来处理上传的文件,默认为空,表示采用ID为“Database”的文件上传处理器,这个处理器的作用就是将文件上传到服务器下某个目录。

autoSubmit boolean true 选择好文件后是否自动上传,默认为true,表示选择好文件组件会自动触发上传动作,如果设置为false,那么我们需要手工调用该组件的submit方法来执行上传动作。

除了上述四个属性外,RichUploader还提供了四个事件,如下表所示:

事件名 描述

onSelect 在选择好一个文件,但还没开始将文件上传到服务端时触发的事件,在这个事件当中,我们可以通过其中提供的arg参数的filename属性拿到选择好的文件的文件名称。

onSuccess 文件上传成功之后触发的事件,从这个事件当中的arg参数中,我们可以获取到上传成功之后文件的文件名(arg.filename),以及文件存储后产生的id(arg.id),一般来说,对于我们的业务系统,我们需要保存这个id ,通过这个id就可以找到上传的文件,filename一般仅用于显示。

onFail 文件上传失败之后触发的事件,从这个事件当中的arg参数中,我们可以获取到上传失败的错误消息(arg.errorMessage),比如上传文件类型不对,文件尺寸太大,或其它的错误消息等。

我们知道RichUploader组件有个名为processor的属性,它可以决定文件上传到服务端时采用哪个文件上传处理器来处理上传的文件,默认情况下,BDF2-UPLOADER模块当中提供了两个文件上传处理器,分别是LocalDirectory及Database。LocalDirectory就是将上传的文件存储到服务端某个目录下;而Database则是将上传文件存储到服务端数据库的特定表当中。如果在使用过程当中,我们有自己的文件存储方式(比如存储到DMS中),那么可以自定义自己的文件上传处理器,要自定义文件上传处理器,我们需要实现IFileProcessor接口,这个接口源码如下:

Page 91: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IFileProcessor接口源码

package com.bstek.bdf2.uploader.processor;import java.io.InputStream;import com.bstek.bdf2.uploader.model.UploadDefinition;/** * @author Jacky.gao * @since 2013-5-1 */public interface IFileProcessor { /** * 保存上传的文件 * @param uploadDefinition 可以从这个对象中取到上传文件的ID、名称、大小等信息 * @param inputStream 上传文件的流对象 */ void saveFile(UploadDefinition uploadDefinition,InputStream inputStream); /** * 根据给出的文件上传对象,返回对应的文件流 * @param uploadDefinition 文件上传对象 * @return 要取回的文件流 */ InputStream loadFile(UploadDefinition uploadDefinition); /** * 根据文件上传对象,删除对应的文件 * @param uploadDefinition 文件上传对象 */ void deleteFile(UploadDefinition uploadDefinition); /** * @return 返回当前处理器的ID */ String key(); /** * @return 是否禁用当前处理器 */ boolean isDisabled();}

接口比较简单,这里就不于解释了,处理器实现类编写完成之后,需要配置到Spring当中,这样我们就可以在RichUploader中使用这个处理器了,方法就是给RichUploader的processor属性值设置为我们自定义的处理器key()方法返回的值即可。

例子是最好的示范,我们来看看系统默认提供的Database这个自定义处理器的写法:

Page 92: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

Database文件上传处理器

package com.bstek.bdf2.uploader.processor.impl;import java.io.InputStream;import com.bstek.bdf2.uploader.model.UploadDefinition;import com.bstek.bdf2.uploader.processor.IFileProcessor;import com.bstek.bdf2.uploader.service.ILobStoreService;/** * @author Jacky.gao * @since 2013-5-20 */public class DatabaseFileProcessor implements IFileProcessor { private boolean disabled; private ILobStoreService lobStoreService; public void saveFile(UploadDefinition uploadDefinition,InputStream inputStream) { try { lobStoreService.storeBinaryStream(inputStream, inputStream.available(),uploadDefinition.getId()); } catch (Exception e) { throw new RuntimeException(e); } } public InputStream loadFile(UploadDefinition uploadDefinition) { try { return lobStoreService.getBinaryStream(uploadDefinition.getId()); } catch (Exception e) { throw new RuntimeException(e); } } public void deleteFile(UploadDefinition uploadDefinition) { try { lobStoreService.deleteBinaryStream(uploadDefinition.getId()); } catch (Exception e) { throw new RuntimeException(e); } } public String key() { return "Database"; } public boolean isDisabled() { return disabled; } ......}

对于已经上传好的文件,我们可以通过下页两个URL对通过RichUploader上传的文件进行在线显示(比如图片、XML文件等)或直接下载。我们首先来看看下载文件的URL:

<contextPath>/dorado/bdf2/uploader/process.download?id=<通过RichUploader上传文件成功后拿到的文件的id>

如果需要在线显示一个通过RichUploader上传的文件,其访问的URL格式如下:

Page 93: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

<contextPath>/dorado/bdf2/uploader/process.display?id=<通过RichUploader上传文件成功后拿到的文件的id>

对于BDF2-UPLOADER模块,我们还提供了下面这个属性允许用户覆盖:

属性名 类型 默认值 描述

bdf2.upload.dataSourceName

String 空 BDF2-UPLOADER模块要采用的数据源,为空表示采用默认数据源。

bdf2.uploader.allowMaxFileSize

int 0 允许上传文件的最大尺寸,默认为0表示不限制尺寸。该属性可以在RichUploader组件使用时重新定义,一旦定义会覆盖这个属性定义的值,否则就采用这里定义的值。

bdf2.uploader.allowFileTypes String 空 允许上传文件的类型,多个类型用逗号分隔,默认为空表示不限制类型。该属性可以在RichUploader组件使用时重新定义,一旦定义会覆盖这个属性定义的值,否则就采用这里定义的值。

bdf2.uploader.defaultProcessor

String Database 默认要采用的文件上传处理器,默认为Database,表示上传的文件存放于数据库特定表中。该属性可以在RichUploader组件使用时重新定义,一旦定义会覆盖这个属性定义的值,否则就采用这里定义的值。

bdf2.uploader.localDirectoryFileProcessorDirectory

String uploadfiles 如果采用LocalDirectory这个文件上传处理器,那么该属性就是定义文件上传到服务端后该存放于哪个目录下,默认为uploadfiles,表示将存放到应用所在目录的WEB-INF/uploadfiles目录下,如果我们需要指定其它目录,那么需要定义一个真实存在的目录,比如定义值为D:\myuploadfiles,那么就表示上传的文件放存放于D盘下的myuploadfiles目录中。

bdf2.uploader.localDirectoryFileProcessorStorageMethod

String month 文件存储在目录中时,子目录的定义方式,这里支持三个值:year、month、day,分别表示按年、按年\月及按年\月\日这三种格式来划分目录存放上传文件。默认为month,表示按年\月格式划分目录存放上传文件。

Page 94: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.uploader.localDirectoryFileProcessorFileNameStorageMethod

String uuid 上传后文件命名方式,这里支持三种格式:hybrid、uuid及realName。hybrid表示文件在命名是将uuid+文件名这种混合方式命名;uuid则将直接采用uuid命名文件;而realName只是采用文件真实名称。

bdf2.uploader.disableDatabaseFileProcessor

boolean false 是否禁用Database方式文件处理器,一旦禁用,运行时将不能使用。

bdf2.uploader.disableLocalDirectoryFileProcessor

boolean false 是否禁用 方式文LocalDirectory件处理器,一旦禁用,运行时将不能使用。

在BDF2-UPLOADER当中为了方便我们操作文件,还提供了两个service,分别是IFileService及ILobStoreService。IFileService用于实现对上传文件的各种,比如根据上文件的文件ID查找对应的上传文件对象、根据上传文件ID查询上传的文件流等,其源码如下:

IFileService源码

package com.bstek.bdf2.uploader.service;import java.io.InputStream;import com.bstek.bdf2.uploader.model.UploadDefinition;/** * @author Jacky.gao * @since 2013-5-12 */public interface IFileService { public static final String BEAN_ID="bdf2.uploader.fileService"; UploadDefinition getUploadDefinition(String id); InputStream getFile(UploadDefinition definition); InputStream getFile(String id); void deleteUploadDefinition(String id);}

对于这个service的实例获取,我们可以通过ContextHolder.getBean(IFileService.BEAN_ID)来实现,或者直接在Spring当中注入ID为bdf2.uploader.fileService的bean。

ILobStoreService则为我们提供了一个将文件存储到BDF2-UPLOADER中特定的数据库表的service,其源码如下:

ILobStoreService接口源码

package com.bstek.bdf2.uploader.service;import java.io.InputStream;import java.io.Reader;import java.sql.SQLException;/** * 大对象仓库服务接口,对外提供二进制数据,超长文本等的储存、更新、取出等操作。 * <p> * 这里的操作分为两组:<br/> * <ol> *<li>byte及binaryStream相关的为第一组,代表了对二进制数据,如图片、声音、视频等的操作,在数据库中通常以BLOB或IMAGE类型表示</li>

Page 95: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

*<li>string、asciiString及characterStream相关的为第二组,代表了对大文本对象,如文本文件、新闻公告等的操作, * 在数据库中通常以CLOB或TEXT类型表示</li> * </ol> * 需要注意的是,不管为哪种类型的大对象,仓库中均不允许其内容为空,若业务数据允许引用一个空对象,请将业务表中的允许为空的记录的相关字段设置为NULL。<br/> * 对于不再需要的大对象,请及时使用delete*函数清理,以免造成空间的浪费。<br/> * 此接口中的所有函数均不能保证一定能完成指定的操作,请在使用时处理抛出的异常<br/> * </p> * @author [email protected] * @since 2.0 */public interface ILobStoreService { /** * 保存一个byte数组,并返回它在仓库中的主键。 * * @param content * 需要保存的内容,不能为空 * @return 保存后的内容在仓库中的主键,稍后可通过{@link #getBytes(String)}取得仓库中的内容 * @throws SQLException * 将byte数组保存到数据库时可能抛出此异常 */ String storeBytes(byte[] content) throws SQLException; /** * 保存一个byte数组,并返回它在仓库中的主键。 * @param content 需要保存的内容,不能为空 * @param id 需要保存的内容的ID * @throws SQLException * 将byte数组保存到数据库时可能抛出此异常 */ void storeBytes(byte[] content,String id) throws SQLException; /** * 根据内容在仓库中的主键,删除对应的记录。 * * @param id * 需要删除的内容在仓库中的主键 * @throws SQLException * 在从数据库中删除数据时可能抛出此异常;此外,如果删除不成功,如与主键对应的记录不存在,也抛出此异常。 */ void deleteBytes(String id) throws SQLException; /** * 更新主键所指的内容。 * <p> * 保证此内容的名称<font color="red">不</font>变。 * </p> * * @param id * 需要被更新的内容的主键 * @param content * 新的值 * @throws SQLException * 在更新数据库的过程中可能抛出此异常;此外,如果更新不成功,如与主键对应的记录不存在,也抛出此异常。 */ void updateBytes(String id, byte[] content) throws SQLException; /**

Page 96: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

* 根据主键查询内容,若未找到,返回<code>null</code>。 * * @param id * 内容的主键,通常由业务记录中的某一字段保有 * @return 与主键相对应的内容 * @throws SQLException * 在查询数据库的过程中可能抛出此异常。 */ byte[] getBytes(String id) throws SQLException; /** * 保存一个二进制数据流中的数据,并返回其在仓库中的主键。 * * @param inputStream * 需要保存的内容,不能为空 * @param contentLength * 内容的长度。如若保存一个{@link java.io.File}对象,则通常此长度由 * {@link java.io.File#length}取得 * @return 保存后的内容在仓库中的主键,稍后可通过{@link #getBinaryStream(String)}取得仓库中的内容 * @throws SQLException */ String storeBinaryStream(InputStream inputStream, int contentLength) throws SQLException; /** * 保存一个二进制数据流中的数据,并返回其在仓库中的主键。 * * @param inputStream * 需要保存的内容,不能为空 * @param contentLength * 内容的长度。如若保存一个{@link java.io.File}对象,则通常此长度由 * {@link java.io.File#length}取得 * @param id * 需要保存的内容ID * @throws SQLException */ void storeBinaryStream(InputStream inputStream, int contentLength,String id) throws SQLException; /** * 根据内容在仓库中的主键,删除对应的记录。 * * @param id * 需要被删除的内容的主键 * @throws SQLException * 在删除数据库中的记录时可能抛出此异常;此外,如果删除不成功,如与主键对应的记录不存在,也抛出此异常。 */ void deleteBinaryStream(String id) throws SQLException; /** * 更新指定主键代表的内容。 * <p> * 保证此内容的名称<font color="red">不</font>变。 * </p> * * @param id * 需要被更新的内容的主键 * @param inputStream * 用于更新内容的二进制输入流 * @param contentLength * 二进制输入流的长度 * @throws SQLException *

Page 97: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

在更新数据库中的记录时可能抛出此异常;此外,如果更新不成功,如与主键对应的记录不存在,也抛出此异常。; */ void updateBinaryStream(String id, InputStream inputStream, int contentLength) throws SQLException; /** * 根据主键取得内容的二进制输入流,若未找到,返回<code>null</code>。 * * @param id * 需要查询的内容的主键 * @return 代表内容的二进制输入流 * @throws SQLException * 在查询数据库的过程中可能抛出此异常 */ InputStream getBinaryStream(String id) throws SQLException;

/** * 保存一个超长的字符串,并返回其在仓库中的主键。 * * @param content * 需要保存的内容。 * @return 保存后的内容在仓库中的主键,稍后可通过{@link #getString(String)}取得仓库中的内容 * @throws SQLException * 在将内容保存到数据库的过程中可能抛出此异常。 */ String storeString(String content) throws SQLException; /** * 保存一个超长的字符串,并返回其在仓库中的主键。 * * @param content 需要保存的内容。 * @param id 需要保存的内容ID。 * @throws SQLException * 在将内容保存到数据库的过程中可能抛出此异常。 */ void storeString(String content,String id) throws SQLException; /** * 根据内容在仓库中的主键删除对应的记录。 * * @param id * 需要删除的内容在仓库中的主键 * @throws SQLException * 在从数据库中删除记录时可能抛出此异常;此外,如果删除不成功,如与主键对应的记录不存在,也抛出此异常。; */ void deleteString(String id) throws SQLException; /** * 更新指定的主键代表的内容。 * * @param id * 需要更新的内容的主键 * @param content * 用于更新的值 * @throws SQLException * 在更新数据库中的记录时可能抛出此异常;此外,如果更新不成功,如与主键对应的记录不存在,也抛出此异常。 */ void updateString(String id, String content) throws SQLException; /** * 根据主键查询其在仓库中的内容,若未找到,返回<code>null</code>。

Page 98: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

* * @param id * 需要查询的内容的主键 * @return 代表查询内容的字符串 * @throws SQLException * 在查询数据库的过程中可能抛出此异常 */ String getString(String id) throws SQLException; /** * 保存一个字节流,并返回其在仓库中的主键。 * * @param asciiSrteam * 需要保存到仓库中的字节流。 * @param contentLength * 字节流的长度。 * @return 保存后的内容在仓库中的主键,稍后可通过{@link #getAsciiStream(String)}取得仓库中的内容 * @throws SQLException * 在将内容保存到数据库的过程中可能抛出此异常。 */ String storeAsciiStream(InputStream asciiSrteam, int contentLength) throws SQLException; /** * 保存一个字节流,并返回其在仓库中的主键。 * * @param asciiSrteam * 需要保存到仓库中的字节流。 * @param contentLength * 字节流的长度。 * @param id 保存的ID * @throws SQLException * 在将内容保存到数据库的过程中可能抛出此异常。 */ void storeAsciiStream(InputStream asciiSrteam, int contentLength,String id) throws SQLException; /** * 根据内容在仓库中的主键删除对应的记录。 * * @param id * 需要删除的内容在仓库中的主键 * @throws SQLException * 在从数据库中删除记录时可能抛出此异常;此外,如果删除不成功,如与主键对应的记录不存在,也抛出此异常。; */ void deleteAsciiStream(String id) throws SQLException; /** * 更新指定的主键代表的内容。 * * @param id * 需要更新的内容的主键 * @param asciiStream * 用于更新的字节流 * @param contentLength * 字节流的长度 * @throws SQLException * 在更新数据库中的记录时可能抛出此异常;此外,如果更新不成功,如与主键对应的记录不存在,也抛出此异常。; */ void updateAsciiStream(String id, InputStream asciiStream,

Page 99: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

int contentLength) throws SQLException; /** * 根据主键查询其在仓库中的内容,若未找到,返回<code>null</code>。 * * @param id * 需要查询的内容的主键 * @return 代表内容的字节流 * @throws SQLException * 在查询数据库的过程中可能抛出此异常 */ InputStream getAsciiStream(String id) throws SQLException; /** * 保存一个字符流,并返回其在仓库中的主键。 * * @param reader * 需要保存的字符流 * @param contentLength * 字符流的长度 * @return 保存后的内容在仓库中的主键,稍后可通过{@link #getCharacterStream(String)}取得仓库中的内容 * @throws SQLException * 在将内容保存到数据库的过程中可能抛出此异常。 */ String storeCharacterStream(Reader reader, int contentLength) throws SQLException; /** * 保存一个字符流,并返回其在仓库中的主键。 * * @param reader * 需要保存的字符流 * @param contentLength * 字符流的长度 * @param reader * 需要保存的字符流ID * @throws SQLException * 在将内容保存到数据库的过程中可能抛出此异常。 */ void storeCharacterStream(Reader reader, int contentLength,String id) throws SQLException; /** * 根据内容在仓库中的主键删除对应的记录。 * * @param id * 需要删除的内容在仓库中的主键 * @throws SQLException * 在从数据库中删除记录时可能抛出此异常;此外,如果删除不成功,如与主键对应的记录不存在,也抛出此异常。; */ void deleteCharacterStream(String id) throws SQLException; /** * 更新指定的主键代表的内容。 * * @param id * 需要更新的内容的主键 * @param reader * 用于更新的字符流 * @param contentLength * 字符流的长度 * @throws SQLException

Page 100: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

* 在更新数据库中的记录时可能抛出此异常;此外,如果更新不成功,如与主键对应的记录不存在,也抛出此异常。; */ void updateCharacterStream(String id, Reader reader, int contentLength) throws SQLException; /** * 根据主键查询其在仓库中的内容,若未找到,返回<code>null</code>。 * * @param id * 需要查询的内容的主键 * @return 代表内容的字符流 * @throws SQLException * 在查询数据库的过程中可能抛出此异常 */ Reader getCharacterStream(String id) throws SQLException; /** * 服务ID,代表了其在spring中的beanName。

Page 101: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

*/ public static final String BEAN_ID = "bdf2.lobStoreService";}

同样对于这个service的实例获取,我们可以通过ContextHolder.getBean(ILobStoreService.BEAN_ID)来实现,或者直接在Spring当中注入ID为bdf2.lobStoreService的bean。

8.BDF2-WEBSERVICE

BDF2-WEBSERVICE模块是利用Spring-WS实现,Spring-WS是一款轻量级的Webservice框架实现,具有简单、灵活、与Spring框架无缝集成等特点。基于Spring-WS,BDF2-WEBSERVICE模块中提供了一个快速的构建Webservice服务的环境,对于构建的Webservice服务,可以灵活决定是否添加基于WS-Security的加密认证。

要使用BDF2-WEBSERVICE模块,可以到nexus.bsdn.org上下载BDF2-WEBSERVICE模块相关jar,或到我们提供的在线项目创建向导中选择BDF2-WEBSERVICE模块并下载。同样如果您采用的是Maven来管理您的项目,那么只需要在您项目的pom.xml文件当中添加下面的依赖信息即可:

BDF2-WEBSERVICE模块的依赖配置

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-webservice</artifactId> <version>2.0.0</version></dependency>

添加好BDF2-WEBSERVICE模块后,就可以启动项目对BDF2-WEBSERVICE模块进行测试了,这里需要特别指出的是,BDF2-WEBSERVICE模块虽然是基于Spring-WS项目构建,但却不用在web.xml当中配置标准Spring-WS使用时需要配置的MessageDispatcherServlet。在BDF2-WEBSERVICE模块当中,我们已经将这个MessageDispatcherServlet要担负的作用集成到Dorado7提供的Controller当中,所以原MessageDispatcherServlet的工作完全由Dorado7中提供的Controller来完成。

BDF2-WEBSERVICE模块当中允许用户覆盖的属性如下表所示:

属性名 类型 默认值 描述

Page 102: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.webservice.useSecurity boolean false 是否为所有的Webservice服务添加WS-Security的加密认证,一旦设置成true,那么所有BDF2-WEBSERVICE模块暴露的Webservice服务客户端在进行访问时,都必须提供基于WS-Security的加密认证规范的用户名及密码信息,否则将不能调用目标Webservice服务;默认为false,表示不用WS-Security的加密认证就可以访问Webservice服务(具体项目当中可以通过实现BDF2-WEBSERVICE模块当中提供的IWebserivce来对单个Webservice服务添加WS-Security的加密认证,这样更为灵活,所以一般

)。该属性设置为false即可

bdf2.webservice.userServiceBean

String bdf2.userService 一旦我们的Webservice服务添加WS-Security的加密认证后,用户在访问这个服务时就需要提供用户名及密码,这个属性的值对应的bean,就是负责验证用户提交的用户名及密码是否正确。这个属性的值应该是一个Spring的bean的id,一个实现了UserDetailsService接口且配置到Spring当中的bean的id,它的默认值为bdf2.userService,表示采用BDF2-CORE模块当中提供的IUserService接口实现类(该接口扩展自 )UserDetailsService接口,当然,如果您的项目当中没有使用BDF2-CORE模块,那么需要重新编写一个UserDetailsService接口实现类,并配置到Spring当中,将该属性值改为配置的bean的id,否则会报找不到bdf2.userService这个bean的异常。

Page 103: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2.webservice.userCacheBean

String 空 如果我们对外暴露的Webservice服务,添加了WS-Security认证要求,且外部访问比较频繁,这样频繁的访问服务端每次都需要进行用户名及密码的认证,可能会对系统造成一定的压力,为解决这个问题,这个属性允许用户设置一个实现了org.springframework.security.core.userdetails.UserCache接口的bean的id,利用这个接口,将可以用于访问Webservice服务用户账号信息缓存起来,当用户调用Webservice服务时,就用这个接口当中缓存的账号信息对用户名密码进行验证,这样可大大减轻服务端验证用户名密码的压力。默认为空,表示不用缓存的用户账号信息进行认证,而采用bdf2.webservice.userServiceBean属性定义的bean来进行认证。

8.1.Webservice原理

前面说过,BDF2-WEBSERVICE是基于Spring-WS构建,所以如果您了解Spring-WS的开发流程,那么,一切就很容易实现。反之,如果您不了解Spring-WS或不太理解Webservice的运行原理,那我们建议您先去了解一下。这里在开始开发一个Webservice之前,我们建议您去w3school上学习一下 及 ,熟悉这两种类型的XML对于我们开发基于Spring-WS的Webservice来说是必须的,同时它也会帮助我们理解XSD语法 WSDL语法Webservice的整个运行原理,总之有百利而无一害。不要害怕学习,w3school上关于这两种类型XML的教程写的非常好,也非常简单,先去学习一下吧。

从零开始学习Webservice开发并不是BDF2-WEBSERVICE的责任,但这里我们也会简单介绍一下Webservice运行原理,要想更为深入的学习Webservice,大家还需要去好好研究它的规范才行。

我们知道,Webservice主要特点是跨平台,也就是我们通过JAVA暴露的Webservice可能通过Java的客户端调用,也可以通过.net或php的客户端调用,反之亦然。应该说,这一点是吸引我们使用Webservice最重要的原因,那它是怎么实现这种跨平台调用的呢?

Webservice规则通过传输固定格式的XML来解决跨平台调用问题,那就是在用户请求目标Webservice时,需要将请求的内容包装成一个特定的XML格式:SOAP,也就是简单对象访问协议,这是一个特定基于XML格式的消息协议。调用客户端将消息封装成SOAP格式的XML,通过标准的HTTP协议向目标Webservice发送一个标准的post请求,请求的内容就是这个封装好的SOAP格式的XML消息。Webservice服务端在收到这个客户端提交上来的POST请求之后,会将请求的XML内容以一个SOAP格式进行解析,在解析并获取到客户端请求的SOAP格式的XML消息之后,接下来,Webservice服务端需要知道服务端的哪个类哪个方法可以接受并响应这个请求。

一般来说,Webservice服务端有两种方式用于匹配具体的用于响应Webservice服务的类及方法:一个是根据请求的SOAP消息的XML的root的节点名称及其采用的namespace;一个是根据从SOAP消息的header中取出的SOAPAction的值(对于Spring-WS来说,这两种方法都支持),一旦找到了匹配的响应Webservice服务的类及方法,那么它会检查这个服务是否需要基于WS-Security的认证,如果需要,那么它会尝试从这个SOAP的header中获取相关认证信息,在通过认证之后,按照匹配的响应Webservice服务的类及方法的要求,将SOAP格式的XML消息的body部分解析成相应的参数对象,交由匹配的响应Webservice服务的类及方法去执行处理。方法处理完成之后,如果有响应,那么Webservice服务端会将响应的结果反序列化成一个标准的SOAP格式的XML,再这个XML回写到客户端,客户端再对取到的SOAP格式的响应结果进行解析,从而完成整个Webservice调用。

可以看到,Webservice服务的请求与响应的内容皆以XML格式为载体,我们知道XML是一种跨平台的消息传递格式,所有平台都可以对其进行解析、处理,所以Webservice也就可以进行跨平台调用。到目前为止,我们还没有涉及到WSDL,实际上Webservice服务本身与WSDL并无关系,WSDL的全称是Webservice描述语言,也就是说WSDL是用来描述目标Webservice是什么样的,用来告诉我们的客户该怎么调用这个Webser

Page 104: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

vice的。所以我们完全可以把WSDL理解成是某个Webservice服务的说明文档或者是帮助文档,没有这个WSDL,我们的Webservice服务照样可以正常运行,也就是说WSDL存在与否不会对Webservice服务产生任何影响。但对于我们程序员来说,暴露的Webservice是给我们调用的,所以我们必须学会看懂WSDL,不然就不知道如何调用目标Webservice,这也是我们开篇强调学习WSDL的重要原因。

开篇除了强调学习WSDL之外,我们还强调学习XSD,其实对于标准的Webservice而言是不需要了解XSD的,但我们这里强调学习XSD的原因是其可以帮助我们编写WSDL文件。了解了WSDL之后,会发现其语法较为复杂,手工编写起来难度很大,所以Spring-WS为我们提供了另外一种选择,那就是通过编写XSD来代替WSDL,Spring-WS会帮我们将XSD动态转换成WSDL,相比WSDL,编写XSD要简单许多。

Webservice开发有两种模式:一种称之为contract-first;另一种是contract-last。关于这两种模式的优劣, 作了大篇幅Spring-WS官方文档的讨论,感兴趣可以去看看,这里就不再赘述了。Spring-WS采用的是contract-first模式,相比contract-last有众多优势,具体大家可以看到Spring-WS官方文档。

对于Spring-WS来说,基于contract-first模式开发Webservice首先要做的第一件事就是确定我们的WSDL,也就是把要把编写的目标Webservice描述清楚:其中包含请求当中应该包含哪些信息、响应当中应该包含哪些信息、应该采用什么样的namespace等。所以这里的WSDL相当于我们软件开发当中的需求文档,把需求文档写清楚了,程序员才知道如何编写具体的业务代码,只是这里的需求文档是WSDL,是一个XML文件,除此之外,它与需求文档的在本质完全相同。

例子是最好的教程,接下来我们就来编写一个Webservice,并用SOAPUI来测试其是否可使用,按照Spring-WS的contract-first模式,我们首先来编写一个用于描述Webservice内容的WSDL,这里其实就是编写一个XSD文件。

8.2.定义XSD

对于Spring-WS的contract-first模式来说,开发一个Webservice我们需要从定义一个XSD文件开始。我们要定义的Webservice允许用户发一个请求,当中包含请求的用户数目及用户所在的公司两个信息,Webservice服务在收到这个请求之后,会根据请求的用户数目及所在公司的ID,生成对应的用户,对于生成的用户应该包含四个信息,分别是:用户名、出生日期、性别及所在公司ID,最后Webservice服务将生成的用户集合返回给客户端。

根据上述需求,我们的XSD内容编写如下:

Page 105: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

UserSerivce.xsd的内容

<?xml version="1.0" encoding="UTF-8"?><schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.bstek.com/ws"xmlns:tns="http://www.bstek.com/ws" elementFormDefault="qualified"> <element name="UserRequest"> <complexType> <all> <element name="userCount" type="int"></element> <element name="targetCompany" type="string"></element> </all> </complexType> </element> <element name="UserResponse"> <complexType> <sequence> <element name="users" maxOccurs="unbounded" type="tns:User"></element> </sequence> </complexType> </element> <complexType name="User"> <all> <element name="username" type="string"></element> <element name="birthday" type="date"></element> <element name="gender" type="boolean"></element> <element name="companyId" type="string"></element> </all> </complexType></schema>

从上面的XSD内容来看,其中我们采用的namespace为http://www.bstek.com/ws,这个地址是虚拟的,用户在定义时可以灵活选择,在这个XSD内容当中,我们定义了一个名为User的复杂类型对象(complexType),它包含用户名、出生日期、性别及所在公司ID四个element,然后我们还定义两个element对象,分别是UserRequest及UserResponse,其中UserRequest中包含两element,分别是userCount及targetCompany,对应我们的需求就是请求的用户数目及所在公司的ID;另一个UserResponse中只有一个名为users的element,它的类型是我们自定义的User类型,且它的maxOccurs为unbounded表示这个element在UserRequest中数目不限制,对应到我们Java当中这个users是一个集合对象,一个包含User对象的集合对象(可能是一个Collection,也可能是一个数组)。

在Spring-WS当中,在定义XSD时,以Request结尾的命名的element,默认将会被认为是Webservice调用的需要的输入信息(也就是调用参数);而以Response结尾命名的element默认将会被认为是Webservice调用的需要的输出信息(也就是调用返回的结果)。对照这个规则,上述的XSD当中,UserRequest将会作为Webservice的输入参数,而UserResponse则会作为Webservice的调用结果返回。

在理解这个XSD内容之后,接下来我们就可以按照这个XSD当中定义的内容来编写我们Webservice服务当中所涉及的相关Javabean。

8.3.根据XSD编写Javabean

根据前面定义的UserService.xsd文件,我们知道,需要定义的JavaBean有三个,分别是User、UserRequest及UserResponse,这三个Javabean采用的namespace、xmlrool、相关属性都已在UserService.xsd当中标明,我们需要做的就是按UserService.xsd文件定义的信息来编写Javabean即可。首先来看看User类,其源码如下:

Page 106: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

User类源码

package ws;import java.util.Date;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlRootElement;@(name="User",namespace="http://www.bstek.com/ws")@XmlAccessorType(XmlAccessType.FIELD)public class User{ private String username; private Date birthday; private boolean gender; private String companyId; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public boolean isGender() { return gender; } public void setGender(boolean gender) { this.gender = gender; } public String getCompanyId() { return companyId; } public void setCompanyId(String companyId) { this.companyId = companyId; }}

在这个User类当中,我们添加了两个Annotation,分别是XmlRootElement及XmlAccessorType,其中XmlRootElement用于根据XSD文件中定义的XmlRool及namespace来定义这个User类在序列化成XML之后Xmlroot值及namespace的值;XmlAccessorType用于定义这个bean中属性在序列化成xml之后子节点名及通过什么样方式访问这些属性值,这里定义成XmlAccessType.FIELD,表示直接采用属性名方式。

UserRequest与UserResponse与User类定义方式相同,全部是严格按照UserService.xsd定义编写,它们的源码如下:

Page 107: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

UserRequest源码

package ws;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement(name="UserRequest",namespace="http://www.bstek.com/ws")@XmlAccessorType(XmlAccessType.FIELD)public class UserRequest { private int userCount; private String targetCompany; public int getUserCount() { return userCount; } public void setUserCount(int userCount) { this.userCount = userCount; } public String getTargetCompany() { return targetCompany; } public void setTargetCompany(String targetCompany) { this.targetCompany = targetCompany; } }

UserResponse源码

package ws;import java.util.List;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement(name="UserResponse",namespace="http://www.bstek.com/ws")@XmlAccessorType(XmlAccessType.FIELD)public class UserResponse { private List<User> users; public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; }}

Javabean定义完成之后,接下来就可以定义负责具体接受客户端请求的Webservice服务类啦,在Spring-WS当中,这个类叫做Endpoint,我们来看看如何编写这个Endpoint。

8.4.编写Endpoint

从UserService.xsd中定义内容来看,其中有两个Element,一个是UserRequest,一个是UserResponse,根据Spring-WS的默认规范,对于

Page 108: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

以Request结尾的Element将作为Webservice服务的入参,而对于Response结尾的Element则要作为出参,所以我们即将要编写的Endpoint类中用于接受客户端请求的方法应该包含一个以UserRequest对象的入参,同时该方法还需要返回一个UserResponse对象,明确这一点之后,接下来就可以编写我们的Endpoint类了。我们的UserServiceEndpoint类源码如下:

UserServiceEndpoint类源码

package ws;import java.util.ArrayList;import java.util.Date;import java.util.List;import org.springframework.ws.server.endpoint.annotation.Endpoint;import org.springframework.ws.server.endpoint.annotation.PayloadRoot;import org.springframework.ws.server.endpoint.annotation.RequestPayload;import org.springframework.ws.server.endpoint.annotation.ResponsePayload;@Endpointpublic class UserServiceEndpoint{

@PayloadRoot(localPart="UserRequest",namespace="http://www.bstek.com/ws") public @ResponsePayload UserResponse getUsers(@RequestPayload UserRequest request){ int userCount=request.getUserCount(); String targetCompanyId=request.getTargetCompany(); UserResponse response=new UserResponse(); List<User> users=new ArrayList<User>(); for(int i=0;i<userCount;i++){ User user=new User(); user.setBirthday(new Date()); user.setCompanyId(targetCompanyId); user.setGender(true); user.setUsername("user"+i); users.add(user); } response.setUsers(users); return response; }}

在这个UserServiceEndpoint类当中,只有一个getUsers方法,可以看到这个方法需要一个UserRequest对象作为入参,同时方法会返回一个UserResponse对象,这个类与其它Javabean不同的地方是它使用一些Spring-WS提供的Annotation,我们首先来看看标注在类名为的Endpoint。

这个名为Endpoint的annotation表示当前这个类将作为Spring-WS的一个Endpoint,它可以接收特定的用户请求,执行其中的业务方法。

在getUsers方法上有个名为@PayloadRoot的Annotation,标明当前这个getUsers方法支持Webservice以SOAP消息的XMLroot节点名及namespace来匹配找到该方法,比如这里定义@PayloadRoot(localPart="UserRequest",namespace="http://www.bstek.com

")就表示该方法支持请求Webservice的SOAP消息当中,SOAP消息body部分XML节点名为UserRequest,同时采用http://www.bstek.co/wsm/ws作为namespace的SOAP消息,一旦我们的客户端发出的SOAP消息满足上述条件就会执行这里的getUsers方法。

在这个getUsers方法的入参当中有一个UserRequest类型的参数,其前面有个名为@RequestPayload的Annotation,这就表示这个UserRequest值需要从客户端请求的SOAP消息的Body当中解析出来,解析后的Body部分的XML要反序列化成这里需要的UserRequest对象。最后在这个方法的返回值UserResponse前面我们还添加了一个名为@ResponsePayload的Annotation,表示这个返回值将作为响应的负载返回到Webservice调用客户端。

再来看看这个getUsers方法体内部,这里我们只是根据请求信息虚拟了一批用户返回,实际业务中在这个getUsers方法体内部,我们应该去调用需要执行的业务逻辑方法,从而完成Webservice与业务结合的过程。

Endpoint编写完成之后,接下来我们就可以将上述编写XSD及Endpoint类配置到Spring环境当中。

8.5.配置XSD及Endpoint

Page 109: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

在编写完成XSD及根据XSD编写完成相关的Javabean及Endpoint类之后,最后一步就是将它们配置到Spring环境当中。首先我们来看看如何配置一个XSD文件,以使其能动态产生我们所需要的用于描述Webservice的wsdl文档。

打开我们的Spring配置文件,我们这里就采用WEB-INF/dorado-home目录下的datasources.xml文件进行配置,在这个datasources.xml文件当中添加如下配置信息:

XSD文件在Spring当中的配置

<sws:dynamic-wsdl id="UserService" portTypeName="DemoUserRequest"locationUri="/dorado/webservice/requestUserWebService"> <sws:xsd location="classpath:ws/UserService.xsd"/> </sws:dynamic-wsdl>

在上述配置当中,我们采用了一个名为sws的namespace,要使用这个namespace,我们需要在这个datasources.xml文件当中添加关于这个sws namespace的相关定义信息,不过默认我们提供的datasources.xml当中已经添加好这个namespace的定义,如下图所示:

在对这个UserService.xsd的配置当中,我们采用的是sws这个namespace提供的dynamic-wsdl,首先它需要指定一个id属性,这个属性很重要,它是我们请求浏览这个XSD文件生成的WSDL重要的凭据,然后指定它的portTypeName属性为DemoUserRequest,这里的portTypeName就是运行时动态生成的wsdl中的portType的name,代码这个服务的名称,接下来是定义locationUri属性,我们这里将其值定义为/dorado/webservice/requestUserWebService,之前我们介绍过,Webservice请求就是一个标准的Http的Post请求,这里将locaionUri属性值设置为/dorado/webservice/requestUserWebService,表示将动态生成的WSDL当中标明目标Webservice的调用地址格式为下面的样子:

http/https://servername:<port>/<contextpath>/dorado/webservice/requestUserWebService

这里需要着重强调的是,对于采用BDF2-WEBSERVICE模块发布的Webservice,在设置这个locationUri属性值是必须要以“/dorado/webservice/”开头,至于后面的值是什么,对于我们这个UserServiceEndpoint来说其实一点也不重要,因为我们这里的UserServiceEndpoint中的getUsers方法是通过请求的SOAP消息的body部分的ROOT节点名及namespace来进行匹配的,与具体的请求地址无关,所以对“/dorado/webservice/”后面是什么就不重要了。

配置好XSD之后,可以启动项目,请求如下格式地址:

http/https://servername:<port>/<contextpath>/dorado/webservice/UserService.wsdl

可以在我们的流程器当中如下图所示的WSDL文档内容:

Page 110: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

可以看到在访问这个wsdl时,我们也是以“/dorado/webservice/”开头,后台跟一个UserService.wsdl值,这里需要注意的是要访问wsdl,请求的URL一定要以“.wsdl”结尾,至于前面的UserService,其实就是sws:dynamic-wsdl中的id属性值,所以一旦请求以“/dorado/webservice/”开头同时以“.wsdl”结尾,那么就会有一个由BDF2-WEBSERVICE模块当中提供的专门的Controller负责进行处理,它只会关注.wsdl前面,所以对于上例来说,请求“/dorado/webservice/UserService.wsdl“可以看到WSDL,同样请求"/dorado/webservice/dasfasfd/asfasdf/UserService.wsdl”一样可以看到这个WSDL。

XSD配置好了,它也按我们的要求正确的生成了我们需要的WSDL文档了,最后就差配置Endpoint,再次打开我们的datasources.xml文件,在其中添加下面两行配置信息:

配置Endpoint

<context:component-scan base-package="ws"></context:component-scan><sws:annotation-driven/>

第一行的作用是告诉Spring要通过所有位于ws包下添加了Annotation的Javabean为标准的Spring的bean,第二行则是告诉Spring-WS扫描Spring Bean当中包含endpoint等与Webservice相关的Annotation,将它们映射成一个EndpointMapping。

对于我们这里的EndpointMapping实际就是将" "+"UserRequest"组合值与UserServiceEndpoint类及其getUserhttp://www.bstek.com/wss方法绑定,一旦发现请求的SOAP消息当中包含这样的namespace及XmlRoot就交由这个UserServiceEndpoint类的getUsers方法处理。

到这里为止,一个完整的基于Spring-WS的Webservice的编写与部署就全部完成了,接下来就可以对其进行测试了,我们这里采用的是SoapUI测试Webservice,SoapUI下载地址是 。http://www.soapui.org/

8.6.SoapUI调用Webservice

SoapUI是一款开源的Webservice调用客户端其下载地址为 ,下载其提供的windows binaryhttp://www.soapui.org/zip解压到本地后即可运行。

运动SoapUI后,在工具栏上点击“Create new soapui project”按钮,在弹出的窗口当中输入我们之前测试的wsdl地址,如下图所示:

Page 111: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

点击OK,可以看到系统会自动根据wsdl内容为我们创建相应的Webservice调用请求,如下图所示:

将SOAP消息当中的“?”换成具体值,点击工具栏上的提交按钮,可以在右边看到对应该请求服务端响应的结果,如下图所示:

Page 112: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

如果您也看到了如上图所示的结果,那说明您的Webservice编写成功了,到这里我们针对Webservice的测试也就完成了。

实际上,对于多数的Webservice在调用时都需要客户端提供相应的用户名密码进行验证,从而保护我们编写的Webservice服务,下面我们就来看一下如何为我们刚编写的Webservice添加WS-Security验证。

8.7.为Webservice添加Security认证

前面我们介绍过,要为在BDF2-SERVICE模块当中发布的Webservice添加WS-Secrutiy认证,可以覆盖bdf2.webservice.useSecurity属性值,将这个属性值设置为true即可,这样所以的发布的Webservice就全部需要WS-Security认证了。但实际应用当中,我们发布的Webservice当中可能有些需要进行认证,但还有一些我们是希望用户不用任何认证就可以直接访问的,这样就要求我们发布的Webservice可以独立控制自己是否需要进行WS-Security认证。

在BDF2-SERVICE模块当中,要想让发布的Webservice自已决定是否添加WS-Security认证,我们只需要我们的Endpoint类实现IWebservice接口即可,这个接口源码如下:

IWebService接口源码

package com.bstek.bdf2.webservice.jaxb;/** * @author Jacky.gao * @since 2013-3-6 */public interface IWebservice { Class<?>[] bindClasses(); boolean useSecurity();}

可以看到,这个接口非常简单,只有两个方法需要我们实现,第一个bindClasses用于将需要进行XML与JavaBean之间进行相互转换的类返回(unmarshaller与marshaller);第二个方法useSecurity就用来标明当前Webservice是否需要WS-Security认证,返回true表示需要,false表示不需要。

修改我们的UserServiceEndpoint类,让其实现IWebservice接口,修改后的代码如下:

Page 113: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

修改后的UserServiceEndpoint类源码

package ws;import java.util.ArrayList;import java.util.Date;import java.util.List;import org.springframework.ws.server.endpoint.annotation.Endpoint;import org.springframework.ws.server.endpoint.annotation.PayloadRoot;import org.springframework.ws.server.endpoint.annotation.RequestPayload;import org.springframework.ws.server.endpoint.annotation.ResponsePayload;import org.springframework.ws.soap.addressing.server.annotation.Action;import com.bstek.bdf2.webservice.jaxb.IWebservice;@Endpointpublic class UserServiceEndpoint implements IWebservice{ @PayloadRoot(localPart="UserRequest",namespace="http://www.bstek.com/ws") public @ResponsePayload UserResponse getUsers(@RequestPayload UserRequest request){ int userCount=request.getUserCount(); String targetCompanyId=request.getTargetCompany(); UserResponse response=new UserResponse(); List<User> users=new ArrayList<User>(); for(int i=0;i<userCount;i++){ User user=new User(); user.setBirthday(new Date()); user.setCompanyId(targetCompanyId); user.setGender(true); user.setUsername("user"+i); users.add(user); } response.setUsers(users); return response; } public Class<?>[] bindClasses() { return new Class[]{UserResponse.class,UserRequest.class,User.class}; } public boolean useSecurity() { return true; }}

重启我们的工程,再次通过SoapUI调用我们的Webservice,可以在右边响应窗口当中看到如下图所示信息:

Page 114: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

提示我们需要在调用目标Webservice时添加WS-Security的header,否则目标Webservice不允许调用。对于SoapUI来说,它支持标准的WS-Security认证,我们需要做就是双击左边树形导航的DemoUserRequestSoap11节点,在弹出的窗口当中定义相应的用户名及密码,同时需要将WSS-Type属性值改为PasswordDigest,表示Soap消息的header当中采用加密方式传递用户名及密码信息,如下图所示:

再点用SoapUI请求服务,可以看到已经能正确得到响应结果了。

这里需要特别指出的是,这里定义的username及password一定要与bdf2.webservice.userServiceBean属性定义的UserDetailsService实现类访问的用户信息对应,同时其中的password也必须是用户的实际存储的密码,比如,如果用户的密码是以密文形式存放的,这里也要输入具体的密文,而不能是加密之前的密码。

8.8.编写Webservice时的注意事项

对于Webservice的编写,我们可能会犯错的地方应该就是入参与出参了,以上面我们编写的Webservice为例,它的入参是UserRequest对象,出参是UserResponse对象,可以看到它的入参与出参都只是一个,这并不是巧合,而是故意为之,是一种最佳实践。

对于入参,UserRequest对象包含两个属性,一个是userCount,一个是targetCompany,也就是说我们的Webservice服务类需要两个参数,但在编写Webservice的Endpoint时,我们并没发将其作为两个入参处理,而是将这两个参数以属性的形式放在一个对象当中,作为一个入参处理。这样做的好处是避免了我们手工解析RequestPayload中的XML可能会出现的错误(Spring-WS允许我们方法调用参数当中解析RequestPayload中的XML以实现多个入参),同时即使我们的服务类需要再多的其它类型的参数(或者只有一个参数),我们也可以将其作为这个UserRequest对象的一个属性处理,简单且容易理解。

可能你会遇到一种相对较为简单的入参情况,那就是这个入参只有一个值,比如只有一个String类型的值,那么对于这种情况我们又该如何处理呢?同样遵行我们一个参数的原则,将我们需要端所需要的一个参数也放在一个我们自定义的对象当中,作为这个对象的属性实现数据的传递。比如我们之前的例子当中,如果服务端只需要一个userCount参数,而不需要targetCompany参数,同样我们也需要将这个userCount参数作为一个Javabean的属性。

讨论完入参后,我们来看看出参。对于出参而言,我们同样要遵循一个参数的原则,以我们上面所举的示例来说,出参是一个UserResponse对象,它有一个集合类型的User对象属性,也就是说,它的出参实际上也是一个值,一个集合类型的值,但我们并没有将这个集合直接作为出差使用,那是因为系统无法将集合值直接序列化成SOAP的消息部分内容,所以我们将这个集合放在一个我们自定义的名为UserResponse对象当中,这样在返回值时,系统会按照UserResponse对象当中定义的Annotation将这个返回值序列化成SOAP的消息部分内容。

通过以上对入参及出参的讨论,我们总结出一个结论,我们基于Spring-WS编写的Webservice的服务端方法,无论出参还是入参都只能是一个参数,而不能是多个,同时这个参数必须是我们自己定义的且添加了相关Annotation(比如@XmlRootElement等)的Javabean,对于服务端执行业务时需要的参数(无论是一个还是多个)都放到这个自定义的Javabean中,作为这个Javabean的标准属性实现参数值的传递。

9.BDF2-WEBSERVICE-CLIENT

为了帮助我们的程序能快速调用目标Webservice服务,BDF2-WEBSERVICE-CLIENT模块提供了一个调用Webservice服务的客户端,利用这个客户端,通过几行简单的代码,就可以实现对目标Webservice服务的调用。在使用空上客户端时,它既可以调用那些不需要任何安全性验证的Webservice,也可以调用那些使用WS-Security进行认证的Webservice,同时对于一些采用HttpBasic进行认证的Webservice,我们的Webservice调用客户端也提供了支持。

为了演示这个客户端的具体用法,接下来我们用这个客户端来尝试调用在BDF2-WEBSERVICE模块当中介绍的示例Webservice,看看它能不能

Page 115: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

调用成功并为我们返回正确的结果。具体调用代码如下:

WebserviceClient类调用Webservice测试

package ws;import com.bstek.bdf2.webservice.client.WebServiceClient;public class WebserviceInvokeTest { public static void main(String[] args) throws Exception{ String uri="http://localhost:8080/bdf2-test/dorado/webservice/requestUserWebService"; //设置Webservice客户端要调用的目标Webservice的地址 WebServiceClient client=new WebServiceClient(uri); //设置要调用的目标Webservice的WS-Security认证所需要的username及password,以及是否对用户名及密码进行加密传输 client.setUsernameToken("admin", "81c1206f28f2738cdf714b4e10428c66f58eee10", true); /** * 设置要调用的目标Webservice的Http Basic认证所需要的username及password, * Http Basic认证当中所使用的密码为加密之前的密码,而非被加密之后的密码 * */ //client.setHttpAuthenticationCredentials("admin", "admin"); UserRequest request= new UserRequest(); request.setTargetCompany("bstek"); request.setUserCount(2); //设置在调用目标Webservice过程当中,需要将Javabean与XML进行相互序列化的Javabean类的class client.setMarshallerClasses(new Class[]{UserRequest.class,UserResponse.class}); //发送调用请求并返回调用结果 UserResponse response=(UserResponse)client.sendAndReceive(request); //输出返回结果 for(User user:response.getUsers()){ System.out.println("username:"+user.getUsername() +" company:"+user.getCompanyId()); } }}

在这个测试代码当中,我们来看看其中要调用的目标Webserivce的uri,这个uri的值为“http://localhost:8080/bdf2-test/dorado/webser”,实际上这个值是从我们之前生成的WSDL中获取的,再来看看我们的UserService.wsdl中最后那个service部vice/requestUserWebService

分,如下图所示:

Page 116: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1.

2.

3.

可以看到,这个uri的值源自我们的wsdl,实际上在编写Webservice调用客户端时,我们需要好好研究目标Webservice产生的WSDL文件,要通过这个文件知道目标Webservice的调用地址是什么(uri),需要传递的入参是什么,出参又是什么等,只有了解这些信息之后,才能确定该如何编写我们的Webservice调用客户端,所以,对于使用Webservice的广大程序员来说,了解并能读懂WSDL是何其重要。

10.BDF2-RAPIDO

概述

Rapido是BDF2当中提供的一个较为独立的模块,利用该模块可以实现在网页当中设计、开发Dorado7应用页面。Rapido在使用上与我们现在使用的基于Eclipse插件的Dorado7IDE有些类似,但在开发方式上有本质的不同,利用Rapido来开发Dorado7页面更为简单,速度当然也更快,同时因为其在网页开发页面,所以正常情况下不需要编写Java代码,所以其更容易为一些不了解Java的开发人员接受,同时开发好的页面还实时预览以查看效果。如果您有一些Dorado7组件使用基础,同时又对Java不是很熟悉,那么使用Rapido来开发应用页面,就是一个不错的选择。

使用Rapido您可以在网页当中连接后台数据库,可以通过向导方式来拼接我们需要的查询数据的SQL,如果您对手写SQL比较熟悉的话,也可以手写输入查询SQL,SQL定义完成之后还可以预览SQL查询结果。在Rapido当中提供了大部分Dorado7常用组件、全部的组件属性、所有的组件布局方式以及全部的组件事件,这样在原Dorado7 Eclipse IDE当中能做出的页面效果,在Rapido当中也可以采用同样的方式开发出来。

为了使采用Rapido开发更为灵活,我们也提供了一些可供用户扩展的点,在这些扩展点上用户可以编写一些复杂的Java代码,以实现页面在与后台交互时可以做一些复杂的业务逻辑。Rapido当中可供用户扩展的点包括以下几种:

数据库表的主键生成方式。在对数据进行维护时,插入新的数据就需要有一些数据库主键生成方式,默认Rapido提供了基于UUID的主键生成方式,如果需要需要其它方式,可以通过实现接口加以扩展。BeanShell脚本支持。Rapido当中允许用户定义BeanShell脚本,同时对于脚本中可使用的变量,用户也可以自行定义。定义好的BeanShell脚本可在数据更新或纯粹的Ajax调用中执行。IAction接口实现。IAction是Rapido中提供的一个业务逻辑调用实现接口,用户可以在这个接口实现当中添加属于自己应用的业务逻辑,以实现复杂业务逻辑的执行。

在原Dorado7IDE当中,我们开发页面都在一个view.xml当中进行,在Rapido当中,我们将一个标准的view.xml开发拆分成六个部分,每个部分相互衔接,用户可以根据页面需要灵活选择使用。这样做的好处就是尽可能将一个复杂页面的开发的复杂度降到最低。Rapido中包含的六部分内容如下图所示:

Page 117: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1. 2. 3. 4. 5. 6.

页面。用于生成最终的view.xml文件,页面由若干个组件构成。组件。用于定义页面需要使用的Dorado7组件,组件定义时可定义其属性、布局、事件等关键信息。动作。定义页面组件与后台的交互,用于数据提交或纯粹的Ajax调用。实体。连接后台数据库表,定义SQL及要显示的字段等信息。实体映射。实现静态或动态(来自数据库中表数据信息)字段翻译。元数据。将一些常用字段属性信息通过元数据方式定义起来,这样在定义实体时就可以直接数据元数据中定义的字段信息,以实现字段属性的统一定义与管理。

在后面的章节当中,我们将对这六个部分分别进行介绍。

实现机制

Rapido作为现有Dorado7IDE页面开发的补充与完善,它以一个基于数据库的信息管理系统方式实现,既所有信息在定义时全部放在数据库表当中,通过关系型数据库表将构成页面定义的六部分信息分别进行存储,在生成页面时,再将这六部分定义的信息从数据库中抽取出来,最终生成一个view.xml文件。

从其实现机制上看,它并没有改变Dorado7原有的页面渲染方式,只是调整了页面中各部分信息定义和存储的方式,在最后一步预览时才将这些定义的信息从数据库表中读取出来,生成view.xml文件供Dorado7使用。也就是说采用Rapido生成的view.xml文件,在最终Dorado渲染的时候,还是采用原有机制,不依赖Rapido定义的数据表或缓存之类,这样就避免了由此可能产生的性能问题。

利用Rapido开发页面,我们通常从创建实体开始,实体创建完成之后到组件页,将创建的实体与Dorado7相关组件绑定,最后在页面中添加这些组件,生成Dorado7所需要的view.xml页面,如下图所示。

在定义过程当中,根据需要可能要定义元数据或定义数据映射,同样在定义组件时可能需要定义动作等。这里值得一提的时为实现快速开发,Rapido还提供了诸多快捷功能,比如可自动实现DataGrid的filterbar后台数据查询等。

Page 118: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

安装与配置

Rapido是BDF众多模块中的一部分,所以在使用时需要配置好一个BDF项目,需要注意的时,它依赖BDF中的com.bstek.bdf.jbpm4.dorado7模块,也就是在使用Rapido模块时,要保证项目中包含com.bstek.bdf.jbpm4.dorado7模块,并能正常运行。

如果已经有一个可运行的BDF项目后,接下来只需要到BDF的下载页面里将Rapido模块下载下来(Rapido单独发布,不与现在的BDF众模块一块发布),解压后将名为com.bstek.bdf.rapido.[version].jar放置到项目的WEB-INF/lib目录下,然后打开dbschema,把Rapido运行时依赖的数据库表创建出来,最后再配置Rapido中一些参数即可。

Rapido中提供的可覆盖的系统参数如下表所示:

属性名 描述

bdf2.rapido.dataSourceName Rapido运行时,定义页面的所需六部分信息存储所依赖的表所在的数据源名称,默认值为空,表示采用BDF2中默认数据源

bdf2.rapido.app.dataSourceName 利用Rapido定义页面实体时采用的数据源,需要注意的时,利用BDF2中提供的多数据源机制,使用Rapido时,可以通过该属性单独指定一个业务数据库所在的数据源名,也就是说可以将BDF2的基础表及Rapido基础表放在一个数据源中,而定义页面需要使用的业务表所在数据源定义为另一个,这样在定义页面实体时,我们只能看到业务表,而看不到BDF2中众多基础表,这样就可以极大方便Rapido使用人员。

bdf2.rapido.outputDir Rapido定义完成后,最终生成的view.xml文件存储路径,它的默认值为rapidooutput,表示将在我们应用的根下创建一名为rapidooutput的目录,然后将生成的view.xml文件及其所在子目录存放于该目录下,如果我们需要设置一个绝对路径,比如D:/output目录,那么可以在WEB-INF/dorado-home/configure.properties文件中添加该属性,并设置该属性值为D:/output。需要注意的是这个output目录必须在D盘下已存在,否则将出现错误。

bdf2.rapido.urlPrefix 请求Rapido生成的view.xml页面时,URL的前缀是什么。它的默认值为r,这就表示如果我们要访问一个位于test包下的名为Employee的view.xml文件,那么请求的地址就是r.test.Employee.d,如果要修改这个前缀,同样在WEB-INF/dorado-home/configure.properties中添加该属性,同时将其值定义成我们需要的即可。

运行工程,打开浏览器访问 ,就可以打开Rapido操作主界面。bdf2.rapido.view.Workspace.d

以上内容了解完后,接下来我们就可以使用Rapido来在线设计Dorado7应用页面啦。

10.1.实体

概述

Rapido各模块之间关系,前面的内容也做了简单描述,我们要开发一个业务页面,在Rapido当中首先要做的就是先定义若干个实体。定义实体之前,我们需要先定义好实体存放的包名,如下图所示:

在实体节点上右键–>添加包,在弹出的窗口当中输入具体的包名即可。

Page 119: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

操作

定义好包之前,在需要放置实体的包下双击包名即可在右边工作区中看到实体定义界面,如下图所示:

要添加实体,可以在左边右键,在弹出菜单中选择添加顶级实体,如下图所示:

需要注意的是,实体支持树形结构,利用这种树形结构就可以实现我们应用当中的主从表、递归树之类层次形业务数据展示需求。添加完实体后,就可以定义该实体对应的数据库表及查询数据采用的SQL等信息。

点击“主体表名”字段中的按钮,就可以弹出查询SQL拼装向导,利用该向导,我们就可以定义一些比较规则的查询SQL,如下图所示:

包名添加注意事项包名添加时支持以点分隔的多包同时定义方式,此功能有些类似Eclipse中定义Java的package功能。

Page 120: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

在实体SQL定义时,除了可以通过上述向导方式实现,如果您对SQL比较熟悉,那么可以直接在查询SQL字段中直接定义要查询的SQL,在SQL定

义过程当中,我们还可以通过边上的 图标,打开一个专业的SQL编辑器输入SQL,在SQL定义中,还可以使用velocity语法定义动态SQL,如下图所示:

Page 121: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

具体操作,这里就不再赘述。

扩展

在实体定义过程当中,可以看到,实体对应的表字段主键系统可以帮助我们自动计算出来,有了主键,就可以实现表数据的更新操作,但如果要向表中插入新的数据,就需要指定主键生成机制。

通过情况下,主键有用户自定义与自增长两种类型。用户自定义的主键又可以分为页面中用户手工输入主键;后台业务代码生成主键;数据库表自增长类型的主键。在Rapido当中,默认提供了自增长与自定义两种主键生成方式,对于自增长类型主键,在插入数据时会采用数据库自增长机制填充主键;对于自定义主键生成方式,Rapido默认提供了基于UUID的主键生成机制。在实际应用开发当中,我们可能需要其它类型的自定义主键生成方式,比如Oracle数据库中的sequence机制等,在这种情况下我们就可以通过实现IGenerator接口,来定义属于我们自己的主键生成方式,IGenerator接口内容如下:

package com.bstek.bdf2.rapido.key;import java.util.Map;/** * 定义主键生成器 * @author [email protected] * @since 2012-7-16 */public interface IGenerator<T> { /** * 用于显示的描述 * @return 返回描述信息 */ String desc(); /** * 产生一个唯一的用于填充主键的ID值,唯一的参数是一个Map,其中一定包含的参数有如下三个:<br> * 1.entityTableName:当前操作的表名,<br> * 2.entityFields:当前表对应实体定义时包含的字段,<br> * 3.entityTablePrimaryKeys:当前表在定义实体时设置的主键名,多个主键字段名之间采用逗号分隔(,),<br> * 以上三个在Map中包含的参数,可以在生成唯一ID时可以选择性的使用,<br> * 除以上三个参数包,其中还包含所有在客户端动态添加进来的参数 * @param map 数据提交中所带的各种参数 * @return 返回的的UUID值 */ T execute(Map<String,Object> map);}

接口实现类编写完成之后,我们需要将其配置到Spring上下文环境当中,这样在实体页面配置实体时,在自定义方式主键生成机制下,就可以看到这个实现类信息。

10.2.组件

概述

实体定义完成之后,我们就可以来定义组件了,这里指的组件就是Dorado7中各种组件,定义方式与Dorado7IDE非常类似,组件定义也采用的是树形结构,这样就可以实现组件的嵌套定义。同样,定义组件前要定义存放组件的包,包定义好之前就可以双击进入要定义的组件页面啦。

使用

组件定义界面如下图所示:

Page 122: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

添加组件同样采用右键方式实现,可以看到,Dorado7当中绝大多数组件都可以在这里定义,添加完组件后可以定义其属性及事件,如果当前组件为容器类组件,还可以设置其布局方式,如果是数据敏感组件,还可以设置其绑定的实体,如果是Button类组件还可以与具体的动作绑定等。

组件的定义,需要您熟悉Dorado7组件的各种属性及事件。

这里需要注意的是,和Dorado7IDE一样,如果为AutoForm或DataGrid选择对应的实体,那么会自动创建AutoForm下面的FormElement以及DataGrid下的column,这样就可以极大提高页面的开发效率。

扩展

在使用过程当中,如果您发现Dorado7中新增加一些组件,但在目前的Rapido中还未将其包含进来,那么您可以自己动手编写接口的实现将其添加到Rapido环境当中。

要在Rapido中添加一个新的组件,需要实现两个接口,分别是com.bstek.bdf.rapido.component.ISupport和com.bstek.bdf.rapido.xml.IConverter。ISupport用于定义组件的一些显示信息,其内容如下:

package com.bstek.bdf2.rapido.component;import java.util.Collection;import com.bstek.bdf2.rapido.component.property.PropertySupport;import com.bstek.bdf2.rapido.domain.ComponentInfo;import com.bstek.bdf2.rapido.domain.ComponentProperty;import com.bstek.bdf2.rapido.domain.Entity;/** * 用于定义组件显示信息 * @author [email protected]

在为组件编写事件时,如果需要引用页面当中的组件,那么可以直接通过使用view.id(组件名)实现,如果要引用实体对应的DataSet,可使用规则“ ”实现,比如引用实体Employee对应的DataSet,那么可以采用如下方式编写事件:dataSet+实体名

var ds=view.id("dataSetEmployee")

这样就可以获取到Employee实体对象对应的DataSet,如果要获取动作的引用,同样直接用view.id(动作名)就行。

Page 123: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

* @since 2012-8-25 */public interface ISupport { /** * 组件的显示名称 * @return 具体要显示的名称 */ String getDisplayName(); /** * Dorado7中该组件类的完全限定名 * @return 类全名 */ String getFullClassName(); /** * 组件显示图标 * @return 图标地址 */ String getIcon(); /** * 对组件支持的某些属性进行特殊处理 * @return PropertySupport对象的集合 */ Collection<PropertySupport> getPropertySupports(); /** * 当前组件下可以使用哪些子组件 * @return ISupport对象集合 */ Collection<ISupport> getChildren(); /** * 根据选择的实体自动创建子组件 * @param entity 选择的实体对象 * @return 子组件集合 */ Collection<ComponentInfo> createChildrenByEntity(Entity entity); /** * 根据组件ID创建组件常用属性 * @param componentId 组件ID * @return 组件属性集合 */ Collection<ComponentProperty> createComponentPropertysByComponentId(String componentId); /** * @return 是否支持实体 */ boolean isSupportEntity(); /** * @return 是否支持布局 */ boolean isSupportLayout(); /** * @return 是否支持动作 */ boolean isSupportAction(); /** * @return 是否为容器类型组件 */ boolean isContainer(); /** * @return 是否可以独立存在

Page 124: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

*/ boolean isAlone();}

ISupport接口实现编写完成之后需要配置到Spring环境当中,这样在Rapido组件编辑页面当中就可以看到定义的组件信息。在页面预览时如果希望定义的组件能被正确输出,我们还需要定义一个对应的IConverter接口实现。IConverter接口代码如下:

package com.bstek.bdf2.rapido.xml;import org.dom4j.Element;import com.bstek.bdf2.rapido.domain.ComponentInfo;import com.bstek.dorado.idesupport.model.RuleSet;/** * 用于view.xml页面生成时,输出组件对应的xml信息 * @author [email protected] * @since 2012-8-25 */public interface IConverter{ /** * 根据给定的组件信息生成组件对应的XML * @param component 组件信息 * @param ruleSet 组件对应的Dorado7的规则信息 * @param rootElement view.xml文档的根 * @return 返回组件的Dom4j的Element对象 * @throws Exception */ Element convert(ComponentInfo component,RuleSet ruleSet,Element rootElement) throws Exception; /** * 当前Converter是否支持给定的组件 * @param component 给定要判断的组件 * @return 是否支持 */ boolean support(ComponentInfo component);}

IConverter编写完成后,同样需要配置到Spring环境当中,这样新增组件的工作就完成了。

10.3.页面

概述

组件定义完成,就可以定义页面啦。之前说过,页面是由若干个组件构成,同样页面也需要位于不同的包下,双击包名即可进入该包下页面编辑。

操作

页面操作如下图所示:

Page 125: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

可以看到,在添加组件时,只能选择位于包根下的组件,对于组件里的子组件是不能选择的。组件添加完后,可以设置该组件在该页面中是否为只读,如果设置为只读,那么页面在渲染时将会把该组件及其下所有子组件设置为只读状态(readOnly=true)。

页面添加好组件后,就可以进行预览了,预览的过程就是先页面组件信息从数据库中读取出来,然后再生成一个view.xml文件,最后再利用Dorado7引擎去渲染这个view.xml文件。预览的过程也就是Rapido生成页面的过程。预览操作完成之后,就可以看到页面被Dorado7引擎渲染的效果,同时打开由bdf.rapido.outputDir所定义的页面输出路径下对应包所在目录当中,就可以看到生成的对应的view.xml文件,该文件是个标准的Dorado7的view.xml文件,可以在Dorado7的Eclipse IDE当中打开,当然也可以根据需要进行二次编辑。

10.4.动作

概述

动作是Rapido当中提供的用于实现将前台数据提交给后台处理或执行一个Ajax调用的功能,从本质上来说,它所完成的就是Dorado7当中的UpdateAction与AjaxAction两种类型Action要做的事情。动作在定义的过程当中,如果选择与某实体关联,那么最终将会生成一个UpdateAction;如果不选择与某实体关联,那么它就是一个AjaxAction。

动作定义中,除了可以选择具体要绑定的实体外,还允许用户编写BeanShell脚本与绑定若干个预先定义好的实现了IAction接口的实现类,这样就尽可能赋予动作最大的灵活性,在不编写Java代码的情况下完成足够多的后台复杂的业务逻辑,以进一步增加Rapido开发复杂业务页面的能力。

操作

切换到Rapido工作区,展开动作节点,在需要维护动作的包下双击,就可以打开动作维护页面,效果如下图所示:

Page 126: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1. 2. 3.

点击添加按钮,在弹出的窗口当中就可以添加一个动作,在动作定义时还可以定义动作使用的参数,这些参数在支持执行期间会从前台页面传输到后台供使用。

扩展

前面提过,动作定义时可以添加若干个预置的“具体动作”,它们是一些实现了IAction接口的配置在Spring当中的Java类,在Rapido当中默认提供了三个IAction接口实现,分别是:

根据流程模版的ID,开启一个流程实例。保存流程任务审批过程中的审批意见信息。根据任务ID完成一个具体的流程任务。

可以看到,这三个接口实现都与流程相关,有了这三个实现,就可以将Rapido做的页面与流程关联起来,从而实现与流程相关业务页面开发。IAction接口内容如下:

Page 127: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

package com.bstek.bdf2.rapido.action;import java.util.Collection;import java.util.Map;import com.bstek.bdf2.rapido.domain.Parameter;/** * 用于定义前后台交互时要执行的具体动作,<br> * 动作可以累加,也就是说可以一次性执行多个当前接口的实现类,<br> *用户可以通过实现该接口,在execute方法中添加具体的需要执行的具体逻辑代码,然后将实现类配置到spring当中即可 * @author [email protected] * @since 2012-7-5 */public interface IAction { /** * 返回当前动作的名称 * @return 返回动作名称 */ String getName(); /** * 具体要执行的动作代码,这里的参数只有一个Map,<br> * 在这个Map当中包含了当前业务数据的ID(key为businessId)以及所有在动作定义时定义的参数 * @param map 外部传入的参数 */ Map<String,Object> execute(Map<String,Object> map); /** * 执行这个实现时需要哪些参数 * @return 返回一个Parameter对象实例集合 */ Collection<Parameter> requiredParameters();}

如果您需要编写一个IAction接口实现,那么同样需要将实现类配置到Spring上下文当中。

在编写BeanShell脚本时,除了可以使用Rapido内置的变量外,用户可以通过实现com.bstek.bdf2.bsh.VariableRegister,该接口源码如下:

package com.bstek.bdf.bsh;import java.util.Map;/** * 注册BeanShell脚本中要使用的变量信息,实现类需要配置到Spring环境当中,<br> * 因此在注册时可使用Spring环境中各种类型的Bean对象 * @author [email protected] * @since 2012-8-28 */public interface VariableRegister { /** * 返回注册到BeanShell上下文中的变量集合 * @return 返回一个变量集合的Map,Map中key为变量名,Value就是一个VariableInfo对象,用于描述变量信息 */ Map<String,VariableInfo> register();}

一旦将实现该接口的Bean配置到Spring当中,我们在动作中编写BeanShell脚本时就可以下图所示的可用脚本变量中看到新加的BeanShell变量信

Page 128: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

息。

扩展示例

自定义执行存储过程的BeanShell变量示例:

1.定义ProcedureTemplate对象

package com.bstek.rapido.demo;import java.sql.CallableStatement;import java.sql.Connection;import java.sql.SQLException;import java.sql.Types;import java.util.HashMap;import java.util.Map;import org.springframework.dao.DataAccessException;import org.springframework.jdbc.core.CallableStatementCallback;import org.springframework.jdbc.core.CallableStatementCreator;import com.bstek.bdf.rapido.RapidJdbcDaoSupport;/** * 存储过程执行对象 * @author Lucas * */public class ProcedureTemplate extends RapidJdbcDaoSupport { /** * @param procedureSql * 执行存储过程语句 * @param param * 向存储过程传递的参数 * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Map<String, Object> call(final String procedureSql,

Page 129: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

final Map<String, Object> param) { final Map<String, Object> outParam = new HashMap<String, Object>(); this.getJdbcTemplate().execute(new CallableStatementCreator() { @Override public CallableStatement createCallableStatement(Connection con) throws SQLException { CallableStatement cs = con.prepareCall(procedureSql); int i = 1; for (String key : param.keySet()) { Object obj = param.get(key); if (obj instanceof String) { int j = i++; cs.setString(j, (String) obj); cs.registerOutParameter(j, Types.VARCHAR); } else if (obj instanceof Integer) { int j = i++; cs.registerOutParameter(j, Types.INTEGER); cs.setInt(j, (Integer) obj); } else if (obj instanceof Long) { int j = i++; cs.registerOutParameter(j, Types.INTEGER); cs.setLong(j, (Long) obj); } } return cs; } }, new CallableStatementCallback() { @Override public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { cs.execute(); int i = 1; for (String key : param.keySet()) { Object obj = param.get(key); if (obj instanceof String) { outParam.put(key, cs.getString(i++)); } else if (obj instanceof Integer) { outParam.put(key, cs.getInt(i++)); } else if (obj instanceof Long) { outParam.put(key, cs.getLong(i++)); } } return null; } }); return outParam; } /** * 执行无参存储过程 * * @param procedureSql * 执行存储过程语句 */ public void execute(final String procedureSql) {

Page 130: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

this.getJdbcTemplate().execute(procedureSql); }}

2.将ProcedureTemplate对象注册为BeanShell变量

package com.bstek.rapido.demo;import java.util.HashMap;import java.util.Map;import com.bstek.bdf.d7.bsh.VariableExecutor;import com.bstek.bdf.d7.bsh.VariableInfo;import com.bstek.bdf.d7.bsh.VariableRegister;/** * 注册ProcedureTemplate对象实例 * @author Lucas * */public class ProcedureTemplateRegister implements VariableRegister { private ProcedureTemplate procedureTemplate; public ProcedureTemplate getProcedureTemplate() { return procedureTemplate; } public void setProcedureTemplate(ProcedureTemplate procedureTemplate) { this.procedureTemplate = procedureTemplate; } @Override public Map<String, VariableInfo> register() { Map<String, VariableInfo> map = new HashMap<String, VariableInfo>(); VariableInfo procedureTemplateInfo = new VariableInfo(); procedureTemplateInfo.setName("procedureTemplate"); procedureTemplateInfo.setDesc("自定义的执行存储过程的ProcedureTemplate对象实例"); procedureTemplateInfo.setVariableExecutor(new VariableExecutor() { public Object execute() { return procedureTemplate; } }); map.put("procedureTemplate", procedureTemplateInfo); return map; }}

3.配置到Spring配置文件中

<bean id="procedureTemplate" class="com.bstek.rapido.demo.ProcedureTemplate" /><bean class="com.bstek.rapido.demo.ProcedureTemplateRegister"> <property name="procedureTemplate" ref="procedureTemplate" /></bean>

4.BeanShell脚本中使用procedureTemplate变量

Page 131: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

Map resultMap=new HashMap();Map paramMap=new HashMap(); paramMap.put("id","123"); paramMap.put("name","beanShell"); resultMap = procedureTemplate.call("exec test_insert ?,?",paramMap); resultMap.put("id_test",resultMap.get("id"));return resultMap;

10.5.实体映射

概述

实体映射实际上应该叫实体字段映射,用于翻译Rapido中定义的实体字段信息。

比如有一Employee实体,它有一个名为dept_id字段,在实际页面开发中,我们通常都需要将这个dept_id显示为具体的dept_name,同时在Employee页面维护当中,还需要在dept_id所在编辑框处出现一个用于选择dept的下拉框,下拉框中显示是dept_name,当选中某个dept_name时,回填到dept_id字段上的值还是当前dept_name对应的dept_id的值,这个时候我们就可以采用实体映射来实现。

联系到Dorado7中页面组件开发,Rapido中的实体映射实际上就是在定义DataType中PropertyDef中的mapping属性,只是Rapido中提供的方式更为简便,也更为快捷。

操作

在Rapido工作区,展开“实体映射”节点下包,双击某个包名,就可以进入到实体映射维护界面,如下图所示:

从编辑界面当中可以看到,映射的数据来源有两类:一类是数据库表中的数据,比如上例中的部门信息等;一类是用户自定义,比如性别之类。如果选择“数据库表”,那么就可以在窗口中的查询SQL表单中编写具体的SQL(或打开SQL向导),同时需要定义“显示字段名”和“实际值字段名”;如果选择“自定义”,上述三个字段就会变成只读,在下面的"自定义键值对"中可输入映射的键值信息,格式为:

定义好映射之后,在实体维护界面就可以在需要映射翻译的字段上选择与之关联的实体映射信息。

Page 132: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

10.6.元数据

概述

我们知道,元数据就是用于描述数据的数据,Rapido当中的元数据是用于描述实体字段信息的数据。在定义实体字段时,我们需要指定实现字段的默认值、标题、显示格式、验证器等信息,一旦定义好这些信息,那么在生成页面时就会根据这些信息对组件显示效果进行调整。

在Rapido当中,除了可以看这些信息直接定义在实体字段当中,还可以将这些信息定义到元数据当中,然后将定义好的元数据绑定到某实体字段上,这样这个实体字段就会将对应元数据中定义的默认值、标题、显示格式、验证器等信息加载过来,同时如果在实体字段上同时定义了描述字段信息与元数据,那么实体字段上定义的描述信息将覆盖元数据中定义的描述信息。

使用

元数据页面效果如下图所示:

元数据的维护操作比较简单,这里就不再赘述了。一旦定义好元数据,那么就可以在实体字段当中使用它,使用的方法是选择某个需要匹配元数据的字段,点击工具体栏上的“元数据匹配”按钮,即可完成操作,如下图所示:

Page 133: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

需要注意的是,元数据与实体字段的匹配是依赖元数据的名称与实体字段的列名是否相同来完成匹配的(元数据名中是否包含当前列名,如果包含则认为相符),如果相同,则会将元数据信息填充到实体字段的“采用的元数据”列当中,如果匹配到多于一条相符的元数据信息,那么会弹出一个窗口让操作者选择一个合适的。

11.BDF2-PROFILE

BDF2-PROFILE模块提供了一个允许管理员对系统当中页面组件的显示进行个性化定制的功能。对于这种组件的个性化定制,管理可以将其施加给某个用户、某个部门或某个公司等,一旦对目标群体(用户、部门或者公司)施加了个性化定制功能,那么隶属于这个用户、部门或者公司的人登录系统之后就可以看到这个页面被管理员修改后的效果,接下来们举个例子来说明这个功能的适用场景。

假如我们需要开发一个系统,这个系统将以SAAS模式给不同的公司去使用,但对于某一些功能页面,不同的公司对页面显示效果要求可能也略有不同,比如某个业务系统维护页面,某些公司可能其中的一个表单中字段A、B、C的显示顺序为A、B、C,但有些公司可能因为某些原因希望将顺序改成B、C、A,对于这样一种需求,因为两家公司习惯不同,对页面的显示要求也不相同,但这么一点不同又不值得我们专门开发一个页面特地给这家公司使用,这个时候就可以利用BDF2-PROFILE模块轻松解决这个问题。当然,BDF2-PROFILE模块能个性化的功能不仅仅是表单元素或表格列的显示顺序,它几乎可以实现所有的支持属性及事件定义的组件个性化定制,比如针对不同的公司为某个Button设置不同的caption,或者针对不同公司为某个UpdataAction设置不同的保存前的确认消息或保存成功之后的提示消息等。

要使用BDF2-PROFILE模块,首先需要将BDF2-PROFILE模块相关的jar放置到我们的项目当中,具体做法就是可以到nexus.bsdn.org上下载BDF2-PROFILE模块相关jar或到我们提供的在线项目向导当中,创建一个包含BDF2-PROFILE模块的项目,当然如果您采用的是Maven来管理我们的项目,那么只需要在我们项目的 pom.xml当中添加下面的依赖信息即可。

BDF2-PROFILE模块的依赖信息

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-profile</artifactId> <version>2.0.0</version></dependency>

添加完BDF2-PROFILE模块所需要的jar之后,接下来还需要编写一个IProfileDataService接口实现类,并将其配置到Spring当中,否则启动时会抛出下面的异常:

Page 134: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

异常消息

Caused by: java.lang.RuntimeException: The current spring application context without a[com.bstek.bdf2.profile.service.IProfileDataService] interface implementation atcom.bstek.bdf2.profile.view.component.ComponentMaintain.afterPropertiesSet(ComponentMaintain.java:414) atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1545) atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483) ... 23 more

我们来看看IProfileDataService接口实现类代码:

Page 135: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IProfileDataService接口源码

package com.bstek.bdf2.profile.service;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import com.bstek.bdf2.profile.model.AssignTarget;import com.bstek.bdf2.profile.model.UrlDefinition;import com.bstek.dorado.data.provider.Criteria;import com.bstek.dorado.data.provider.Page;

/** * 使用profile模块必须实现的接口 * @author Jacky.gao * @since 2013-2-26 */public interface IProfileDataService { /** * 根据给出的父ID,返回其下所有子的URL,实现URL树构建 * @param parentId 父URL ID * @return 返回指定父ID的URL集合 */ List<UrlDefinition> loadUrls(String parentId); /** * 分页加载分配资源的目标对象 * @param page Dorado7分页对象 * @param criteria 过滤查询对象 */ void loadAssignTargets(Page<AssignTarget> page,Criteria criteria); /** * 返回加载个性化信息需要使用的分配资源目标对象的ID,比如username,companyid之 * @return 一个字符串 */ String getAssignTargetId(HttpServletRequest request);}

接下来我们编写一个IProfileDataService接口实现类,代码如下:

IProfileDataService接口实现类源码

package test;

import java.util.ArrayList;import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;

import com.bstek.bdf2.core.context.ContextHolder;

Page 136: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

import com.bstek.bdf2.core.model.Url;import com.bstek.bdf2.core.view.url.UrlMaintain;import com.bstek.bdf2.profile.model.AssignTarget;import com.bstek.bdf2.profile.model.UrlDefinition;import com.bstek.bdf2.profile.service.IProfileDataService;import com.bstek.dorado.data.provider.Criteria;import com.bstek.dorado.data.provider.Page;

@Componentpublic class TestProfileDataService implements IProfileDataService { @Autowired @Qualifier("bdf2.urlMaintain") private UrlMaintain urlMaintain; public List<UrlDefinition> loadUrls(String parentId) { List<UrlDefinition> list=new ArrayList<UrlDefinition>(); try { for(Url url:urlMaintain.loadUrls(parentId)){ UrlDefinition def=new UrlDefinition(); def.setId(url.getId()); def.setUrl(url.getUrl()); def.setName(url.getName()); def.setParentId(parentId); list.add(def); } } catch (Exception e) { throw new RuntimeException(e); } return list; }

public void loadAssignTargets(Page<AssignTarget> page, Criteria criteria) { List<AssignTarget> targets=new ArrayList<AssignTarget>(); AssignTarget t1=new AssignTarget(); t1.setId("root-bstek"); t1.setName("上海锐道"); targets.add(t1); AssignTarget t2=new AssignTarget(); t2.setId("root-ibm"); t2.setName("IBM中国"); targets.add(t2); page.setEntities(targets); }

public String getAssignTargetId(HttpServletRequest request) { return ContextHolder.getLoginUser()==null?null:ContextHolder.getLoginUser().getCompanyId();

Page 137: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

}

}

从实现类当中可以看到,在第一个方法加载菜单时,我们使用时BDF2-CORE模块当中加载菜单的类,实际应用当中,如果您项目当中没有CORE模块,那么,您需要自己编写加载URL菜单的代码;第二个方法加载需要分配个性化信息的目标对象时,我们虚拟了两家公司,实际应用当中,可以是公司,也可以是部门、群组或者直接分给某个用户;最后一个方法是系统在加载个性化信息时使用的,这里与前面对应采用的是公司的ID。

实现类编写好并配置到Spring当中之后,接下来启动我们的项目,浏览com.bstek.bdf2.profile.view.component.ComponentMaintain.d这个URL,可以看到如下图所示的页面效果:

可以看到,在选择不同的公司后,对应的URL菜单也会加载进来,在选择不同的URL时,这个URL对应的页面组件会被剖析成一棵树形,在这棵树当中找到我们需要进行个性化调整的组件,设置它的属性、事件或其下子组件的顺序。在做完这些操作之后,别忘记刷新系统缓存,否则将不会生效。

12.BDF2-AUTHORITYDELEGATION

功能介绍

BDF2-AUTHORITYDELEGATION实现了权限下放管理功能,所谓权限下放管理即系统超级管理员通过定义一些用户为下级管理员,并为之分配一些可以管理的用户、部门、岗位、群组及URL资源,使之具有权限管理的功能。

环境搭建

BDF2-AUTHORITYDELEGATION依赖BDF2-CORE开发实现,所以要在包含BDF2-CORE模块的BDF2项目中才能使用该模块功能,项目中引入该模块方法如下:

Maven类BDF2项目,在项目pom文件中添加如下配置:

Page 138: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1.

2.

3.

4.

depenency依赖

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-authoritydelegation</artifactId> <version>2.0.0</version> </dependency>

Dynamic Web类BDF2项目,项目中添加bdf2-authoritydelegation-Version.jar即可。

使用手册

权限下放管理功能使用步骤如下:

采用系统管理员账号登录系统。

访问下放资源管理页面bdf2.authoritydelegation.view.allocation.AllocationMaintain.d,添加下级管理员,并根据业务需要为之分配可管理的用户、部门、岗位、群组及URL资源。采用下级管理员用户账号登录系统,访问角色资源管理页面bdf2.authoritydelegation.view.role.url.RoleUrlMaintain.d及角色成员管理页面bdf2.authoritydelegation.view.role.member.RoleMemberMaintain.d进行权限管理。角色关联的成员或URL资源发生变化时要通过角色成员管理页面或角色资源管理页面中"刷新缓存"功能更新系统缓存。

权限下放管理示例

系统中提前创建好了系统管理员用户admin和普通用户user1、user2,以及一个“权限下发管理测试角色1”。 第一步:采用admin用户登录系统。 第二步:通过下放资源管理页面添加用户user1为下级管理员。 为下级管理员user1分配一个可以管理的用户user2如图11-1。

图11-1

为下级管理员user1分配可以管理的URL资源如图11-2。

Page 139: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

图11-2

第三步:这时用user1用户账号登录系统,

访问角色资源管理页面可以看到user1可以为“权限下发管理测试角色1”分配其可管理的URL资源,如图11-3。

图11-3

访问角色成员管理页面可以看到user1可以为“权限下发管理测试角色1”分配其可管理的用户user2,如图11-4。

Page 140: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

图11-4

第四步:略(刷新缓存)。

属性配置

属性名 数据类型 默认值 描述

bdf2.authoritydelegationDataSourceName

String 空 AUTHORITYDELEGATION模块使用的数据源名称,默认值为空采用默认数据源,通过BDF2中覆盖属性值方法可指定特定数据源。

13.BDF2-COMPONENTPROFILE

功能介绍该模块提供了doardo7数据感知控件(datagrid,autoform)的个性化定制功能.所谓的定制化就是允许用户根据自己的需求去调整某一字段顺序,显隐宽度等信息,并将该信息持久化到数据库, 使得用户有更人性化的浏览体验.

环境搭建

本模块依赖于BDF2-ORM模块,也就是说只要是BDF项目,都可以运行该模块.

Maven Project

为现有的BDF2 maven项目添加如下依赖:

BDF2-COMPONENTPROFILE maven依赖

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-componentprofile</artifactId> <version>2.0.0</version></dependency>

Page 141: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1.

Dynamic Web Project

如果现在已经有了一个bdf2项目, 可以去nexus下载该模块的jar包: , 并将其放到项目web-inf/lib目录下.bdf2-componentprofile-2.0.0.jar

如果还没有一个bdf项目可以通过我们的 来生成一个新的bdf2项目.向导

jar包添加完毕之后, 通过在线的方式更新dorado7规则. 如果在视图文件的DoradoToolBox的BDF2分类里看到两个ProfileAction(AutoformProfielAction,DataGridProfileAction)控件说明该模块已成功安装.

配置方式

实现接口

要使用该控件,首先要实现com.bstek.bdf2.componentprofile.service.IDataService接口. 该接口只有一个方法,getProfileKey.个性化定制的结果以这个key去保存,同时以这个key去加载.如果我们定义profileKey为当前登录用户名,那么每个用户都可以拥有专属自己的个性化定制,如果profileKey定义为当前登录用户所在的部门,那么每个部门共有一个个性化定制化.

IDataService接口

public interface IDataService { String getProfileKey();}

Page 142: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1.

2.

以当前登录人为profileKey的样例

public class UserProfile implements IDataService { public static String UNKNOWN = "unknown"; @Override public String getProfileKey() { String profileKey = null; try { profileKey = ContextHolder.getLoginUserName(); } catch (Exception e) { profileKey = UNKNOWN; } return profileKey; }}

配置ProfileAction控件首先要根据需要为view页面添加相应ProfileAction控件。2.1 dataGridProfileAction只比正常的action多了一个属性dataGrid,用来绑定要保存个性化配置的dataGrid.dataGridProfileAction的配置:

2.2autoformProfileAction的autoForm跟dataGridProfileAction的dataGrid功能类似,是用来绑定一个要保存个性化信息的autoform控件。该action比datagridProfileAction多了四个属性如图所示:

Page 143: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

2.

3.

多出四个属性是因为autoform的个性化是在一个单独的对话框中实现的,而datagrid是可以直接对自身做修改,这四个属性控件着配置对话框中相应的列的显隐,默认都是显示的。如果某一列,不希望用户去个性化定制,将其设置为false即可。

当我们对dataGrid做过调整之后,触发dataGridProfileAction,调整过后的数据就可以自动的保存到数据库里。触发的方式也跟其它action一样,可以通过button绑定action的方式也可以通过调用action的execute方法。当触发autoformProfileAction之后,会弹出如上图所示的配置对话框,在该对话框内对autoform做修改,然后点击保存,即可保存个性化配置,这时需要刷新页面看到效果。绑定方式(两个action方法一样,以dataGridProfileAction为例):

Page 144: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

3.

4.

onClick事件方式:

重置个性化设置当我们需要清除已有的个性化配置时,需要执行profileAction的resetConfig方法来恢复到默认设置。该事件只能通过javascript来触发。

14.BDF2-EXPORT

功能介绍

bdf2-export模块主要提供了对Dorado7中Datagrid和Autoform两种类型的控件报表导出功能,以Datagrid或者Autoform为显示模板,导出其数据为PDF或者Excel格式,支持Datagrid控件中filterBar数据过滤导出、列头组合自定义、数据自定义导出等功能。同时可以利用BDF2中提供的swf-viewer模块,在线浏览产生的报表文件,供用户在线浏览和打印使用。

环境配置

Maven Project (推荐)

如果您采用Maven管理项目,可以通过访问 ,在模块选择中勾选bdf2-export模块,动态创建一个Maven在线创建BDF2项目向导Project即可。或者在现有的BDF2Maven项目中,将bdf2-export模块的依赖信息添加到pom.xml文件当中,依赖配置如下:

Page 145: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

bdf2-export依赖信息

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-export</artifactId> <version>2.0.0</version></dependency>

Dynamic Web Project

如果您打算采用Dynamic Web Project方式,那么访问 ,在项目类型中选择 在模块选在线创建BDF2项目向导 Dynamic Web Project,择中勾选bdf2-export模块,动态创建一个 即可,在这个动态创建的项目中包含了bdf2-export模块的jar包和第Dynamic Web Project三方依赖包。

由于bdf2-export模块采用Dorado7组件方式实现导出、预览功能,那么在创建配置完成项目后,需要启动项目工程更新Dorado7配置规则(在线更新),更新完Dorado7配置规则后,打开DoradoView文件,可以看到在Dorado7工具栏新添加了一个组件Export2ReportAction。如下图所示:

Export2ReportAction控件

Page 146: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

Export2ReportAction控件提供了若干属性配置,其中关键属性如下表所示:

属性名 类型 默认值 描述

template String 空 导出数据模板DataGrid或者AutoForm的控件id,如果有多个值那么逗号分隔,例如 autoForm

d1, ataGrid1

interceptorName String 空 服务端数据拦截器名称

rowSpace int 1 报表间距,template属性指定多个控件id时有效

autoDownload boolean true 是否自动下载

showTitle boolean false 是否显示标题

titleName String 空 标题的名称

titleBgColor String #FFFFFF 标题背景色

titleFontColor String #000000 标题颜色

titleFontSize int 18 标题字体大小

showPageNumber boolean true 是否显示页号,导出pdf格式时有效

headerBgColor String #D8D8D8 表头背景色

headerFontColor String #000000 表头颜色

headerFontSize int 10 表头字体大小

dataBgColor String #FFFFFF 数据背景色

dataFontColor String #000000 数据颜色

dataFontSize int 10 数据字体大小

dataScope String currentPage 数据范围,可选值currentPage、serverAll

maxSize int 1000 导出数据最大记录数

fileName String 空 导出报表文件名称

extension String xls 导出报表类型,可选值xls、xlsx、pdf,如果导出的excel数据量比较大,建议配置成xlsx格式

除了以上关键属性外,Export2ReportAction还提供一个关键事件,可以利用此事件自定义客户端数据的导出,如下表所示,

事件名称 事件描述

onGetExportData(self,arg) 客户端获取导出的数据时触发,

arg.id 导出模板控件id

arg.data 导出的数据

功能演示

导出DataGrid

Page 147: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

如下图所示,对DataGrid控件数据做服务端导出,其中dataGrid控件id为dataGrid1:

在view上添加Export2ReportAction控件,设置其属性template为dataGrid1,dataScope为serverAll,showTitle为true,titleName为注册用户,导出效果如下:

默认导出是excel文件,如果设置属性extension为pdf,则导出的是pdf文件,导出pdf效果如下:

如果设置属性template为多个DataGrid控件id,并以逗号分隔表示,则可以导出多张表格数据 ,导出的excel文件效果如下图所示:

导出的pdf文件效果如下图所示:

Page 148: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

导出AutoForm

如下图所示,对AutoForm控件数据做导出,其中AutoForm控件id为autoForm1:

在Export2ReportAction控件上设置其属性template为autoForm1 ,则默认的导出效果如下图所示:

Page 149: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

如果设置属性extension为pdf,导出pdf效果如下:

在线预览

在view中添加一个dialog控件,设置id属性为dialogSwfViewer,在dialog上添加一个swfViewer控件,设置id属性为swfViewerTest,宽度和高度分别为100%,调用代码如下所示:

var action=view.get("#export2ReportAction1");var dialog=view.get("#dialogSwfViewer");//在服务端生成文件action.set("autoDownload",false);//文件类型为pdfaction.set("extension","pdf");action.execute(function(result){ dialog.show(); var swfViewer=view.get("#swfViewerTest"); //result包含文件id和name信息 swfViewer.set("parameter",result); //export.pdf2swf 为内置的处理器 swfViewer.set("handlerName","export.pdf2swf"); swfViewer.refreshSwf();});

在线预览效果如下图所示:

在线预览打印用到bdf2-swfviewe模块,相关配置可参考16.BDF2-SWFVIEWER

Page 150: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

客户端数据自定义拦截

设置属性 在事件dataScope为currentPage, onGetExportData上定义待导出的excel数据,例如导出DataGrid中用户勾选的数据,代码如下所示:

var grid=view.id(arg.id);if(grid instanceof dorado.widget.DataGrid){ //获取表格中勾选的数据 arg.data=grid.get("selection");}

服务端数据自定义拦截

如果需要对服务端导出的数据做拦截,则需要写一个实现com.bstek.bdf2.export.interceptor.IDataInterceptor接口的类,并配置的spring上下文环境中,然后指定Export2ReportAction控件的属性interceptorName值为实现类中getName()方法返回的名称。

例如对上面示例中的DataGrid控件列“是否是管理员”、“是否可用”做中文格式化处理,同时动态在服务端添加一条导出记录,则实现类定义如下所示:

Page 151: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

package com.test;import java.util.HashMap;import java.util.List;import java.util.Map;import org.springframework.stereotype.Component;import com.bstek.bdf2.export.interceptor.IDataInterceptor;@Componentpublic class UserReportDataInterceptor implements IDataInterceptor { public String getName() { return "userReportDataInterceptor"; } public String getDesc() { return "服务端表格数据拦截自定义演示"; } public void interceptGridData(List<Map<String, Object>> list) throws Exception { for (Map<String, Object> user : list) { if (user.get("administrator") != null) { user.put("administrator",((Boolean) user.get("administrator")) == true ? "是": "否"); } if (user.get("enabled") != null) { user.put("enabled",((Boolean) user.get("enabled")) == true ? "是" : "否"); } } Map<String, Object> insertUser=new HashMap<String, Object>(); insertUser.put("username", "user4"); insertUser.put("cname", "user4"); insertUser.put("ename", "user4"); list.add(insertUser); } public void interceptAutoFormData(List<Map<String, Object>> list) throws Exception { }}

同时,在Export2ReportAction控件中指定属性interceptorName为userReportDataInterceptor,导出显示效果如下图所示,

15.BDF2-IMPORT

功能介绍

bdf2-import模块主要提供excel导入功能,可以将存放于Excel当中的数据批量导出到数据库(或者其它存储介质)。在某些项目当中,项目开

Page 152: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

发完成之后,接下来可能会面对大量基础数据录入的问题,而这些基础数据可能多数都以Excel形式存在。对于这种情况,我们可以利用BDF2当中提供的Excel数据导入功能,通过简单的模型定义,在不写一行代码的情况下,就可以将位于Excel中的数据导入到我们的数据库当中,从页实现大量、多种类型的基于Excel的基础数据的录入工作,bdf2-import功能模块具有以下特点:

提供可视化的Excel导入配置界面,可以在线对Excel文件数据进行预览、数据合法性效验、解析导入数据库;可以同时支持Microsoft-Excel 97-2003(.xls)和Microsoft-Excel 2007+(.xlsx);

通过实现指定的接口,可以对导入Excel数据进行自定义处理;

环境配置

要使用 bdf2-import模块,我们可以到nexus.bsdn.org上下载最新的bdf2-import模块的jar,或者可以到我们提供的在线创建项目向导中选择bdf2-import模块并下载即可;同样,如果您采用的是Maven来管理项目,那么只需要将bdf2-import模块的依赖信息加到我们的pom.xml当中即可:

bdf2-import模块所需要的依赖

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId> bdf2-import</artifactId> <version>2.0.0</version></dependency>

在bdf2-import模块当中,提供了提供可视化的Excel导入配置界面,所以,如果您在使用bdf2-import模块时,也同时使用了bdf2-core模块,那么可以在登录之后访问generate.system.menu.action这个用于初始化菜单的URL,创建好的导航菜单如下图所示:

导入模板定义

打开"导入模板定义"页面,添加一个导入方案,如下图所示。

Page 153: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

点击【添加】按钮,定义导入模板,如下图所示。

这里的需求注意的是导入模板的方案的ID,这个ID首先需要唯一,然后尽量定义的有意义,因为我们后面在具体调用时要使用这个ID,其次需要注意的是如果导入数据的处理不采用“ ”,而使用“自定义处理类”,那么我们写一个实现系统默认处理类 com.bstek.bdf2.importexcel.processor.IExcelProcessor接口的自定义类,并配置到spring环境当中,接口IExcelProcessor定义如下:

Page 154: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

IExcelProcessor接口

package com.bstek.bdf2.importexcel.processor;import com.bstek.bdf2.importexcel.model.ExcelDataWrapper;/** * 要导入的Excel内容的处理类接口,如果自定义处理类,需要实现此接口 * * @author [email protected] * @since 2.0 */public interface IExcelProcessor { public String getName(); /** * 执行处理Excel内容的方法 * * @param excelDataWrapper * 一个包装了Excel信息的集合 * @return 导入处理成功返回的记录数 * @throws Exception */ public int execute(ExcelDataWrapper excelDataWrapper) throws Exception;}

定义完方案之后,就可以为该定义信息添加具体与Excel对应的列信息了,如下图所示。

同样在定义列信息时,对于列中数据的处理,还允许用户添加处理拦截类,这个拦截类的作用可以对导入的数据进行格式化(比如excel里定义的性别为男、女,存储到数据库中应该变为1和0等),或合法性验证(长度是否符合要求,是否为空等)。系统默认提供了三种拦截类,如下图所示:

如果需要自定义拦截类,那么只需要定义一个类实现com.bstek.bdf2.importexcel.interceptor.ICellDataInterceptor接口,并配置到spring环境即可,例如系统中格式化性别的拦截类定义如下:

Page 155: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

FormatGenderInterceptor

package com.bstek.bdf2.importexcel.interceptor.impl;import org.springframework.stereotype.Service;@Service("bdf2.formatGenderInterceptor")public class FormatGenderInterceptor extends RequiredInterceptor { public Object execute(Object cellValue) throws Exception { super.execute(cellValue); if ("男".equals(cellValue)) { return "Y"; } else { return "N"; } } public String getName() { return "格式化性别[男=Y,女=N]"; }}

调用导入模板

定义好导入模板后,我们就可以在应用需要的地方调用这些定义好的excel模板,快速实现将Excel中的数据上传并导入到服务端。由于bdf2-import调用导入模板采用Dorado7组件方式实现,那么在创建配置完成项目后,需要启动项目工程更新Dorado7配置规则(在线更新),更新完Dorado7配置规则后,打开Dorado View文件,可以看到在Dorado7工具栏新添加了一个组件ImportExcelAction。如下图所示:

Page 156: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

ImportExcelAction控件提供若干关键属性,如下表所示:

属性名 类型 默认值 描述

excelModelId String 空 定义导入模板时指定的方案编号

startRow int 空 excel的开始行,如果不指定默认值,默认第一行开始解析

endRow int 空 excel的结束行,如果不指定默认,系统自动判断excel的结束行值

showImportData boolean true 是否在线预览导入的数据,默认true

bigData boolean false 如果数据量比较大,可以设置为true,解析的excel单元格值类型为字符串

在view中添加一个ImportExcelAction控件,设置对应的模板方案编号,调用示例代码如下:

var importExcelAction=view.get("#importExcelActionDemo");importExcelAction.set("excelModelId","0001");importExcelAction.execute(function(arg){ dorado.MessageBox.alert("成功解析导入[" + arg + "]数据!");});

Page 157: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

整个调用界面效果如下面几张图所示:

16.BDF2-SWFVIEWER

bdf2-swfviewer是BDF2提供的一个标准的Dorado Addon,可以在线显示flash动画,同时提供了一个flash形式的小插件,可实现报表的在线预览与打印。

Page 158: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

要使用bdf2-swfviewer模块,我们可以到nexus.bsdn.org上下载最新的bdf2-swfviewer模块的jar,或者可以到我们提供的在线创建项目向导中选择bdf2-swfviewer模块并下载即可;同样,如果您采用的是Maven来管理项目,那么只需要将bdf2-swfviewer模块的依赖信息加到我们的pom.xml当中即可:

bdf2-swfviewer模块依赖

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-swfviewer</artifactId> <version>2.0.0</version></dependency>

添加完成bdf2-swfviewer模块后,启动项目工程,在线更新Dorado配置规则文件,更新完成之后,打开一个view,可以在工具栏当中看到如下图所示组件图标:

SwfViewer控件关键属性如下所示;

属性名 类型 默认值 属性描述

swfUrl String 空 如果不指定值,采用默认的url处理dorado/bdf2/swfviewer/swfFile.do

handlerName String 空 配合服务端处理类,动态指定swf文件资源位置,需要定义一个类实现com.bstek.bdf2.swfviewer.handler.ISwfFileHandler接口

Page 159: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

parameter String 空 服务端处理类可以接受的参数值

showType String complex 显示类型,可选值simple、complex

printEnabled boolean true 是否显示打印按钮

在线显示flash动画

在view文件中添加一个SwfViewer控件,指定一个swf资源文件,如下配置所示:

<SwfViewer id="swfViewerTest"> <Property name="showType">simple</Property> <Property name="swfUrl">dorado/res/com/test/black_clock.swf</Property></SwfViewer>

这样一个简单的配置就完成了,页面运行效果如下图所示:

在线预览和打印

在线预览和打印需要需要将pdf文件通过服务端动态生成swf格式的文件,因此需要安装swftools,swftools提供了一个名为pdf2swf的工具,可以将pdf文件转换成swf文件。swftools的安装配置可以参考bdf1中的相关文档:6.1.安装与配置SWFTOOLS

安装完成swftools之后,需要在dorado-home/configure.properties文件中指定swftools的安装目录和对应的xpdfpath安装目录。

window系统用户可能的配置如下,

bdf2.swfviewer.pdfToSwf=D:/mysoft/swftools/pdf2swf.exebdf2.swfviewer.xpdfPath=D:/mysoft/xpdf-chinese-simplified

Linux系统用户可能配置如下,

bdf2.swfviewer.pdfToSwf=/usr/bin/pdf2swfbdf2.swfviewer.xpdfPath=/usr/share/xpdf-chinese-simplified

对指定的pdf文件实现在线预览功能,服务端需要实现com.bstek.bdf2.swfviewer.handler.ISwfFileHandler接口,实现类代码如下所示:

安装swftools的目录不能包含空格,否则无法转换pdf文件!

Page 160: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

package com.test;import java.io.File;import java.util.Map;import org.springframework.stereotype.Component;import com.bstek.bdf2.swfviewer.controller.PdfToSwfConverter;import com.bstek.bdf2.swfviewer.handler.ISwfFileHandler;@Componentpublic class TestPdfToSwfHandler implements ISwfFileHandler { public String getHandlerName() { return "test.pdf2swfHandler"; } public String getHandlerDesc() { return "在线预览打印pdf文件"; } public File execute(Map<String, Object> varMap) throws Exception { System.out.println(varMap.get("testParameter1")); System.out.println(varMap.get("testParameter2")); File pdfFile = new File("d:/UserInfo.pdf"); String sourcePdf = pdfFile.getAbsolutePath(); PdfToSwfConverter pdfToSwfConverter = new PdfToSwfConverter(); String swf = pdfToSwfConverter.execute(sourcePdf, null); return new File(swf); }}

SwfViewer控件需要设置的代码示例如下,

swfViewer.set("handlerName","test.pdf2swfHandler");swfViewer.set("parameter",{ testParameter1:"test1", testParameter1:"test2"});swfViewer.refreshSwf();

通过服务端编写实现类转换pdf为swf文件,在view界面通过SwfViewer控件显示运行效果如下:

Page 161: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

点击右上角的打印机图标,即可实现打印功能,如下图所示:

17.BDF2-DBCONSOLE

功能介绍

bdf2-dbconsole模块是一个标准的Dorado7Addon,可以在线浏览特定数据库表信息,同时可以实现对数据库信息进行跨平台维护。目前的版本可以实现对H2,、MySQL、SQLServer、Orac

如果访问界面出现如下提示,表示当前浏览器没有安装Adobe Flash Player,请下载安装最新的Adobe Flash Player即可:

Page 162: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

1. 2. 3. 4. 5. 6. 7. 8.

le、DB2等常见主流程数据库表的管理,可以实现诸如表结构信息的查看与维护(创建表、增删改表格列信息),表数据的查看与维护,SQL的执行以及查询结果数据的Excel导出等。有了这个网页版的数据库浏览器之后,用户简单的打开网页就可以连接到他需要维护的数据库,实现对表结构与数据的查看与维护,且所有类型数据库操作方法及风格统一,大大增加数据库维护人员及管理员的便利性。bdf2-dbconsole模块主要特性:

支持大部分数据库包括:H2,、MySQL、SQLServer、Oracle、DB2等数据库。采用数据源连接模式,提供SQL执行效率。同时连接到多个数据库、易于使用的连接向导。数据库对象树视图,所选数据库的层次,它的表格和字段等。不需要写SQL语句就能够查看,新增,删除,修改表格中的数据和字段。支持数据库中各种字段类型。SQL语法高亮显示、支持批处理操作。数据导出Excel文件。

环境搭建

要使用 bdf2-dbconsole模块,我们可以到nexus.bsdn.org上下载最新的bdf2-dbconsole模块的jar,或者可以到我们提供的在线创建项目向导中选择bdf2-dbconsole模块并下载即可;同样,如果您采用的是Maven来管理项目,那么只需要将bdf2-dbconsole模块的依赖信息加到我们的pom.xml当中即可:

bdf2-dbconsole依赖信息

<dependency> <groupId>com.bstek.bdf2</groupId> <artifactId>bdf2-dbconsole</artifactId> <version>2.0.0</version></dependency>

在bdf2-dbconsole模块当中,提供了在线管理数据库信息的页面,所以,如果您在使用bdf2-dbconsole模块时,也同时使用了bdf2-core模块,那么可以在登录之后访问generate.system.menu.action这个用于初始化菜单的URL,创建好的导航菜单如下图所示:

使用说明

安装配置完成后,运行Web工程,打开数据库浏览器菜单,就可以看到bdf2-dbconsole数据库浏览器的主界面,数据库浏览器主界面如下图所示:

Page 163: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

默认情况下,打开的是当前系统采用的数据源,点击数据源根节点,可以看到对应数据库中所有的表,点击每个表,就会在右边窗口出现可以对表数据以及结构进行操作的三个标签页。对象浏览器标签页主要完成对表结构的浏览和修改操作,数据浏览器标签页主要完成对数据库的浏览和修改操作,SQL编辑器主要完成在线编辑SQL,对数据库进行SQL查询或者更新操作。

对象浏览器标签页

对象浏览器标签页主要完成表的结构的浏览,表结构Excel导出,当用户选中表节点是,可以查看表的所有列名称、类型、字段长度、是否是主键、是否可以为空等信息。同时还可以完成对表结构的修改操作,如添加字段、删除自带、修改字段等功能,如下图所示:

数据浏览器标签页

数据浏览器标签页主要可以查看当前表的数据,同时可以对表数据进行批量的添加。删除、修改维护。同时可以导出当前查询页记录或者所有的记录为Excel文件。如下图所示:

SQL编辑器标签页

Page 164: 1. BSTEK Development Framework2(BDF2) Home€¦ · Confluence spaces are great for sharing content and news with your team. This is your home page. Right now it shows This is your

SQL编辑器标签页支持语法高亮,允许用户直接输入一条或多条标准的SQL(查询或更新的SQL),同时支持对查询结果进行Excel导出操作。如下图所示: