1. 模块化与解耦规范
1.1. 写在规范之前
无论做多么小的功能组件,编码时自省,要思考以下问题
- Basic
- 数组是否做了边界判断。
- 入参是否进行了空指针防护。
- 性能
- 大循环中是否会多次访问数据库。
- 是否使用了同步锁,如果使用同步锁,粒度要尽可能细。
- 安全
- 是否不登录就可以访问,返回值是否包含敏感信息。
- 是否根据前端传入的用户Id返回指定的信息,而不是根据当前用户。
- 集群
- 是否要支持集群,如果使用Timer是否要控制只在主节点运行。
- 需不需要缓存同步,缓存是否存储了太多数据。
- 依赖(升级和兼容性)
- 是否引入第三方依赖,第三方依赖是否支持JDK6,对于WAS和WLS是否有可能有影响。
- 日志:
- 是否输出太多Info级别的日志。
- 是否输出了必要的信息便于排查(利用好debug和trace)
- 资源释放
- 连接池是否在finally中close。
- 是否有打开未关闭的Stream。
- 内存(数据量):
- 是否做了分页,内存里预期上限会有多少条数据。
- 线程安全
- 是否使用了线程安全的集合
- 反之,方法内部变量不要使用线程安全的集合
1.2. 编码规范
一致性高于一切
1.2.1. 命名
编码和命名沿用Java编码规范,保持语义一致。
禁止使用个人姓名或昵称用于任何地方作为名称,产品是企业行为,不要带个性化的特征。
分类 | 命名 | 备注 |
---|---|---|
Package | com.seeyon.apps.{moduleName}.api | 全部小写 |
Interface | com.seeyon.apps.{moduleName}.api.XXXApi | 如com.seeyon.apps.news.api.NewsApi |
Impl | com.seeyon.apps.{moduleName}.api.XXXApiImpl | 如com.seeyon.apps.news.api.NewsApiImpl,实现代码放在具体的模块工程 |
Bean Name | xXXApi | 如newsApi |
Spring xml File Name | spring-{moduleName}-api.xml | 如spring-news-api.xml |
Param | com.seeyon.apps.{moduleName}.api.XXXParam | 方法参数超出3个需引入Param对象作为参数 |
PO | com.seeyon.apps.{moduleName}.po | 统一,方便维护 |
所有的接口方法必须抛出异常,异常类型为BusinessException
void run() throws BusinessException;
示例:
import com.seeyon.apps.news.api.NewsApi;
import com.seeyon.apps.news.api.NewsParam;
...
NewsApi api;
long id;
api.get(id);
api.get(new NewsParam(newsType,pageNum,pageSize));
bean定义放在具体模块的工程,如spring-news-api.xml
:
<bean name="newsApi" class="com.seeyon.apps.news.api.NewsApiImpl"/>
方法命名
命名采用"动作+属性" 的方法。并且,动作以小写字母开始,属性以大写字母开始。常用的动作有:is、get、set、create、 update、delete。 遵从业界惯例,for可缩写为4,to可缩写为2
分类 | 命名 | 备注 |
---|---|---|
新建 | createXXX | 不用insert,公告发布可使用issue,协同发送使用send,但须申请并增补到术语表中 |
修改 | updateXXX | 不用modify、save |
删除 | deleteXXX | 不用remove |
查询-单一对象 | get | 无数据统一返回null,不允许抛出异常 |
查询-对象列表 | find | 不用search、query,无数据不允许返回null,返回Collections.emptyList() |
查询-全部对象 | findAll | 无数据时处理同上 |
查询-对象计数 | getXXXCount | - |
新建、修改和删除不允许使用boolean或int作为返回值,原则上应该是void的
long id;
String subject;
NewsType type;
// 按Id取
News aNews = api.get(id);
// 按某一特定的规则取,如果不会产生歧义,就不必加ByName、BySubject
News aNews = api.get(subject);
// 获取指定条件的列表
List<News> newsList = api.find(type);
// 获取所有
List<News> all = api.findAll();
1.2.2. 日志
日志使用我们封装的CtpLogFactory,变量名统一使用LOG
import org.apache.commons.logging.Log;
import com.seeyon.ctp.common.log.CtpLogFactory;
public class NewsApiImpl {
private final Log LOG = CtpLogFactory.getLog(NewsApiImpl.class);
}
分页:迁移代码继续使用Pagination,CTP的新实现继续沿用Flipinfo,不再引入新的分页对象。
当前用户:区分用户的所有接口实现均不允许内部获取CurrentUser,必须显性传入memberId。
1.2.3. 参数设计规范
参数名应能从字面表明参数的作用
参数对象不允许使用基础数据类型,必须使用其封装类作为参数类型。如参数需要传Integer类型的,不允许使用int。
接口中写明的参数在使用时都必须有用,不允许出现其中某个参数为特定值时,可以忽略另一个参数的情况。如有特殊的需求,建议拆分接口为多个。如findBulletinTypes(int spaceType, long accountId)方法,在spaceType为-1时,不管accountId为任何值,都会返回所有的公告板块列表。这样的接口就会产生使用上的歧义,建议增加一个接口findAllBulletinTypes()取代这种情况。
8种基础数据类型的参数,使用封装类型
// 正确 void methodOk(Long l,Integer i,Boolean b){ } // 错误 void methodBad(long l,int i,boolean b){ }
接口方法参数和返回值不允许传递第三方组件的实体,如JSONObject、JAXP对象,以避免无谓的第三方组件替换和升级代价。
1.2.4. 模块管理
模块使用唯一的int值进行标识。
内部模块必须在
com.seeyon.ctp.common.constants.ApplicationCategoryEnum
枚举中定义。合并com.seeyon.ctp.common.ModuleType,统一使用com.seeyon.ctp.common.constants.ApplicationCategoryEnum
外部扩展应用可以不在枚举中添加。
0-10000为内部模块预留编号,外部扩展应用不允许使用。
方法定义和调用方式如下,注意,参数必须为 int ,参数名称必须为 category
// 正确定义 List<AppLog> getAllAppLogs(int category); // 错误定义 List<AppLog> getAllAppLogs(ApplicationCategoryEnum appEnum); // 正确调用 getAllAppLogs(ApplicationCategoryEnum.global.getKey()); getAllAppLogs(10021); // 错误调用 getAllAppLogs(0);
目前的模块定义如下
标识 | 名称 | 备注 |
---|---|---|
global | 0 | 全局 |
collaboration | 1 | 协同应用 |
form | 2 | 表单 |
doc | 3 | 知识管理 |
edoc | 4 | 公文 |
plan | 5 | 计划 |
meeting | 6 | 会议 |
bulletin | 7 | 公告 |
news | 8 | 新闻 |
bbs | 9 | 讨论 |
inquiry | 10 | 调查 |
calendar | 11 | 日程事件 |
12 | 邮件 | |
organization | 13 | 组织模型 |
project | 14 | 项目 |
relateMember | 15 | 关联人员 |
exchange | 16 | 交换 |
hr | 17 | 人力资源 |
blog | 18 | 博客 |
edocSend | 19 | 发文 |
edocRec | 20 | 收文 |
edocSign | 21 | 签报 |
exSend | 22 | 待发送公文 |
exSign | 23 | 待签收公文 |
edocRegister | 24 | 待登记公文 |
communication | 25 | 在线交流 |
office | 26 | 综合办公 |
agent | 27 | 代理设置 |
modifyPassword | 28 | 密码修改 |
meetingroom | 29 | 会议室 |
taskManage | 30 | 任务管理 |
guestbook | 31 | 留言板 |
info | 32 | 信息报送 |
infoStat | 33 | 信息报送统计 |
edocRecDistribute | 34 | 收文分发 |
notice | 35 | 公示板 |
attendance | 36 | 签到 |
mobileAppMgrForHTML5 | 37 | 移动应用接入-html5应用包 |
sapPlugin | 38 | sap插件 |
ThirdPartyIntegration | 39 | 第三方整合 |
1.2.5. Maven
价值
控制内部模块间依赖。
管理第三方类库,避免版本冲突。
自动化构建。
引入Maven管理依赖,文件放置遵从Maven的标准: Eclipse工程文件(.project、.classpath)请勿提交到svn上。
目录结构
http://10.3.4.218:6666/
└── svn
└── v5
├── ctp Ctp基础框架,包括原core和T1
│ └── trunk
│ ├── ctp-core
│ │ └── pom.xml
│ ├── ctp-common 平台基础组件,原ctp_t1
│ │ └── pom.xml
│ └── pom.xml
├── ctp-organization 组织模型,原ctp_t2
│ └── trunk
│ └── pom.xml
├── ctp-portal 门户,原ctp_t3
│ └── trunk
│ └── pom.xml
├── ctp-workflow 工作流,原ctp_t4
│ └── trunk
│ └── pom.xml
├── ctp-report 报表
│ └── trunk
│ └── pom.xml
├── ctp-form 表单,原ctp_t5
│ └── trunk
│ └── pom.xml
├── ctp-index 全文检索引擎
│ └── trunk
│ └── pom.xml
├── apps-api 解耦后的开放接口
│ └── trunk
│ └── pom.xml
├── apps-common 应用基础组件
│ └── trunk
│ └── pom.xml
├── apps-collaboration 协同应用,原f1
│ └── trunk
│ └── pom.xml
├── apps-task 任务
│ └── trunk
│ └── pom.xml
├── apps-calendar 日程
│ └── trunk
│ └── pom.xml
├── apps-plan 计划
│ └── trunk
│ └── pom.xml
├── apps-project 项目
│ └── trunk
│ └── pom.xml
├── apps-f7
│ └── trunk
│ ├── apps-edoc 公文
│ │ └── pom.xml
│ ├── apps-exchange 公文交换
│ │ └── pom.xml
│ └── pom.xml
├── apps-colcube 协同立方
│ └── trunk
│ └── pom.xml
├── apps-performancereport
│ └── trunk
│ └── pom.xml
├── apps-meeting 会议
│ └── trunk
│ └── pom.xml
├── apps-meetingroom 会议室
│ └── trunk
│ └── pom.xml
├── apps-meetingsummary
│ └── trunk
│ └── pom.xml
├── apps-office
│ └── trunk
│ └── pom.xml
├── apps-pubresource
│ └── trunk
│ └── pom.xml
├── apps-index 全文检索应用
│ └── trunk
│ └── pom.xml
├── apps-blog
│ └── trunk
│ └── pom.xml
├── apps-doc 文档中心
│ └── trunk
│ └── pom.xml
├── apps-rss
│ └── trunk
│ └── pom.xml
├── apps-uc
│ └── trunk
│ └── pom.xml
├── apps-addressbook 通讯录
│ └── trunk
│ └── pom.xml
├── apps-bbs BBS
│ └── trunk
│ └── pom.xml
├── apps-bulletin 公告
│ └── trunk
│ └── pom.xml
├── apps-inquery 调查
│ └── trunk
│ └── pom.xml
├── apps-webmail
│ └── trunk
│ └── pom.xml
└── apps-hr
└── trunk
└── pom.xml
根据发布要求,支持两种目录结构的Project:
需要单独发布的模块使用自己的trunk,可单独打Tag和Branch,如apps-collaboration。
多个小模块可使用同一trunk,合并版本发布,采用多模块目录结构,如上例中的ctp-core和ctp-common。
标准Project目录结构
apps-xxx/ http://10.3.4.218:6666/svn/v5/apps-xxx/trunk
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── seeyon
│ │ ├── resources
│ │ └── webapp
│ │ ├── WEB-INF
│ │ │ └── web.xml
│ │ └── index.jsp
│ └── test
│ ├── java
│ │ └── com
│ │ └── seeyon
│ └── resources
└── pom.xml
分模块目录结构
apps-xxx/ http://10.3.4.218:6666/svn/v5/apps-xxx/trunk
├── apps-news
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ └── seeyon
│ │ │ ├── resources
│ │ │ └── webapp
│ │ │ ├── WEB-INF
│ │ │ │ └── web.xml
│ │ │ └── index.jsp
│ │ └── test
│ │ ├── java
│ │ │ └── com
│ │ │ └── seeyon
│ │ └── resources
│ └── pom.xml
├── apps-bbs
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ └── seeyon
│ │ │ ├── resources
│ │ │ └── webapp
│ │ │ ├── WEB-INF
│ │ │ │ └── web.xml
│ │ │ └── index.jsp
│ │ └── test
│ │ ├── java
│ │ │ └── com
│ │ │ └── seeyon
│ │ └── resources
│ └── pom.xml
└── pom.xml
解耦后工程代码结构如下
API调用
应用调用其它模块的API时,请使用依赖注入获取实例。
因为应用解耦的控制,不确保API有实现,所以必须在调用前判断API是否为null,如
private NewsApi newsApi;
public void foo(){
if(newsApi!=null){
newsApi.doSth();
}
}
在我们已实现的代码中,大家经常使用hasPlugin来替代判null,但这有一个前提条件,你必须清楚这个API是属于哪个插件的,而且也不排除你写错了插件Id的可能性。
另外,API层的价值在于,二次开发可以用自己的实现完整的替换我们的标准实现,如果它的插件Id不是你所判断的那个呢!
版本管理
遵从语义化版本规范,jar的命名规则为
seeyon-{分类}-{模块名称}-{主版本}.{次版本}.{增量版本}-{里程碑版本}.jar
分类取值为ctp、apps、api
模块名称如core、collaboration
如 seeyon-ctp-core-1.1.12-alpha-1.jar
- 除非发生重大架构变更,今后很长一段时间,我们的主版本号均使用1,如
seeyon-ctp-core-1.2.1
- 增加了新特性次改变版本号。
- 进行了重大Bug修复时改变增量版本号。
- 里程碑版本如 alpha-1、beta-2
- 版本号记录在meta中,release时去除jar的版本号 示例如下
seeyon-ctp-common
seeyon-ctp-organization
seeyon-ctp-portal
seeyon-ctp-workflow
seeyon-ctp-report
seeyon-apps-collaboration
seeyon-apps-news
seeyon-apps-meeting
POM
值域 | 取值 | 备注 |
---|---|---|
Group Id | com.seeyon.{category} | category取值范围为ctp、apps、api,如com.seeyon.ctp、com.seeyon.apps、com.seeyon.api |
Artifact Id | {category}-{moduleName} | category同上,如ctp-core、apps-collaboration、apps-news |
Version | - | |
Packaging | jar | - |
产品部署包版本
- ftp目录:productPack/v5/6.0/
- 文件名 : SVN版本号+产品内容
1.2.6. 文档规范
面向开发、测试和维护人员。
详细的文档帮助开发人员快速学习,降低解释成本。
详细的文档有助于单元测试建立用例,保证代码质量。
详细的文档有助于后续的维护人员管理。
使用Markdown作为设计和开发手册文档工具。 使用javadoc编写API文档,文档可以使用Markdown语法,。
- 文档在Interface中编写,Impl不需重复。
- 需写清楚场景
/**
* <pre>
* 得到我能访问的组
* 正常情况:
* 1、普通用户包括(前提是这个单位下的组):
* 1.1 我建的私有组
* 1.2 我是成员或关联人员系统组
* 1.3 公开范围有我的系统组(单位、集团、项目)
* <del>1.4 我是部门管理员的部门系统组</del>
*
* 2、单位管理员:
* 2.1 这个单位所有的单位系统组(不包括部门组)
* 2.2 集团公开范围有这个单位的系统组
*
* 3、集团、审计、系统管理员管理员
* 3.1 所有的集团系统组
* 3.2 这个单位的系统组
* <del>2. 公开范围有我的系统组</del>
*
* 4.其它:
* 4.1 看到集团组的前提是单位在集团树下
* </pre>
*
*
* @param memberId 人员id
* @param accountId 单位id,每次都返回集团组
* @return 组实体列表
* @throws BusinessException
*/
List<V3xOrgTeam> getTeamsByMember(Long memberId, Long accountId) throws BusinessException;
- PO的文档统一写在setter方法上,getter不用赘述。
- 善用@link
/**
* 按Id取岗位。 如果id为空或-1,则返回未分配岗位(id为{@link com.seeyon..organization.domain.OrgEntity#DEFAULT_NULL_ID 空})。
*
* @param id
* 岗位Id
* @return 岗位实体
* @throws BusinessException
*/
V3xOrgPost getPostById(Long id) throws BusinessException;
- 参数和返回值要写清楚各种取值的含义
/**
* 获取岗位下的人员,支持标准岗
* <pre>
* /--- PostId --|-- accountId --|--------------- 返回值 ------------/
* | 标准岗 | null/集团ID | 全集团所有单位引用自建岗下的人员 |
* | 标准岗 | 单位ID | 指定单位引用自建岗下的人员 |
* | 单位自建岗| 此参数被忽略| 指定单位引用自建岗下的人员 |
* </pre>
*
* @param postId
* @param accountId
* @return
* @throws BusinessException
*/
public List<V3xOrgMember> getMembersByPost(Long postId, Long accountId) throws BusinessException;
- 后续追加的接口和方法要写清楚支持的版本
@since 5.0Sp2
@since 5.1Sp1 m201411
支持并强烈建议使用Markdown语法书写javadoc
/** * 获取岗位下的人员,支持集团基准岗,限制单位可见范围 * * - 首先 * - 其次 * * ```java * // 示例代码 * OrgManager orgManager; * orgManager.getMembersByPost4Access(); *
- | PostId | accountId | 返回值 |
- |-------------------------|---------------|-------------------------------------|
- | 标准岗 | null/集团ID | 全集团所有单位引用自建岗下的人员 |
- | 标准岗 | 单位ID | 指定单位引用自建岗下的人员 |
- | 单位自建岗 | 此参数被忽略| 指定单位引用自建岗下的人员 |
- @param postId 岗位id
- @param accountId 当前登录者的当前登录单位id
- @return
- @throws BusinessException
*/
List
getMembersByPost4Access(Long postId, Long accountId) throws BusinessException; ```
使用中文标点符号。
1.3. 插件启停用监听
解耦以后,为支持插件的灵活控制,允许在运行期改变插件的启用状态。
这样就带来了一个问题,以公告为例,如果在公告插件未启用的状态下新建单位,公告板块等信息无法进行初始化,此时就需要我们在公告插件启用时进行补偿。
为此,平台提供了com.seeyon.ctp.common.plugin.PluginChangeEvent插件状态变更事件,便于应用监听进行补偿。
应用可以实现一个监听器,例如:
import com.seeyon.ctp.common.plugin.PluginChangeEvent;
import com.seeyon.ctp.util.annotation.ListenEvent;
public class BbsPluginChangeListener{
@ListenEvent(event = PluginAddEvent.class)
public void onPluginAdd(PluginAddEvent evt) {
Set<String> plugins = evt.getPlugins();
if (Strings.isNotEmpty(plugins) && plugins.contains("bbs")) {
log.info("bbs插件补偿");
try {
// 遍历各单位,确定是否未初始化板块信息;如未初始化,则进行补偿。
List<V3xOrgAccount> allAccounts = orgManager.getAllAccounts();
if (Strings.isNotEmpty(allAccounts)) {
for (V3xOrgAccount account : allAccounts) {
if (!account.isGroup()) {
bbsBoardManager.initBbsBoard(account.getId());
}
}
}
} catch (Exception e) {
log.error("bbs插件补偿:", e);
}
}
}
}
补偿列表
插件 | 补偿点 |
---|---|
news | 初始化新闻板块 |
bulletin | 初始化公告板块 |
inquiry | 初始化调查板块 |
bbs | 初始化讨论板块 |
doc | 添加单位系统文档库 |
office | 初始化综合办公 |
hr | 初始化HR数据 |
微协同补偿 | |
colcube | 新增协同立方指标 |
show | 初始化秀 |
project | 初始化关联项目 |
collaboration | 初始化系统预置模板分类 |
edoc | 初始化公文元素、节点权限、常用语 |
edoc | 初始化文单等 |
infoopen | 初始化信息报送 |
meeting | 初始化会议资源、会议用品 |
portal | 初始化单位空间 |
1.4. MVC开发规范
bean的id一定要使用接口的名称,首字母小写,简化注入,便于调用。
不要使用Spring的init-method,请继承com.seeyon.ctp.common.AbstractSystemInitializer,在initialize方法中进行bean的初始化操作。
<!-- 这样是不允许的!!! --> <bean id="iframeSection" class="com.seeyon.ctp.portal.section.bridge.IframeSection" init-method="init"/>
明确MVC各层职责,不能错误使用。
例如Manager不能替代Dao的职责。
HTTPRequest、HTTPResponse和HTTPSession只能出现在Controller,不允许传递到Manager层。
Controller层不允许编写业务逻辑,业务逻辑由Manager层处理。
Controller层的一个方法只能调用Manager层中的一个方法,目的是为了确保统一事务。
错误:
public class CollaborationController { private CollaborationManager collaborationManager; private AttachmentManager attachementManager; public ModelAndView newCol(){ collaborationManager.save(...); attachementManager.save(...); } } public class CollaborationManagerImpl implements CollaborationManager{ public void save(){ ... } }
正确:
public class CollaborationController { private CollaborationManager collaborationManager; public ModelAndView newCol(){ collaborationManager.save(...); } } public class CollaborationManagerImpl implements CollaborationManager{ private AttachmentManager attachementManager; public void save(){ ... attachementManager.save(...); } }
Controller职责不宜过多,原则上一个Controller只封装一个完整业务逻辑(CRUD)。
Manager层中分简单复杂(或大小)方法,之间可以互相调用,目的是完成完整的业务逻辑。(Manager本身就一层,并非像图中的三层,图中体现的是跨模块的Manager调用。)
Manager和Dao层遵循接口开发要求,例如:Manager、ManagerImpl、Dao,DaoImpl。
所有的异常需要抛到Manager层,由Manager层统一Catch后包装为BusinessException抛出。
Manager的方法不允许抛出BusinessException之外的异常。
建议Manager的所有方法都抛出BusinessException,但不作硬性要求(初期不抛,后续版本抛出异常会影响到调用代码);
Dao不能省略,必须定义。
Dao层支持Hibernate和JDBC两种方式。
原则上只允许使用Hibernate模式访问数据库。调用JDBC需评审并备案。
只有Dao层能够书写HQL和SQL语句,Controller和Manager层不允许出现SQL。
各层之间数据传递规范,PO与对应表字段一一对应,不允许增加额外的属性,不允许有业务逻辑;
PO需继承BasePO,BasePO中已实现toXml、toJSON,出于性能考虑可以覆盖实现。
C层向V层只能返回VO对象,VO对象为类Map的结构。对于简单情况可以用PO代替VO。
命名规范:XXX模块对应的MVC、DAO对应各层的命名规范为 XXXController、XXXManager、XXXDao、XXXPO、spring-XXX-controller.xml、spring-XXX-manager.xml、xxx.hbm.xml
基于接口编码,Manager和Dao必须定义接口,一个接口一个实现类。
应用不允许自己调用Log4j,相应的日志由框架统一使用AOP记录。
按模块和功能划分package,原则上一个package不能超过10对类(接口+实现)。
1.4.1. 工具规范
- 插件化
框架插件机制提供了全面的插件扩展开发支持,包括插件启动、插件自定义配置文件、i18n、spring和hibernate PO加载等
1.4.2. 插件规范
插件相关的配置文件存放于cfgHome/plugin目录中,文件夹名称需与插件id保持一致。其中包括插件基础配置、插件自定义配置文件、插件i18n国际化配置、
spring配置文件
插件代码包必须在“com.seeyon.apps.插件id”,比如com.seeyon.apps.samples
插件的PO对象和配置文件必须在“com.seeyon.apps.插件id.po”下,可以带子包用于配置自定义HQL的hbm文件
1.4.3. 插件基础配置
插件基础配置文件位于“cfgHome/plugin/插件id”目录下,文件名必须为pluginCfg.xml,例子如下:
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<id>samples</id>
<name>例子模块</name>
<category>10000</category>
</plugin>
TAG | 备注 |
---|---|
id | 插件id, |
name | 插件名称 |
category | 插件类别,产品插件分类从10000至19999,客开插件从20000开始 |
1.4.4. 插件启动初始化
系统启动时可以进行插件的启动和系统停止时的销毁操作,需继承自com.seeyon.ctp.common.AbstractSystemInitializer,并在spring中注册即可,例子代码如下
import com.seeyon.ctp.common.AbstractSystemInitializer;
public class SamplesInitializer extends AbstractSystemInitializer {
public void destroy() {
System.out.println("销毁Samples模块");
}
public void initialize() {
System.out.println("初始化Samples模块");
}
}
Spring注册例子如下(插件相关spring配置文件命名规则为spring-插件id-plugin.xml):
<bean name="samples_systemInitializer" class="com.seeyon.apps.samples.SamplesInitializer">
<property name="sortOrder">
<value>7</value>
</property>
</bean>
<bean name="samples_systemInitializer2" class="com.seeyon.apps.samples.SamplesInitializer2">
<property name="sortOrder">
<value>2</value>
</property>
</bean>
其中sortOrder为插件初始化顺序从小到大执行,如果没有配置该属性,则优先执行,在没有配置的多个初始化类之间顺序不定
1.4.5. 插件自定义配置文件
插件自定义配置文件位于“cfgHome/plugin/插件id”目录下,文件名必须为pluginProperties.xml,例子如下:
<?xml version="1.0" encoding="utf-8"?>
<ctpConfig>
<samples>
<test name="test配置">插件配置测试</test>
</samples>
</ctpConfig>
该例中可通过AppContext.getSystemProperty("samples.test.name")取得“test配置”,AppContext.getSystemProperty("samples.test")取得“插件配置测试”
注意:属性key必须以插件id开始
1.4.6. 插件国际化配置文件
插件国际化配置文件位于“cfgHome/plugin/插件id/i18n”目录下,按照JDK国际化文件的配置规则命名,可采用多组文件。使用方式请参考i18n国际化组件部分说明。
注意:属性key必须以插件id开始
默认插件国际化资源配置只能后台使用(ResourceUtil接口,国际化页面function - ctp:i18n),前端javascript无法使用,如果要开放给前端,可以在插件的i18n
文件夹根下创建名为“export_to_js.xml”导出为javascript的$.i18n函数可使用的国际化资源,具体请参考国际化一节
如何转换V5的国际化文件?
1.下载可读V5国际化语言包(下载请鼠标右键-链接地址另存为): A8-V5-6.0SP1国际化语言包 A8-V5-6.1国际化语言包
2.下载【可读V5国际化语言包】后,可以根据意思转换为其他语言包,注意转换后也要保持与【可读V5国际化语言包】文件路径一致。
3.新语言包用JDK的native2ascii.exe工具再做一次Unicode编码转换。
命令示例:native2ascii.exe common_zh_CN.properties
1.4.7. 插件spring配置
插件spring配置文件位于“cfgHome/plugin/插件id/spring”目录下,可有多个spring配置文件,按照框架规范命名包括:
- spring-插件id-controller.xml controller配置,url需要形如“/插件id/xxx.do”
- spring-插件id-manager.xml manager配置,bean id需要形如“插件id_mgr_xxx”
- spring-插件id-dao.xml dao配置,bean id需要形如“插件id_dao_xxx”
1.4.8. 插件hibernatePO加载
插件的PO对象和配置文件必须在“com.seeyon.apps.插件id.po”下,可以带子包用于配置自定义HQL的hbm文件
1.5. 一致性要求
开发工具 建议使用Eclipse idea绝对禁止使用破解版
如果觉得VSCode顺手,也可以用它
设计工具
框图可使用Visio或直接使用Powerpoint,不做特别限制,但必须提交底稿。
UML相关(用例、时序图、类图)推荐使用EA。
数据建模工具
使用PowerDesigner
JDK
使用JDK 1.8,编译级别设为1.6。
8.0以后允许使用1.8语法
第三方组件引入的原则
注意开源组件的版权协议,避免侵权。
已有同类组件的,如果不是在性能和特性上有绝对的优势,不引入。
不引入三年内频繁上报高危安全问题的组件,请查询https://www.cvedetails.com。
不引入多年未维护社区不活跃的第三方组件。
XML解析器
只允许使用dom4j
对于将XML解析为Bean,建议使用Commons Digester。
XML配置解析建议使用Commons Configuration。
服务器端JSON解析
统一使用框架提供的com.seeyon.ctp.util.json.JSONUtil,不要自己去封装或调用fastjson或Jackson,不允许使用org.json或net.sf.json,以便框架统一升级替换。
当前用户 不允许应用自行创建User对象设置到当前用户,请统一使用UserUtil的build方法创建,setCurrentUser方法设置。
模板引擎
请勿自行引入freemarker或velocity等,统一使用smarty。
字符串处理 请使用com.seeyon.ctp.util.Strings的一系列工具方法:isBlank、isEmpty、toHTML、escapeJavascript等。
日期处理 统一使用com.seeyon.ctp.util.Datetimes,因为框架的实现带了多时区的处理,绝对禁止使用DateFormat。
// Bad DateFormat df = new SimpleDateFormat("yyyy-mm-dd"); String sDate = df.format(date); Date date = df.parse("2018-10-10"); // Good String sDate =Datetimes.formatDatetime(date); Date date = Datetimes.parseDate("2018-10-10");
无头浏览器 统一使用phantomjs。
HttpClient 统一使用平台提供的HttpClientUtil。
Content Type Controller输出js、json或css时,必须使用准确的ContentType,切勿图省事使用text/html。