一:背景
A8 是单体应用程序,并且功能众多。我们希望A8功能模块化,且模块具有自己发版版本。同时希望客开功能,插件化(以“可插拔”的形式,依托A8 运行)。
二:技术方案
我们采用A8 插件(A8 Plugin) 进行模块化开发,它在不增加额外的知识成本下,满足客开插件:自定义配置,国际化等功能。
1.A8 插件开发文档:
如图:
请各位开发:严格按照上述文档开发插件
2.文件部署规范
为了方便维护标准代码和客开代码,我们对这二者的部署做如下规范:
2.1 标准产品部署:沿用原始A8 部署规范
2.1.1 配置文档,文件存放路径:*\webapps\seeyon\WEB-INF\cfgHome\plugin\自定义文件夹*
如图:

2.1.2 jar 包发布位置:webapps\seeyon\WEB-INF\lib
如图:
2.2 客开插件:
2.2.1 jar 包部署位置: \webapps\seeyon\WEB-INF\lib\customer\xxx.jar
如图:
注意:框架代码:将自动扫描该插件包:所有jar 文件,并加载到jvm中
2.2.2 前端资源文件的部署: 如js,css,图片等: 部署目录是:\webapps\seeyon\customer\客户名称\,访问方式:seeyon\customer\客户名称\xx
如图:
2.2.3 jsp 页面:部署目录是:\webapps\seeyon\WEB-INF\jsp\customer\客户名称\,访问方式:customer\客户名称\xx
如下图:
后端代码:
public class AController extends BaseController { @Override public ModelAndView index(HttpServletRequest request, HttpServletResponse response) throws Exception { //jsp路径是:customer/demo/front/jsp/capRunningLog ModelAndView mav = new ModelAndView("customer/demo/capRunningLog"); //添加一个测试属性 mav.addObject("a", "123"); return mav; } }
2.2.4 spring 配置;
a.插件开发配置:
b. spring 配置部署:部署位置:\webapps\seeyon\WEB-INF\cfgHome\plugin\customer\客户名称\xxx
案例:我们以访问后台jsp页面,并且在jsp显示客开图片为例进行说明:
a.假设我们的客开插件名称为:demo 。jsp 文件保存在:\webapps\seeyon\WEB-INF\jsp\customer\demo\capRunningLog.jsp
b.需求: 我们希望能正确请求到上述jsp 页面,同时在jsp 页面上显示该插件下:\webapps\seeyon\customer\demo\img\img\123.png 文件
c.操作过程:
后端代码: 访问jsp 页面,请以:customer/客开插件名称/开头,如上述jsp 页面:customer/demo/capRunningLog
public class AController extends BaseController { @Override public ModelAndView index(HttpServletRequest request, HttpServletResponse response) throws Exception { //jsp路径是:customer/demo/capRunningLog ModelAndView mav = new ModelAndView("customer/demo/capRunningLog"); //添加一个测试属性 mav.addObject("a", "123"); return mav; } }
前端代码:
<%@ page contentType="text/html; charset=utf-8" isELIgnored="false" %>
<%@ include file="/WEB-INF/jsp/common/common.jsp" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${ctp:i18n('system.menuname.BusinessOrderPlatform')}|${ctp:i18n('system.menuname.BusinessMonitorCenter')}</title>
<!--引用标准产品文件 -->
<link href="${staticPath}/portal/icons/default/fonts/plane/iconfont.css${staticSuffix}" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="${staticPath}/common/cap4/bizconfig/css/monitor/capRunningLog.css${staticSuffix}" />
<script src="${staticPath}/common/cap4/common/underscore.js${staticSuffix}"></script>
<link rel="stylesheet" href="${staticPath}/common/cap4/bizconfig/jqSelect/jquery.searchableSelect.css${staticSuffix}" />
<script src="${staticPath}/common/cap4/bizconfig/jqSelect/jquery.searchableSelect.js${staticSuffix}"></script>
</head>
<body style="background-color:#F9F9F9;">
<div id="runningLog" class="table-wrap" style="display: none">
<div id="view" class="view_menu">
<div class="tab tab_biz div_active ready-cli" id="tab_biz">${ctp:i18n('cap.monitor.application')}</div>
<div class="tab tab_form" id="tab_form">${ctp:i18n('common.form.label')}</div>
</div>
<div class="panel-header">
<div style="min-width:1024px;">
<div class="cap-search cap-search-time">
<label for="">${ctp:i18n('common.option.time.label')}</label>
<span>
<input readonly="readonly" unselectable="on" id="beginoperatime" type="text" />
<i class="iconfont cap-icon-riqishijian start-time-operator"></i>
</span>
<p>-</p>
<span>
<input readonly="readonly" unselectable="on" id="endoperatime" type="text" />
<i class="iconfont cap-icon-riqishijian end-time-operator"></i>
</span>
</div>
<div class="cap-search cap-search-text">
<label for="">${ctp:i18n('common.opinion.member.label')}</label>
<input readonly="readonly" unselectable="on" id="operationName" type="text">
<input class="display_none" id="operationNameId" type="text">
<i class="iconfont cap-icon-xuanren j-xuanren-operator"></i>
</div>
<div class="cap-search cap-search-text">
<label for="">${ctp:i18n('common.opinion.describe.label')}</label>
<input id="description" type="text">
</div>
<div class="cap-search cap-search-text j-business">
<label for="">${ctp:i18n('cap.monitor.application')}</label>
<input id="bizName" type="text">
</div>
<br>
<div class="cap-search cap-search-time">
<label for="">${ctp:i18n('common.date.create.label')}</label>
<span>
<input readonly="readonly" unselectable="on" id="begincreatetime" type="text" />
<i class="iconfont cap-icon-riqishijian start-time-creator"></i>
</span>
<p>-</p>
<span>
<input readonly="readonly" unselectable="on" id="endcreatetime" type="text" />
<i class="iconfont cap-icon-riqishijian end-time-creator"></i>
</span>
</div>
<div class="cap-search cap-search-text">
<label for="">${ctp:i18n('common.creater.label')}</label>
<input readonly="readonly" unselectable="on" id="creatorName" type="text">
<input class="display_none" id="creatorNameId" type="text">
<i class="iconfont cap-icon-xuanren j-xuanren-createor"></i>
</div>
<div class="cap-search cap-search-select j-opType">
<label for="">${ctp:i18n('common.option.type.label')}</label>
<select class="j-select-value" id="operateType"></select>
</div>
<div class="cap-search cap-search-text j-formName" style="display: none;">
<label for="">${ctp:i18n('common.form.label')}</label>
<input id="formName" type="text">
</div>
<button id="doReset" class="cap-button" style="display: none"><i class=""></i><span>${ctp:i18n('form.reset.button.label')}</span></button>
<button id="doSearch" class="cap-button" style="display: none"><i class=""></i><span>${ctp:i18n('common.button.condition.search.label')}</span></button>
</div>
</div>
<div class="table-content" id="runningLogParent">
<table class="j-runnerTabel"></table>
</div>
</div>
<!--客开后端测试属性-->
hhaah: <label for="">${a}</label>
<!-- 客开静态资源引用-->
<img src="${staticPath}/customer/demo/front/img/123.png" />
</body>
</html>如上述代码:引用后端变量和静态资源
hhaah: <label for="">${a}</label>
<img src="${staticPath}/customer/demo/front/img/123.png" />
请注意:A8 原来提供的属性,现在均可用,如上述的:${staticPath} 表示/seeyon
d.效果
