葛 萌,黄素萍,欧阳宏基
(咸阳师范学院计算机学院,陕西 咸阳 712000)
框架是建立在面向对象程序设计语言、设计模式、反射机制以及算法基础上的一种“半成品”软件。开发人员按照既定流程和规则对框架预留的接口进行实现,即可高效地获得具有良好稳定性、扩展性和维护性的应用系统,所以基于框架进行设计是实现应用系统的首选方案。在Java Web领域,使用最为普遍的框架有Struts1、Struts2和Spring MVC。三者都是基于MVC设计模式,实现了模型、视图和控制器的解耦合[1]。Struts1是最早出现的Java Web框架,迅速占据了一定的市场份额。但由于Struts1与Servlet API耦合紧密,必须依赖Web容器进行调试;表示层与控制层之间的参数传递需要编写ActionForm[2];Action必须是单例且线程安全的,因此Struts1目前主要应用在一些系统的二次开发场景中,新系统的开发一般不再使用。Struts2是Struts的第二代产品,在Struts1的基础上融入WebWork思想,完全脱离了Servlet,增加了AOP、自定义数据类型转换、支持多视图类型等功能[3]。但在2013年和2017年爆发了远程代码执行的高危漏洞[4],对构建于Struts2的多个应用领域带来了不小的损失,市场逐渐丧失了对Struts2框架的信任。近几年,利用Spring MVC创建Java Web应用成为一种热门。
本文首先分析了Spring MVC框架的核心组件,研究了Spring MVC执行HTTP请求与响应的一般流程。然后从框架配置、控制器定义等方面阐述了Srping MVC的实现过程。最后从数据校验、全局异常处理、拦截器等方面描述了Spring MVC的解决方案,并结合目标考核管理系统实例给出了部分实现源码,为使用Spring MVC框架进行Java Web开发提供了一定帮助。
Spring MVC是Spring框架中用于创建Java Web应用的模块,构建在MVC设计模式基础之上,无需整合即可与Spring一同工作[5]。Spring MVC包括前端控制器、处理器映射器、处理器适配器、处理器、视图解析器和视图等6个核心组件[6-7],调用关系如图1所示。除了处理器和视图组件预留了接口,需要另行开发外,对其余组件,Spring MVC都提供了默认实现,只需进行配置即可使用。前端控制器(DispatcherServlet)负责接收用户请求、转发请求、填充请求执行结果到视图并响应请求。处理器映射器(HandlerMapping)存储了URL与Handler处理器的对应关系。处理器适配器负责调用Handler。Handler是具体执行请求的组件,返回ModelAndView。ModelAndView中封装了逻辑视图名和视图组件要显示的数据。视图解析器(ViewResolver)负责将逻辑视图名解析成具体的视图资源并填充需要显示的数据。视图是展示给用户显示的资源,Spring MVC支持JSP、FreeMarker、Velocity和XSLT等多种视图类型[8]。
图1 Spring MVC核心组件
Spring MVC执行一个HTTP请求的过程如图2所示。首先,Web容器按Spring MVC所接受的请求格式转发到前端控制器;前端控制器请求处理器映射器按照XML或注解的配置方式查找Handler,查找的结果是一个包含拦截器和Handler的HandlerExecutionChain;前端控制器调用处理器适配器去执行Handler,Handler执行完业务逻辑后返回ModelAndView。然后,前端控制器请求视图解析器进行视图解析,根据逻辑视图名解析成真实的视图。最后,前端控制器对视图解析器返回的View进行视图渲染形成真实的视图,并且将ModelAndView中的模型数据填充到Request域并响应用户请求。
图2 Spring MVC执行流程
本章以咸阳师范学院目标考核管理系统为背景,详细描述Spring MVC框架在其中的应用。目标考核管理系统是基于B/S架构、满足Java EE规范、多用户/多角色的管理信息系统,主要用于学校相关职能部门对各二级学院进行年度各项工作目标任务的下达、审核、汇总计算和二级学院进行目标任务核对、完成任务数据的录入与上传等。Spring MVC框架在系统中主要完成控制层的功能,具体实现过程如下描述。
Spring MVC框架的配置主要是完成前端控制器、处理器映射器、处理器适配器、视图解析器的配置。首先,在web.xml中完成前端控制器的配置,用来实例化前端控制器对象、加载Spring MVC的配置文件以及指定前端控制器接受URL请求的格式。具体配置代码请参阅文献[9]。
然后定义spring-mvc.xml文件,完成处理器映射器、处理器适配器、视图解析器的配置。配置方式有非注解和注解2种形式。Spring MVC自3.1版本后提供了注解支持的处理器适配器和处理器映射器,并在DispatcherServlet.properties文件中定义了对应的实现类,采用注解配置方式只需在spring-mvc.xml中启用注解驱动和扫描即可。配置视图解析器时需要定义视图资源真实路径的前缀和后缀,这样在ModelAndView中只需加入模块路径即可。同时为了不让DispatcherServlet拦截系统的静态资源(例如CSS、JS等),还需要配置静态资源的映射路径。具体配置代码请参阅文献[9]。
控制器(Controller)接受前端控制器分配的任务,执行业务逻辑并返回逻辑视图数据,是Java Web应用系统的核心组成部分。创建控制器有2种方法:一是通过实现Spring MVC的Controller接口;二是采用“非侵入+注解”的方式。第2种方式不但将Controller当作普通JavaBean对待,使控制器与Spring MVC API解耦合,而且在较大规模的应用中能够简化spring-mvc.xml配置文件的工作量,提高了开发效率和可维护性。用于实现控制器的相关注解如表1所示。
表1 实现控制器的相关注解
对应用系统中可能出现的异常进行处理是确保系统健壮性的有效手段[10]。基于Java的Web应用普遍采用分层模式进行系统设计,传统的方法是在持久层、业务层、控制层分别对异常进行捕获并处理。此种方法虽然处理过程简单,但是代码中会存在大量的try、catch语句,降低了程序的可读性和维护性[11]。Spring MVC提供了异常处理接口,采用“逐层上抛+集中处理”的全局异常处理方式,该方式能够避免传统异常处理中在各层进行过多的异常处理,处理过程如图3所示,持久层将异常抛给业务层,业务层将异常抛给控制器,控制器将异常抛给前端控制器,前端控制器将异常转发到异常处理组件进行集中处理,并将处理结果显示给客户端。
图3 全局异常处理模型
目标考核系统中的异常处理采用了Spring MVC的全局异常处理方式。具体实现过程是:
1)创建自定义异常类AssessmentException继承Exception,定义message属性表示具体异常信息,Exception及其子异常是非运行时异常,可通过throws声明抛出[12]。
2)在持久层DAO接口中的所有方法、业务层Service接口中的所有方法都声明抛出Exception异常。如果异常与持久化操作相关,则在DAO的实现类中创建AssessmentException对象并封装信息异常;如果异常与业务操作相关,则在Service的实现类中创建AeessmentException对象并封装信息异常,除此之外在Controller中创建AssessmentException对象并封装信息异常。通过实现HandlerExceptionResolver接口创建全局异常处理器AssessmentExceptionResolver,在resolveException方法中实现异常处理逻辑。
3)在spring-mvc.xml文件中通过
Spring MVC拦截器是AOP的具体实现。AOP提供了一种解决应用系统开发中分离关注点的途径,关注点是系统的某个特定业务功能(分为核心业务和辅助业务),任何一个系统都包括核心关注点和横切关注点[13-14]。目标考核系统中的核心关注点有教工信息管理、教学管理、科研管理、指标管理等。横切关注点包括权限验证、安全检查、日志操作、事务管理等。Spring MVC提供HandlerInterceptor接口作为实现AOP的途径,其中包括preHandle()、postHandle()和afterCompletion()这3个方法。preHandle在进入控制器之前执行,postHandle在进入控制器之后、返回ModelAndView之前执行,afterCompletion在控制器执行完成后执行。
以目标考核系统的权限验证为例进行说明。在BRAC模型[15]上定义教工账户类、部门账户类、角色类、权限类。系统所设计的登录帐户包括教师账户和部门账户2种类型,教师账户对应教师角色,具有教师信息管理模块的所有权限;部门账户对应教师以外的相应角色,具有教师信息管理模块以外的其他相应权限。这样设计的优点有2个:1)减少了教师账户的角色数量,从而也就减少了教师账户的权限数,便于操作与管理;2)部门账户不再和某个具体的教师关联,同一部门里的任何一个教工只要知道部门账户,就可以执行相关的功能,便于考核工作的开展。在执行某个控制器方法之前对当前登录帐户的权限进行验证,实现代码如下:
public classAuthorityInterceptor implements HandlerInterceptor
{ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
//从session中获取登录用户信息
Object user=request.getSession().getAttribute("user");
//获取访问当前控制器所对应的URL
String url=buildURL(request);
if(user==null){
if("user/userAction_login".startsWith(url))
//如果是去登录,就放行
return true;
else{
response.sendRedirect("login.jsp");
//如果不是去登录,就转到登录页面
return false;
}}
//如果已登录,就判断权限
else{
//同时对退出操作对应的Action也需要放行,因为退出没有在用户对应的权限中
if(StringUtil.isActionPathValid(url))
//如果是去登录,就放行
return true;
else{
if(user instanceof UserVO)
{
UserVO loginedUser=(UserVO)user;
if(loginedUser.hasAuthorityByUrl(url)){
//如果有权限,就放行
return true;
}else{
//如果没有权限,就转到提示页面
return false;}}
else{
DepartmentAccount loginedUser=(DepartmentAccount)user;
if(loginedUser.hasAuthorityByUrl(url)){
return true;
}else{
//如果没有权限,就转到提示页面
return false;
} }
}} }
Web应用中的数据校验是指在应用系统进行业务逻辑处理前,对视图组件传递来的数据进行有效性、合法性检查,将用户的误输入、恶意输入数据挡在系统之外[16]。目前常用的方式有客户端校验和服务器端校验。客户端校验通常是通过浏览器执行JavaScript代码,利用正则表达式来检查表单输入的数据是否满足一定的规则[17]。客户端校验具有实现简单、节省网络流量的优点,但是也具有校验规则复用性低、浏览器关闭对JavaScript支持下客户端校验失效等缺点。所以为了确保系统的安全性,兼顾多客户端类型,服务器端数据校验是应用系统必须解决的重要问题之一。
Spring MVC本身没有提供数据校验功能,但通过预留的接口使用Hibernate提供的validation框架进行服务器端数据校验,具体使用过程如下:
1)导入hibernate-validator.jar、jboss-logging.jar和validation-api.jar等文件,这些文件只和校验相关,与ORM无关。
2)在spring-mvc.xml中配置校验器,具体实现者是HibernateValidator,代码如下:
3)定义校验信息资源文件,其中key表示需要校验的属性,value为校验不通过的提示信息。将资源文件指定给步骤2中的校验器使用,并将校验器指定给处理器适配器。实现代码如下:
4)在实体类需要校验的属性前通过注解添加校验逻辑。
5)在控制器方法中需要校验的形参前后分别加入@Validated注解和BindingResult,如果校验不通过,BindingResult对象中就会得到校验信息资源文件中对应的value值,将value值存入Model对象,校验信息就可以传递到视图组件显示给用户。
本文提出了基于Spring MVC框架的Java Web应用开发方法,并成功应用到目标考核管理系统控制层的设计中。Spring MVC清晰地划分了系统的控制、业务和视图部分,各部分可以协同完成,提高了开发效率。Spring MVC的控制器可以定义为POJO类型,这种不依赖于第三方API的无侵入式设计理念提高了控制器的开发和测试效率。通过拦截器可以扩充应用系统的辅助关注点,简化AOP的实现。通过实现HandlerExceptionResolver接口可以将异常集中到某个地方统一处理,能够分离业务处理代码与异常处理代码。通过兼容Hibernate的校验框架可以方便服务器端的数据校验,为正确处理业务逻辑提供合法数据。Spring MVC是一款优秀、稳定的Web开发框架,可广泛应用于各行业领域的Java Web应用开发中。