• 【Java】使用原生Java实现MVC的基本思路(廖雪峰Java学习笔记)


    思路

    明确MVC包含Model数据模型)、View(视图)和Controller(控制器)。Model为业务数据模型,在Java中表现为JavaBean;View用于展现数据模型,可以基于JSP实现;Cotroller则用于处理不同的Web请求,可以使用Servlet实现。

    整体思路是,定义一个DispatcherServletHttpServlet,它用于接收处理所有Web请求(getpost),内部实现Web请求路径和Controller的映射关系,将处理请求转发Controller处理,接收处理结果ModelAndView类,将ModelAndView类输入模板引擎进行渲染。

    DispatcherServlet中的路径-Controller映射使用注解和类反射技术。注解用于说明Controller中每个方法(mehod)处理的内容,类反射技术则记录调用每个方法需要输入的参数数量及其类型。两者的映射关系使用Map(String, Object)保存,其中的Object详细记录Controller实例、method、参数信息。DispatcherServlet需要在初始化阶段完成所有Controller的扫描及其与路径映射关系的建立。

    ModelAndView类是Controller的方法处理的结果,也是模板引擎的输入参数。它其实记录的是JSP模板的路径和输入的数据模型。

    模板引擎可以使用第三方成熟插件如Pebble等实现。

    实现步骤

    1. 实现MVC的framework

    • DispatcherServlet 类:接受web请求,覆写doget、dopost,渲染视图
    • GetMapping/PostMapping注解:实现路径与Controller method快捷关联
    • ViewEngine:实现视图的渲染
    // ModelAndView.java
    
    package framework;
    
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    
    public class ModelAndView {
        Map<String, Object> model;
        String view;
    
        /**
         * 无数据模型的模板
         * @param view
         */
        public ModelAndView(String view) {
            this.view = view;
            this.model = new HashMap<String, Object>();
    //        this.model = Map.of();
        }
    
        /**
         * 数据模型为对象的模板
         * @param view
         * @param name
         * @param value
         */
        public ModelAndView(String view, String name, Object value) {
            this.view = view;
            this.model = new HashMap<>();
            this.model.put(name, value);
        }
    
        /**
         * 以Map为数据模型的模板
         * @param view
         * @param model
         */
        public ModelAndView(String view, Map<String, Object> model) {
            this.view = view;
            this.model = new HashMap<>(model);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    // DispatcherServelet.java
    
    DispatcherServlet extends HttpServlet{
    	@Override
    	public void init() throws ServletException{}
    	@Override
      protected void doGet(){}
      @Override
      protected void doPost(){}
      private void process(){}
    }
    
    abstract class AbstractDispatcher{}
    
    class GetDispatcher extends AbstractDispatcher{
    	public GetDispatcher(){}
    	@Override
      public ModelAndView invoke(){}
    }
    
    class PostDispatcher extends AbstractDispatcher{
    	public PostDispatcher (){}
    	@Override
      public ModelAndView invoke(){}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    // GetMapping.java
    
    package framework;
    
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Retention(RUNTIME)
    @Target(METHOD)
    public @interface GetMapping {
        String value();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    // ViewEngine.java
    
    package framework;
    
    import com.mitchellbosecke.pebble.PebbleEngine;
    import com.mitchellbosecke.pebble.error.PebbleException;
    import com.mitchellbosecke.pebble.loader.ServletLoader;
    import com.mitchellbosecke.pebble.template.PebbleTemplate;
    
    import javax.servlet.ServletContext;
    import java.io.IOException;
    import java.io.Writer;
    
    public class ViewEngine {
        private final PebbleEngine engine;
    
        public ViewEngine(ServletContext servletContext) {
            // 定义一个ServletLoader用于加载模板:
            ServletLoader loader = new ServletLoader(servletContext);
            // 模板编码:
            loader.setCharset("UTF-8");
            // 模板前缀,这里默认模板必须放在`/WEB-INF/templates`目录:
            loader.setPrefix("/WEB-INF/templates");
            // 模板后缀:
            loader.setSuffix("");
            // 创建Pebble实例:
            this.engine = new PebbleEngine.Builder()
                    .autoEscaping(true) // 默认打开HTML字符转义,防止XSS攻击
                    .cacheActive(false) // 禁用缓存使得每次修改模板可以立刻看到效果
                    .loader(loader).build();
        }
    
        public void render(ModelAndView mv, Writer writer) throws IOException, PebbleException {
            // 查找模板:
            PebbleTemplate template = this.engine.getTemplate(mv.view);
            // 渲染:
            template.evaluate(writer, mv.model);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    2.定义Bean

    • 平台中使用到的数据模型JavaBean
    // User.java
    
    package bean;
    
    public class User {
        public String email;
        public String password;
    
        public String name;
        public String description;
    
        public User() {
        }
    
        public User(String email, String password, String name, String description) {
            this.email = email;
            this.password = password;
            this.name = name;
            this.description = description;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.实现Controller

    // IndexController.java
    
    package controller;
    
    import bean.User;
    import framework.GetMapping;
    import framework.ModelAndView;
    
    import javax.servlet.http.HttpSession;
    
    public class IndexController {
        @GetMapping("/")
        public ModelAndView index(HttpSession session) {
            User user = (User) session.getAttribute("user");
            return new ModelAndView("/index.html", "user", user);
        }
    
        @GetMapping("/hello")
        public ModelAndView hello(String name) {
            if (name == null) {
                name = "World";
            }
            return new ModelAndView("/hello.html", "name", name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4.定义视图

    
    
    {% extends "_base.html" %}
    
    {% block main %}
    
    {% if user == null %}
    <h3>Welcome!h3>
    {% else %}
    <h3>Welcome {{ user.name }}!h3>
    {% endif %}
    
    {% endblock %}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    mall商城项目:只启动mall-admin情况下Windows环境的部署
    322. 零钱兑换
    [2023.09.15]: Yew SSR模式下的条件编译问题
    RocketMQ 源码一,启动篇
    java集合常用方法汇总
    电力安全事故安全培训3D仿真展馆绿色环保持久复用
    界面组件DevExpress WPF v22.1 - 全新升级类Office、Excel功能
    关于个体工商户的15个财税要点,你清楚吗?
    01标定相关理论
    mybatis的延迟加载原理是什么呢?
  • 原文地址:https://blog.csdn.net/baidu_26646129/article/details/126452148