JAVAWEB实训(1) 编写MVC框架

目录

1. 什么是MVC框架?

1.1 MVC编程模式

1.2 MVC处理流程 

 2. 编写一个简单的MVC框架

2.1 注解

2.2 处理器映射器HandlerMapping

2.3 中央控制器DispatcherServlet

2.4 梳理MVC流程

 3. 源码


1. 什么是MVC框架?

MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。

1.1 MVC编程模式

  • V  即View视图,是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操作的方式。
  • M  即model模型,是指模型表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
  • C  即controller控制器,是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

1.2 MVC处理流程 

è¿éåå¾çæè¿°

【流程图说明】

1.用户发送请求至 前端控制器DispatcherServlet。

2.前端控制器DispatcherServlet收到请求后调用处理器映射器HandlerMapping。

3.处理器映射器HandlerMapping根据请求的Url找到具体的处理器,生成处理器对象Handler及处理器拦截器HandlerIntercepter(如果有则生成)一并返回给前端控制器DispatcherServlet。

4.前端控制器DispatcherServlet通过处理器适配器HandlerAdapter调用处理器Controller。

5.执行处理器(Controller,也叫后端控制器)

6.处理器Controller执行完后返回ModelAnView。

7.处理器映射器HandlerAdapter将处理器Controller执行返回的结果ModelAndView返回给前端控制器DispatcherServlet。

8.前端控制器DispatcherServlet将ModelAnView传给视图解析器ViewResolver。

9.视图解析器ViewResolver解析后返回具体的视图View。

10.前端控制器DispatcherServlet对视图View进行渲染视图(即:将模型数据填充至视图中)

11.前端控制器DispatcherServlet响应用户。

【此处摘自CSDN博主「皓月星辰_w」的原创文章:MVC框架详解(资源整理)_皓月星辰-CSDN博客_mvc框架

 2. 编写一个简单的MVC框架

观察下面这个类中的方法, 这里指定了url和相应的处理器,那么如何才能让用户在浏览器输入url后,后台正确地调用对应的处理器呢?

2.1 注解

 首先每个方法(处理器)是通过注解与url关联的(value):

  • @Target(ElementType.METHOD)表示是方法注解
  • @Retention(RetentionPolicy.RUNTIME)表示注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解
  • @Documented表示这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。

ResponseBody:


import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
 * 被此注解添加的方法,会被用于处理请求
 * 方法返回的内容会被以文本的形式返回到客户端
 */
public @interface ResponseBody {
    String value();
}

 ResponseView:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**
 * 被此注解添加的方法,会被用于处理请求
 * 方法返回的内容会直接重定向
 */
public @interface ResponseView {
    String value();
}

 2.2 处理器映射器HandlerMapping

处理器映射器类实际上是一个工具类:建立映射、存储映射、获取处理器

(1) 处理器映射器会在物理上建立url与处理器之间的映射:

private static Map<String,MVCMapping> data = new HashMap<>();

 用一个HashMap来保存String类型的url与“处理器对象”之间的映射关系,这个处理器对象我们用一个静态内部类进行封装:

/**
 * 映射对象,每一个对象封装一个方法,用于处理请求
 */
public static class MVCMapping{
    private Object object;
    private Method method;
    private ResponseType type;
}

这是因为我们处理请求的类是通过配置文件进行配置的:application.properties

一个类中间可能包含多个处理请求的方法method,注解的类型表示处理器返回的类型type

(2) 处理器映射器向外提供方法:通过url获取处理器

public static MVCMapping get(String uri)

(3) 处理器能够读取指定流中的处理器类,以方法为单位解析成处理器对象MVCMapping,并生成映射:

public static void load(InputStream is) throws IOException {
        Properties ppt = new Properties();
        ppt.load(is);
        //获取配置文件中描述的一个个类
        Collection values = ppt.values();
        for (Object cla : values) {
            String className = (String) cla;
            try {
                //加载类
                Class c = Class.forName(className);
                //创建对象
                Object object = c.getConstructor().newInstance();
                //获取类的所有方法
                Method[] methods = c.getMethods();
                for (Method m : methods) {
                    Annotation[] annotations = m.getAnnotations();
                    if(annotations!=null) {
                        for (Annotation a : annotations) {
                            if (a instanceof ResponseBody){
                                //说明此方法用于返回字符串给客户端
                                MVCMapping mapping = new MVCMapping(object,m,ResponseType.TEXT);
                                Object o = data.put(((ResponseBody) a).value(),mapping);
                                if(o!=null){//存在的重复的请求地址
                                    throw new RuntimeException("请求地址重复:"+((ResponseBody) a).value());
                                }
                            }else if(a instanceof ResponseView){
                                //说明此方法用于返回界面给客户端
                                MVCMapping mapping = new MVCMapping(object,m,ResponseType.VIEW);
                                Object o = data.put(((ResponseView) a).value(),mapping);
                                if(o!=null){//存在的重复的请求地址
                                    throw new RuntimeException("请求地址重复:"+((ResponseBody) a).value());
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

上面代码中出现的ResponseType是自定义的枚举类型:表示响应的类型

public enum ResponseType {
    TEXT,VIEW;
}

2.3 中央控制器DispatcherServlet

(1)中央处理器继承了HttpServlet,在web.xml中我们对其进行配置:

  • 配置初始化文件
  • 配置处理所有的url请求
<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>pers.xls.mvc.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contentConfigLocation</param-name>
        <param-value>application.properties</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

(2)通过重写Servlet的init方法获取初始化文件:

在这里调用处理器映射类来读取初始化文件建立映射

 @Override
    public void init(ServletConfig config) throws ServletException {
        String path = config.getInitParameter("contentConfigLocation");
        InputStream is = DispatcherServlet.class.getClassLoader().getResourceAsStream(path);
        try {
            HandlerMapping.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

(3)通过重写Service方法,根据接收的请求,获取对应的处理器,进行方法的调用:

 @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取用户请求的uri
        String uri = req.getRequestURI();
        HandlerMapping.MVCMapping mapping = HandlerMapping.get(uri);
        if (mapping==null){
            resp.sendError(404,"自定义MVC:映射地址不存在:"+uri);
            return;
        }
        Object obj = mapping.getObject();
        Method method = mapping.getMethod();
        Object result = null;
        try {
            result = method.invoke(obj,req,resp);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        switch (mapping.getType()){
            case TEXT:
                resp.getWriter().println((String) result);
                break;
            case VIEW:
                resp.sendRedirect((String) result);
                break;
        }
    }

2.4 梳理MVC流程

现在我们来梳理一下这个简单的MVC的处理流程:

  • 通过浏览器我们输入特定的 url 比如:localhost:8080/login.do

  • 中央控制器 DispatcherServlet 处理请求,如果是第一次访问,会调用 init 方法,加载src文件夹下的application.properties配置文件,将文件输入流传递给处理器映射器类 HandlerMapping 进行加载

  • 处理器映射器类 HandlerMapping 加载文件输入流,读取配置文件中的处理器类,分析出类中的含有特定注解的方法,建立url到“处理器对象MVCMapping”的映射,存储在哈希表中

  • 中央控制器 DispatcherServlet 继续调用service方法,调用处理器映射器类 HandlerMapping 的 get 方法获取请求uri对应的“处理器对象”如果获取不到,说明不存在该访问地址到处理器的映射,访问的内容不存在。从获取到的“处理器对象”中提取出相关的处理类对象、方法、响应类型,利用Java的反射机制,调用该方法:

         获取响应值,最后根据响应类型的不同对前台进行响应:

 3. 源码

链接:https://pan.baidu.com/s/1kfZAc-Z6DmK0cp1pcEfX7Q 
提取码:g8zx

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hnu哈哈

请接受直女的么么哒????

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值