一种基于ES2SH框架的Java EE应用架构
2018-11-16欧阳宏基宋笑雪
欧阳宏基, 李 红,2, 宋笑雪
(1. 咸阳师范学院 计算机学院, 陕西 咸阳 712000; 2. 西安电子科技大学 智能感知与图像理解教育部重点实验室, 西安 710071)
0 引 言
基于框架的软件复用是目前应用系统开发所采用的主流技术,框架提供了良好的基础结构,只要按照框架的约定进行“填充式”开发即可获得稳定性高、扩展性强的软件成品[1-2]。传统Java EE应用以企业级(Enterprise JavaBean,EJB)框架为主进行构建,具有依赖性高、开发难度大、交互体验差、部署复杂、难于维护等缺点[3-4]。针对这些问题,开源的轻量级框架应用而生,其中以Struts、Spring、Hibernate、Mybatis等最具代表性。轻量级框架具有无侵入式、应用编程接口(Application Programming Interface,API)易于使用、配置简单、开源等优点,允许使用框架的部分技术进行系统局部功能开发;同时不同框架之间允许集成,可充分发挥每种框架的优势而完成系统整体功能的开发。
本文研究了目前主流的Easy UI、Struts2、Spring、Hibernate等框架。在分析它们各自优点的基础上,提出基于ES2SH框架的Java EE应用架构。该架构利用Easy UI创建表示层,Struts2创建控制层,采用JavaScript对象标记(JavaScript Object Notation ,JSON)[5]作为表示层与控制层之间传输数据的格式。利用Hibernate创建数据持久层,通过Spring管理控制层与业务层、业务层与持久层之间组件的依赖关系,采用可扩展标记语言(eXtensible Markup Language,XML)文件+注解的方式进行Struts2、Spring和Hibernate的集成。将该架构应用到高等院校目标考核管理系统的设计与实施中,详细描述了该架构应用的关键技术,为Java EE应用的开发提供了一定的参考价值。
1 相关技术
Easy UI是支持超文本标记语言(HyperText Markup Language,HTML)5标准的JavaScript框架,提供了丰富的用户接口(User Interface,UI)组件支持Web应用的富客户端效果,支持异步的 JavaScript 和 XML (Asynchronous JavaScript and XML,Ajax)请求与响应,兼容JSON格式的数据传输,具有良好的浏览器兼容性、开源、体积小、易于掌握等优点[6]。Struts2是 Web开发框架,它利用核心控制器StrutsPrepareAndExecutorFilter和配置文件,完成了视图、Action控制器和模型之间的分离[7-8]。Spring 是一个基于控制反转(Inversion of Control,IOC)容器和面向方面编程(Aspect Oriented Programming,AOP)编程的轻量级框架。其中,IOC容器是一个Bean工厂,用来创建对象并管理对象之间的依赖关系[9];AOP能够将系统业务逻辑与公共服务模块相分离,以横切关注点集中管理公共服务模块[10],在系统运行时通过动态代理以正交方式织入业务逻辑。Hibernate是对Java数据库连接(Java DataBase Connectivity,JDBC)的轻量级封装,能够完成实体对象与数据库表、对象属性与表字段、对象关联与表关联之间的转换。同时提供了功能强大的Hibernate查询语言(Hibernate Query Language,HQL),允许开发人员以面向对象方式操作关系型数据库[11-12]。注解是Java的一种元数据,它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,主要用来生成文档、跟踪代码的依赖性从而代替配置文件、编译时语法检查等[13]。
2 ES2SH架构
2.1 ES2SH架构模型
根据分层模型,基于上述框架建立满足模型视图控制器(Model View Controller,MVC)模式[14]的5层Java EE应用架构模型,如图1所示。表示层由Easy UI、Java服务器端页面(Java Server Page,JSP)和Struts2标签组成,通过表示层发送的请求统一由控制层的核心控制器接收,核心控制器根据配置文件将请求转发到某个具体的Action组件。Action得到业务逻辑层的执行结果后,将要呈现给表示层的数据封装成JSON格式,通知核心过滤器调用视图响应用户请求。业务逻辑层通过Service接口向Action组件提供服务,具体的业务逻辑处理和计算由Service对象完成,Service对象由Spring IOC容器管理。业务逻辑运算过程中需要的持久化数据通过持久层获取。数据持久层由数据访问对象(Data Access Object,DAO)模式与Hibernate框架搭建,DAO接口定义持久化逻辑并作为业务逻辑层访问的入口,DAO对象封装Hibernate通过映射文件完成持久化对象(Persistent Object,PO)与数据存储层之间的交互,DAO对象由Spring IOC容器管理。数据存储层负责存储应用系统所需要长久保存的数据以及之间的关联关系,通过触发器和存储过程完成相关的汇总、统计计算。
图1 S2SH框架分层架构
从图1可以看出,ES2SH架构很好实现了各层的解耦合,层与层之间通过接口进行方法的调用,使得上层的实现不依赖于下层的变化而变化,很好地满足了面向对象设计的“开-闭”原则。同时,相邻层之间能直接“通信”,间接层之间不能直接“通信”,很好地满足了面向对象设计的“迪米特法则”。
2.2 ES2SH框架的集成
ES2SH框架的集成主要是Struts2、Spring和Hibernate这三者之间的集成。具体集成思路是:以Spring为核心,向下整合Hibernate完成数据持久化操作;向上整合Struts2完成控制逻辑,分离数据显示与业务处理。本文采用XML配置文件+注解的方式进行框架整合和系统开发,其中基础配置部分采用XML文件,包括Struts2、Spring、Hibernate的核心配置文件以及持久化类的映射文件。开发部分中的依赖关系采用注解,依赖关系体现在业务逻辑层对持久层的依赖、控制层对业务逻辑层的依赖。
2.2.1Spring与Hibernate整合
首先,定义Hibernate配置文件-Hibernate.config.xml。然后,在Spring配置文件中定义SessionFactory,在SessionFactory中配置数据库连接池,可以实现数据库连接对象的复用,提高访问效率。同时,把Hibernate.config.xml文件注入给Spring,通过LocalSessionFactory的configLocation属性用来读取Hibernate配置文件。在Spring配置文件中定义事务管理器,由Spring控制Hibernate的事务。持久化逻辑中不用考虑事务相关的代码,提高了开发效率。最后,在Spring配置文件中定义注解扫描,进行此项配置后,在应用开发部分就可采用注解方式进行整合。
2.2.2Spring与Struts2整合
Spring与Struts2框架集成有3种方案[15-16],本文采用将Struts2的Action组件纳入Spring IOC容器管理的方法进行集成。因为该方法充分利用了IOC依赖注入的原理,通过IOC容器获取Action组件,避免了Struts2 API与Spring API的紧密耦合。这两者框架的整合不需要在Spring配置文件中进行配置,只需导入Struts2框架提供支持Spring 的插件包,并在Action类中加入@Controller和@Scope注解即可。
2.2.3使用的注解
开发部分用到的注解如表1所示,使用注解后,不用在 XML配置文件中显式使用
3 ES2SH架构在目标考核管理系统中的应用
3.1 目标考核系统简介
根据上述ES2SH框架的Java EE架构和整合方法,设计并实现了咸阳师范学院目标考核管理系统。目标考核系统是采用计算机与网络技术所实现的目标考核工作的管理信息系统。该系统的主要功能是:学校相关职能部门在发展规划处的领导下,按年度、分指标对二级学院下达目标责任;二级学院教学秘书、各教师、辅导员等在自然年度内按指标要求,录入相关任务完成的具体信息;各职能部门在对录入的信息审核后,根据指标分值和完成情况计算各二级学院的得分情况,根据得分高低来判断各二级学院本年度任务完成的情况,为本年度的奖惩和来年目标任务的下达提供一定的参考。通过该系统体现了目标考核工作的公平、客观、公开、反馈、严格、奖惩等原则,对全校教职工的工作具有良好的引导和激励作用,促进了各项指标的如期实现。系统角色包括教师、各二级学院管理员、二级学院院长、各职能部门管理员、各职能部门领导、校领导和系统管理员。功能模块如图2所示。
表1 用到的注解
图2 目标考核系统功能模块
3.2 领域模型设计
领域模型设计主要是从面向对象角度,根据需求分析提取出系统的实体类、实体类之间的关系、业务对象和值对象。Hibernate会根据映射文件将实体类及其关系自动生成数据库表,因此领域模型设计不再以传统数据库E-R设计为主。业务对象和值对象与数据库无关,前者负责业务计算,后者负责在持久层与业务层、控制层与表示层之间传递数据。值对象中的属性可能是某个或几个实体类属性的子集,这取决于业务层计算和表示层数据显示的需要。 本系统设计的实体类一共有52个,分别表示用户、职能部门账户、角色、权限、教材、专著、科研项目、科研论文、科研奖励、部门、岗位、课程、班级、综合指标、业绩指标、指标下达、指标完成和指标得分等。鉴于篇幅限制,以指标管理模块的综合指标为例,实体类及其联系如图3所示。IndexLevelOne表示1级指标,IndexLevelTwo表示2级指标,IndexLevelThree表示3级指标。1级与2级指标之间是一对多关系,2级与3级指标是一对多关系。AssignTask表示目标任务,由各职能部门给各二级学院下达目标任务时使用。TaskDetail表示目标任务完成的具体信息,由各二级学院教学秘书和教师角色按照3级指标提交任务完成信息时使用。TaskSubtotal表示审核通过的任务信息,审核通过后计算对应3级指标的实际得分和超额得分,供按2级指标和1级指标统计某个学院得分时使用。
图3 综合指标实体类关系
3.3 数据持久层设计
数据持久层负责实体类与数据表之间的映射,设计思路是为每个实体类定义对应的DAO接口和DAO实现类[17],DAO接口中封装该实体类的持久化逻辑并作为业务逻辑层的访问入口,DAO实现类封装Hibernate API具体实现持久化逻辑。通过DAO模式向业务逻辑层屏蔽数据持久化的细节,实现两者之间的解耦合。
由于每个实体类都具有保存、删除、修改、按id查找、查找全部等共性持久化逻辑,为了将这些共性操作合并,同时减少持久化代码的冗余度,设计了图4所示的持久层逻辑结构。其中BaseDao是采用泛型技术设
图4 持久层逻辑结构
计的所有DAO的父接口,其中封装了所有实体类共有的持久化方法。泛型类BaseDaoImp是BaseDao的实现类,通过关联的SessionFactory对象来获取Hibernate的Session对象实现相关的持久化逻辑;在框架集成阶段已经将SessionFactory对象配置好了,所以通过@Resource注解表明从IOC容器获取SessionFactory。具体DAO接口从BaseDao继承,具体DAO接口的实现类继承BaseDaoImp并添加@Repository注解。BaseDaoImp的部分核心代码如下所示:
@Transactional
public abstract class BaseDaoImp
@Resource
private SessionFactory sessionFactory;
private Class
public DaoSupportImp()
{ // 使用反射得到T的真实类型
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
// 获取第一个类型参数的真实类型
this.clazz = (Class
// 获取当前可用的Session
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
//所有实体类的共性保存方法
public void save(T entity) {
getSession().save(entity);
}
….}
3.4 业务层设计
业务层完成系统核心业务逻辑,通过Service接口向控制层提供调用接口,Service接口实现类依赖业务对象完成业务处理与数据计算,调用持久层的DAO接口完成业务逻辑中需要持久化的部分[18],如图5所示。
图5 业务层类图关系
以下是目标考核分值计算Service实现类的部分源码,其中业务方法computeExcessByIndexOne是获得某年某学院某个综合1级指标的超额得分。该方法通过@Resource注解从IOC容器获取所依赖的相关Service对象、DAO对象和业务对象(Business Object,BO),根据得到的1级指标完成得分和下达的目标任务得分,通过summaryProcessBO对象得到超额得分,并返回供控制层使用的值对象(Value Object,VO)。@Service注解表明该类对象是业务层对象,由Spring IOC容器管理。@Transactional注解表明该类中的方法启用事务操作。
@Transactional
@Service("summaryProcessService")
public class SummaryProcessServiceImp implements SummaryProcessService {
// 综合指标计算Service
@Resource
private TaskSubtotalService taskSubtotalService;
// 下达的目标任务Service
@Resource
private AssignTaskService assignTaskService;
//综合一级指标Service
@Resource
private IndexLevelOneService indexLevelOneService;
//综合一级指标Dao
@Resource
private IndexLevelOneDao levelOneDao;
//目标考核分值计算Dao
@Resource
private SummaryProcessDao summaryProcessDao;
//目标考核分值计算BO
@Resource
private SummaryProcessBO summaryProcessBO;
………………
public List
{
// 综合指标得分
float synthesizeScore;
// 超额得分
float excessScore;
List
// 查找某院系某年某个一级指标的得分
TaskSubtotalByIndexOneVO oneVO = taskSubtotalService.getSubtotalByYearDeptOneId(year, deptId, oneId);
// 某年某学院某个一级指标的得分
synthesizeScore = oneVO.getScore();
//获取某年某学院某个一级指标的任务下达分
float assignScore = assignTaskService.get-AssignTask-ScoreByAcceptDeptIndexOne(year, deptId, oneId);
//通过BO对象计算超额得分, 并修正综合指标得分存放到listVO中
excessScore=summaryProcessBO.getExcessScore(synthesizeScore, assignScore);
TaskSubtotalByIndexOneVO vo = new TaskSubtotal-ByIndexOneVO();
vo.setIndexOneId(oneId);
vo.setIndexOneName(oneVO.getIndexOneName());
vo.setDepartmentName(oneVO.getDepartmentName());
vo.setScore(excessScore);
listVo.add(vo);
return listVo;
}
3.5 控制层设计
控制层接收表示层的请求和数据,依据Struts2配置文件调用相关Action处理请求。所以配置文件和Action组件是该层设计的重点。本文将配置文件分为两种:核心配置文件(struts.xml)和各模块对应的配置文件。首先,引入struts2-json-plugin.jar包,在struts.xml中定义抽象package继承json-default,支持Action返回JSON格式数据到表示层,其他配置文件的package继承当前package。然后,在该package中配置自定义拦截器栈、默认Action、全局Result并引入各模块对应的配置文件。在自定义拦截器栈中新增4个拦截器,分别是权限验证拦截器、Session失效拦截器、日志记录拦截器和异常拦截器。最后,在其他配置文件中定义相关Action和表示层的映射关系。
由于每个模块在控制层都具备一些共同的操作,为了节省代码量,设计了如图6所示的Action组件类图。其中,BaseAction是每个模块对应Action的直接父类,它继承ActionSupport类,使当前Action具备数据校验、序列化、国际化和默认处理用户请求等功能,从而简化Action的开发。通过泛型ModelDriven接口获取表示层传递过来的数据,利用ModelDrivenInterceptor拦截器将这些参数封装成实体对象(属性Model表示)并压入ValueStack,便于Action调用。pageSize和pageNum用来表示分页操作的页数和页码。通过@Resource注解定义所有业务层对应的Service接口。在构造方法中通过反射获取属性Model对应的真实类型。具体某个模块对应的Action继承BaseAction后,加入@Controller和@Scope("protoType")注解并定义供表示层调用相关的方法。
图6 Action组件类图关系
3.6 表示层设计
表示层与用户进行数据交互,接收用户请求并响应。本系统中表示层主要由Struts 标签、JSP页面、Div、层叠样式表(Cascading Style Sheets,CSS) 和Easy UI构成,通过VO对象由表示层向控制层传递请求数据,采用JSON由控制层向表示层传递响应数据。这样做避免了显示逻辑与控制层Java代码的耦合,增强了表示层的重用性,提高了用户体验,便于后期测试和维护。以发展规划处管理员角色进入系统后所呈现的主页面为例,详细说明表示层的实现,如图7所示。
首先,创建JSP页面通过taglib指令引入Struts2标签库,通过Script标签引入Easy UI对应的JS文件。然后,利用CSS+Div进行页面布局,通过Easy UI标签提供数据的输入和显示。最后,创建JS文件完成对应Easy UI控件的编程,并以Ajax方式发送请求到控制层的Action。图7左侧是利用Div创建的用于显示登录用户权限的菜单,通过Struts2的〈s:iterator〉、〈s: property〉、〈s:if〉和〈s:else〉等标签动态的读取存放在Session中的用户权限信息,点击对应的权限后通过自定义的addPanel函数在主框架中添加一个页面,该面板中利用Easy UI的datagrid控件显示从控件层返回的JSON数据,流程如图8所示。显示效果如图7右侧部分,通过Ajax请求方式,实现了页面的局部刷新,提高了用户浏览体验。
图7 发展规划处管理员角色主页面
图8 Ajax方式发送请求的流程
4 结 语
本文对Easy UI、Struts2、Spring和Hibernate框架进行分析的基础上,提出了基于ES2SH框架的Java EE应用架构。以Spring为“桥梁”实现了架构的集成,详细描述了集成的原理和实现过程。将ES2SH架构应用于某学院目标考核管理系统的开发实践中,给出了各层的设计思想与核心代码。目前该系统已在学校各职能部门、二级学院和全体教师之间展开运行,全面提升了学校目标考核工作的信息化水平,取得了较好的满意度,表明本文所提出的Java EE架构切实有效。