MVC模式在Java Web应用程序中的实例分析
MVC在软件架构中是一种比较重要的架构思想,已经被广泛的应用在实际的java web项目开发中,我们所要了解和掌握的是mvc的架构思想和使用mvc模式来分析和解决问题的方法。当然相同或不同的项目都有各种分析解决的思路,这里采用一个应用struts2+hibernate+jsp的实例系统来进一步分析mvc模式。
以班级管理系统为例的架构图:
首先由用户通过VIEW层对系统进行业务请求:
classAdd.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>新增班级</title> <meta charset="utf-8" /> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <!-- Bootstrap Styles--> <link href="assets/css/bootstrap.css" rel="stylesheet" /> <!-- FontAwesome Styles--> <link href="assets/css/font-awesome.css" rel="stylesheet" /> <!-- Morris Chart Styles--> <link href="assets/js/morris/morris-0.4.3.min.css" rel="stylesheet" /> <!-- Custom Styles--> <link href="assets/css/custom-styles.css" rel="stylesheet" /> <!-- Google Fonts--> <link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css' /> <!-- TABLE STYLES--> <link href="assets/js/dataTables/dataTables.bootstrap.css" rel="stylesheet" /> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <div id="wrapper"> <nav class="navbar navbar-default top-navbar" role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".sidebar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="index.html"><i class="fa fa-comments"></i> <strong>班级管理系统</strong></a> </div> <!-- headtop最高的mune栏 --> <ul class="nav navbar-top-links navbar-right"> <!-- 第四个 --> <!-- /.dropdown --> <li class="dropdown"> <a class="dropdown-toggle" data-toggle="dropdown" href="#" aria-expanded="false"> <i class="fa fa-user fa-fw"></i> <i class="fa fa-caret-down"></i> </a> <ul class="dropdown-menu dropdown-user"> <li><a href="#"><i class="fa fa-user fa-fw"></i> User Profile</a></li> <li><a href="#"><i class="fa fa-gear fa-fw"></i> Settings</a></li> <li class="divider"></li> <li><a href="#"><i class="fa fa-sign-out fa-fw"></i> Logout</a></li> </ul> <!-- /.dropdown-user --> </li> <!-- /.dropdown --> </ul> </nav> <!--/. NAV TOP --> <!-- 左侧菜单栏 --> <nav class="navbar-default navbar-side" role="navigation"> <div id="sideNav" href=""><i class="fa fa-caret-right"></i></div> <div class="sidebar-collapse"> <ul class="nav" id="main-menu"> <li> <a href="index.html" class="active-menu"><i class="fa fa-sitemap"></i> 班级管理<span class="fa arrow"></span></a> <ul class="nav nav-second-level"> <li> <a href="classAdd.jsp">新建班级</a> </li> <li> <a href="ClassInformationAction.action">班级信息</a> </li> </ul> </li> <li> <a href="ui-elements.html"><i class="fa fa-desktop"></i>学生管理<span class="fa arrow"></a> <ul class="nav nav-second-level"> <li> <a href="studentAdd.jsp">添加学生</a> </li> <li> <a href="StudentInformationAction.action">学生信息</a> </li> </ul> </li> <li> <a href="chart.html"><i class="fa fa-table"></i>课程管理<span class="fa arrow"></a> <ul class="nav nav-second-level"> <li> <a href="courseAdd.jsp">添加课程</a> </li> <li> <a href="CourseInformationAction.action">课程信息</a> </li> </ul> </li> <li> <a href="tab-panel.html"><i class="fa fa-bar-chart-o"></i>成绩管理<span class="fa arrow"></a> <ul class="nav nav-second-level"> <li> <a href="#">成绩录入</a> </li> <li> <a href="#">查询成绩</a> </li> <li> <a href="#">成绩统计</a> </li> </ul> </li> <li> <a href="empty.html"><i class="fa fa-fw fa-file"></i> Empty Page</a> </li> </ul> </div> </nav> <!-- /. NAV SIDE --> <!-- /. NAV SIDE --> <div id="page-wrapper"> <div id="page-inner"> <div class="row"> <div class="col-md-12"> <h1 class="page-header">班级管理<small> > 新增班级</small></h1> </div> </div> <!-- /. ROW --> <div class="row"> <div class="col-lg-12"> <div class="panel panel-default"> <div class="panel-heading"> 请输入新增班级信息 </div> <div class="panel-body"> <div class="row"> <!-- /.col-lg-6 (nested) --> <div class="col-lg-6"> <form role="form" action="ClassAddAction" method="post"> <div class="form-group has-success"> <label class="control-label" for="inputSuccess">请输入班级编号</label> <input name="classunitnumber" type="text" class="form-control" id="inputSuccess"> </div> <div class="form-group has-warning"> <label class="control-label" for="inputWarning">请输入班级名称</label> <input name="classunitname" type="text" class="form-control" id="inputWarning"> </div> <button type="submit" class="btn btn-default" onclick="add()">提 交</button> <button type="reset" class="btn btn-default">重 置</button> </form> </div> <!-- /.col-lg-6 (nested) --> </div> <!-- /.row (nested) --> </div> <!-- /.panel-body --> </div> <!-- /.panel --> </div> <!-- /.col-lg-12 --> </div> <!-- /. ROW --> <footer><p>Copyright © 石家庄铁道大学软件工程系</p></footer> </div> <!-- /. PAGE INNER --> </div> <!-- /. PAGE WRAPPER --> </div> </div> <!-- /. WRAPPER --> <!-- JS Scripts--> <!-- jQuery Js --> <script src="assets/js/jquery-1.10.2.js"></script> <!-- Bootstrap Js --> <script src="assets/js/bootstrap.min.js"></script> <!-- Metis Menu Js --> <script src="assets/js/jquery.metisMenu.js"></script> <script src="assets/js/dataTables/jquery.dataTables.js"></script> <script src="assets/js/dataTables/dataTables.bootstrap.js"></script> <script> $(document).ready(function () { $('#dataTables-example').dataTable(); }); </script> <script> $(document).ready(function() { $('#example').dataTable( { "sPaginationType": "full_numbers" }); }); function add(){ alert("添加成功!"); return true; } </script> <!-- Morris Chart Js --> <script src="assets/js/morris/raphael-2.1.0.min.js"></script> <script src="assets/js/morris/morris.js"></script> <script src="assets/js/easypiechart.js"></script> <script src="assets/js/easypiechart-data.js"></script> <!-- Custom Js --> <script src="assets/js/custom-scripts.js"></script> </body> </html>
然后由Controller层通过struts的拦截器对view层的拦截过滤,实现调用不同的业务处理逻辑。
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name></display-name> <welcome-file-list> <welcome-file>login.html</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>openSessionInView</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>openSessionInView</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <!-- 把Struts的action交给spring托管 --> <constant name="struts.objectFactory" value="spring"/> <!-- 设置struts为开发模式,这样能够及时的响应修改 不过没有搞懂是什么意思--> <constant name="struts.devMode" value="false"/> <constant name="struts.il8n.encoding" value="UTF-8"></constant> <!--设置上传文件大小 (20MB)--> <constant name="struts.multipart.maxSize" value="2097152"></constant> <!-- 定位视图资源的根路径 --> <!-- <constant name="struts.convention.result.path" value="/WEB-INF/"></constant> --> <!--设置struts配置文件修改以后系统是否自动重新加载该文件默认为false但是在开发环境下还是要设置成true--> <constant name="struts.configuration.xml.reload" value="true"></constant> <!--************************************************************************************************************ --> <package name="default" namespace="/" extends="struts-default"> <action name="LoginAction" class="LoginAction"><!-- name是jsp页面识别的名字,class是action中类的名字 --> <result name="success">home.jsp</result> <result name="error">login.html</result> </action> <!-- 班级管理 --> <action name="ClassInformationAction" class="ClassInformationAction"> <result name="success">classInformation.jsp</result> </action> <action name="ClassAddAction" class="ClassAddAction"> <result name="success">classAdd.jsp</result> </action> <action name="ClassEditAction" class="ClassEditAction"> <result name="success">classInformation.jsp</result> </action> <action name="ClassDelAction" class="ClassDelAction"> <result name="success">classInformation.jsp</result> </action> <!-- 学生管理 --> <action name="StudentInformationAction" class="StudentInformationAction"> <result name="success">studentInformation.jsp</result> </action> <action name="StudentAddAction" class="StudentAddAction"> <result name="success">studentAdd.jsp</result> </action> <action name="StudentEditAction" class="StudentEditAction"> <result name="success">studentInformation.jsp</result> </action> <action name="StudentDelAction" class="StudentDelAction"> <result name="success">studentInformation.jsp</result> </action> </package> </struts>
找到相应的action去处理相应的业务:
ClassAddAction.java
package com.zdr.action; import com.opensymphony.xwork2.ActionSupport; import com.zdr.entity.Classunit; import com.zdr.service.ClassunitService; public class ClassAddAction extends ActionSupport{ //变量 private String classunitname = ""; private String classunitnumber = ""; private String result = ""; private ClassunitService classunitService; private Classunit classnumber; //函数 public String getClassunitname() { return classunitname; } public void setClassunitname(String classunitname) { this.classunitname = classunitname; } public String getClassunitnumber() { return classunitnumber; } public void setClassunitnumber(String classunitnumber) { this.classunitnumber = classunitnumber; } public String getResult() { return result; } public void setResult(String result) { this.result = result; } public ClassunitService getClassunitService() { return classunitService; } public void setClassunitService(ClassunitService classunitService) { this.classunitService = classunitService; } @Override public String execute() throws Exception { if(classunitService.checkClassnumber(getClassunitnumber())) { //result = "班级编号重复!"; return ERROR; } else { classnumber = new Classunit(); classnumber.setClassNumber(classunitnumber); classnumber.setClassName(classunitname); classunitService.addClassunit(classnumber); } return SUCCESS; } }
通过调用关系,调用model层dao层方法完成业务处理:
package com.zdr.dao; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.Criteria; import org.hibernate.LockMode; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import org.springframework.context.ApplicationContext; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import com.zdr.entity.Classunit; import com.zdr.entity.Student; /** * A data access object (DAO) providing persistence and search support for * Classunit entities. Transaction control of the save(), update() and delete() * operations can directly support Spring container-managed transactions or they * can be augmented to handle user-managed Spring transactions. Each of these * methods provides additional information for how to configure it for the * desired type of transaction control. * * @see com.zdr.entity.Classunit * @author MyEclipse Persistence Tools */ public class ClassunitDAO extends HibernateDaoSupport { private static final Log log = LogFactory.getLog(ClassunitDAO.class); private SessionFactory sessionFactory; private Session session; // property constants public static final String CLASS_NAME = "className"; protected void initDao() { // do nothing } public boolean checkClassnumber(String classunitnumber) { if(classunitnumber!= null) { Classunit cun = findById(classunitnumber); if(cun == null) { return false; } } return true; } public Classunit findclassunitnumber(String classunitnumber) { if(classunitnumber!=null) { return findById(classunitnumber); } return null; } public void save(Classunit transientInstance) { log.debug("saving Classunit instance"); try { getHibernateTemplate().save(transientInstance); log.debug("save successful"); } catch (RuntimeException re) { log.error("save failed", re); throw re; } } public void delete(Classunit persistentInstance) { log.debug("deleting Classunit instance"); try { getHibernateTemplate().delete(persistentInstance); log.debug("delete successful"); } catch (RuntimeException re) { log.error("delete failed", re); throw re; } } public Classunit findById(java.lang.String id) { log.debug("getting Classunit instance with id: " + id); try { Classunit instance = (Classunit) getHibernateTemplate().get( "com.zdr.entity.Classunit", id); return instance; } catch (RuntimeException re) { log.error("get failed", re); throw re; } } public List<Classunit> findByExample(Classunit instance) { log.debug("finding Classunit instance by example"); try { List<Classunit> results = (List<Classunit>) getHibernateTemplate() .findByExample(instance); log.debug("find by example successful, result size: " + results.size()); return results; } catch (RuntimeException re) { log.error("find by example failed", re); throw re; } } public List findByProperty(String propertyName, Object value) { log.debug("finding Classunit instance with property: " + propertyName + ", value: " + value); try { String queryString = "from Classunit as model where model." + propertyName + "= ?"; return getHibernateTemplate().find(queryString, value); } catch (RuntimeException re) { log.error("find by property name failed", re); throw re; } } public List<Classunit> findByClassName(Object className) { return findByProperty(CLASS_NAME, className); } public List<Classunit> findAll() { log.debug("finding all Classunit instances"); try { String queryString = "from Classunit"; return getHibernateTemplate().find(queryString); } catch (RuntimeException re) { log.error("find all failed", re); throw re; } } public Classunit merge(Classunit detachedInstance) { log.debug("merging Classunit instance"); try { Classunit result = (Classunit) getHibernateTemplate().merge( detachedInstance); log.debug("merge successful"); return result; } catch (RuntimeException re) { log.error("merge failed", re); throw re; } } public void attachDirty(Classunit instance) { log.debug("attaching dirty Classunit instance"); try { getHibernateTemplate().saveOrUpdate(instance); log.debug("attach successful"); } catch (RuntimeException re) { log.error("attach failed", re); throw re; } } public void attachClean(Classunit instance) { log.debug("attaching clean Classunit instance"); try { getHibernateTemplate().lock(instance, LockMode.NONE); log.debug("attach successful"); } catch (RuntimeException re) { log.error("attach failed", re); throw re; } } public static ClassunitDAO getFromApplicationContext(ApplicationContext ctx) { return (ClassunitDAO) ctx.getBean("ClassunitDAO"); } }
处理结果再由struts.xml去返回相应的结果所对应的业务方向。
调用关系图:
总结:
当用户通过view层向系统发送业务请求时,struts.xml根据已经定义好的处理逻辑进行相应的处理,将其业务分给指定的action来处理。而model层通过不同的业务逻辑调用实现试图和逻辑的分层,由action去调用相应的dao层业务进行业务逻辑处理,处理结果再由struts.xml去返回相应的结果所对应的业务方向。
通过mvc体现的质量属性:
可修改性:将视图与业务逻辑分离,易于业务的变动和修改。