首页 > 技术文章 > MVC模式在Java Web应用程序中的实例分析

lk0823 2017-05-05 15:27 原文

      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 &copy; 石家庄铁道大学软件工程系</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体现的质量属性:

可修改性:将视图与业务逻辑分离,易于业务的变动和修改。

 

推荐阅读