MVC

MVC

什么是MVC(Model View Controller)

MVC是一种软件加构思想,其核心四线格式:将数据处理与数据展示分开,按照这种思想,可以将一个软件系统划分为三种不同类型得模块,分别是模型丶视图丶控制器.

  • 模型:

    负责数据处理(即业务处理)

  • 视图:

    负责数据展现(即表示逻辑)

  • 控制器:

    负责协调模型和视图(用户通过视图向控制器发送请求,控制器选择对应的模型来出了力请求;模型将处理结果返回给控制器,由控制器将对应的视图来展现处理结果)

如何使用MVC来开发一个web应用

使用java类充当模型,负责数据处理,业务逻辑,使用jsp充当视图,使用servlet充当控制器,如下图所示:

MVC

MVC

  • JSP放到WEB-INF中,浏览器无法直接访问其中的内容

使用MVC的优点

  • 将数据展现与数据处理分开之后,提高系统的维护性,便于代码的维护

    • 数据处理发生改变,不会影响数据展现部分,反过来也是如此
  • 利于测试

    • 例如将业务逻辑写再java类中,不必要部署整个应用就可以进行单独测试
  • 便于分工协作

MVC的缺点

使用MVC,会增加代码量,增加设计难度,相应会增加软件的成本,所以具有一定规模,并且强调系统的维护性,要求的软件才需要使用MVC.

实现一个简单的MVC框架(SmartMVC)

软件的半成品

(1)目标

SmartMVC是一个通用的控制器(DispatcherServlet),利用SmartMVC在开发一个web应用时,只需要添加相应的配置,该控制器就可以调用相应的模型或者视图,不再需要写控制器.

(2)架构

MVC

MVC

一个简易的SpringMVC流程

MVC

MVC

step1.创建一个maven工程(smartmvc-exec)

step2.导包(dom4j)

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>

step3.添加一个jsp(/WEB-INF/hello.jsp)

<%@ page pageEncoding="utf-8" 
contentType="text/html; charset=utf-8" %>
<html>
    <head></head>
    <body style="font-size:30px;">
        Hello SmartMVC!
    </body>
</html>

step4.在base.annotation包下添加一个java注解(@RequestMapping)

@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    public String value();
}

step5.在demo包下添加HelloController类(处理器)

该类方法前添加@RequestMapping注解(指定请求路径)
方法返回值是一个字符串(即视图名)

public class HelloController {

@RequestMapping("/hello.do")
public String hello(){
    System.out.println(
            "HelloController的hello方法");
    return "hello";
 }
}

step6.添加smart-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!-- 配置处理器:
        class属性用于指定处理器类名。
    -->
    <bean class="demo.HelloController"/>
</beans>

step7.在base.web包下添加DispatcherServlet

在初始化方法里,读取配置文件中的处理器类名,将处理器实例化,
然后将处理器实例交给HandlerMapping来处理。

public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

private HandlerMapping handlerMapping;

@Override
/**
 * 读取配置文件(smartmvc.xml)的内容,将所有
 * 处理器实例化,然后将这些处理器实例交给
 * HandlerMapping来处理。
 * 注:
 *    HandlerMapping负责建立请求路径与处理器的
 * 对应关系。
 */
public void init() throws ServletException {
    //读取配置文件位置及文件名
    String configLocation = 
            getServletConfig()
            .getInitParameter("configLocation");

    SAXReader sax = 
            new SAXReader();

    InputStream in = 
            getClass().getClassLoader()
    .getResourceAsStream(configLocation);

    try {
        /*
         * 利用dom4j读取配置文件的内容,
         * SAXReader的read方法的返回值可以
         * 想像一棵树,我们可以从根节点开始,
         * 一层一层遍历。
         */
        Document doc = sax.read(in);
        //找到根节点
        Element root = 
                doc.getRootElement();
        //找出根节点的所有子节点
        List<Element> elements = 
                root.elements();
        List beans = 
                new ArrayList();
        //遍历子节点,读取处理器类名
        for(Element ele : elements){
            String className = 
                    ele.attributeValue("class");
            System.out.println("className:" 
                    + className);
            //将处理器实例化
            Object bean = 
                    Class.forName(className)
                    .newInstance();
            beans.add(bean);
        }
        System.out.println("beans:" + beans);

        //将处理器实例交给HandlerMapping来处理
        handlerMapping  = new HandlerMapping();
        handlerMapping.process(beans);

    } catch (Exception e) {
        e.printStackTrace();
        throw new ServletException(e);
    }
}
<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>base.web.DispatcherServlet</servlet-class>
    <!--
        指定配置文件的位置及文件名 
    -->
    <init-param>
        <param-name>configLocation</param-name>
        <param-value>smart-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

step8.在base.common包下添加Handler类

public class Handler {
    private Method method;
    private Object obj;

    public Handler(Method method, Object obj) {
        this.method = method;
        this.obj = obj;
    }

    public Method getMethod() {
        return method;
    }
    public void setMethod(Method method) {
        this.method = method;
    }
    public Object getObj() {
        return obj;
    }
    public void setObj(Object obj) {
        this.obj = obj;
    }
}

step9.在base.common包下添加HandlerMapping类(映射处理器)

public class HandlerMapping {

//mappings用于存放请求路径与处理器的对应关系
private Map<String,Handler> mappings = 
        new HashMap<String,Handler>();

/**
 * 依据请求路径返回Handler对象。
 * 注:
 *   Handler对象封装了处理器对象及方法对象,
 *  方便利用java反射来调用处理器的方法。
 */
public Handler getHandler(String path){
    return mappings.get(path);
}

public void process(List beans) {
    for(Object bean: beans){
        //获得Class对象
        Class clazz = bean.getClass();
        //获得所有方法
        Method[] methods = 
                clazz.getDeclaredMethods();
        //遍历所有方法
        for(Method mh : methods){
            //获得@RequestMapping注解
            RequestMapping rm = 
                    mh.getDeclaredAnnotation(
                    RequestMapping.class);
            //获得请求路径
            String path = rm.value();
            //存放请求路径与处理器的对应关系
            mappings.put(path, 
                    new Handler(mh,bean));
        }

    }
    System.out.println("mappings:" + mappings);
    }
}