• 自定义mvc原理和框架实现


    目录

    1.什么是MVC

    2.自定义MVC工作原理图

    3.自定义mvc的简单实现

    1.中央控制器

    2.Action接口定义

    3.实现子控制器

    4.完善中央控制器

    1.请求分发功能

    2.使用配置文件配置action

    3.请求参数处理

    4.完善Action

    4.打包jar包


    1.什么是MVC

          MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。 它是一种软件设计典范。

          用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

    提高了程序的可维护性、可移植性、可扩展性与可重用性,降低了程序的开发难度。它主要分模型、视图、控制器三层。

    • 模型(model): 它是应用程序的主体部分,主要包括业务逻辑模块(web项目中的dao类)和数据模块(pojo类)。pojo一般可以叫做实体域模型,dao和service称为过程域模型。
    • 视图(view): 用户与之交互的界面、在web中视图一般由jsp,html组成,其他的还包括android,ios等等。
    • 控制器(controller): 接收来自界面的请求 并交给模型进行处理 在这个过程中控制器不做任何处理只是起到了一个连接的做用。

    不足的地方:

    • 增加系统结构和实现的复杂性。对于简单的界面,严格遵守MVC,需要使模型、视图与控制器分离,增加系统复杂性
    • 视图和控制器之间的关系太过紧密了

    2.自定义MVC工作原理图

     

    核心组件说明:

    • 中央控制器(ActionServlet): 复杂接收所有的请求,并分别给控制器具体处理。
    • 自控制器(Action):负责处理中央处理器分配的请求
    • 视图(view): jsp页面,负责显示
    • 模型(Model): 负责业务处理逻辑

    3.自定义mvc的简单实现

    1.中央控制器

    通过servlet来实现一个中央控制器,负责所有请求的接收。(后续中央控制器在再将请求转发给各个子控制器,此处可以先把请求接进来,转发功能后面再加)

    1. /**
    2. * 中央控制器,负责接收所有的请求并分别给控制器具体处理
    3. * @author Administrator
    4. */
    5. @WebServlet("*.action")
    6. public class ActionDispatchServlet extends HttpServlet {
    7. @Override
    8. public void doGet(HttpServletRequest req, HttpServletResponse resp) {
    9. doPost(req, resp);
    10. }
    11. @Override
    12. public void doPost(HttpServletRequest req, HttpServletResponse resp) {
    13. System.out.println("dopost ..... ");
    14. }
    15. }

    2.Action接口定义

    Action接口定义了每个子控制器需要遵循的行为,使得所有的子控制器都有一个同一的抽象类型,所以我们可以在中央控制器中使用Action接口类型来引用所有的子控制器。这样就为用户扩展自定义的子控制器提供了条件。

    1. /**
    2. * 每个子控制器必须实现该接口,负责处理中央处理器分配的请求
    3. * @author Administrator
    4. */
    5. public interface Action {
    6. /**
    7. * 处理请求
    8. * @param req 请求
    9. * @param resp 响应
    10. * @return String 返回转发或重定向的jsp页面名称
    11. */
    12. String exeute(HttpServletRequest req, HttpServletResponse resp);
    13. }

    3.实现子控制器

    为了方便调试,我这里实现了两个子控制器(= ̄ω ̄=)

    1. public class BookAction implements Action {
    2. @Override
    3. public String getBooks(HttpServletRequest req, HttpServletResponse resp) {
    4. System.out.println("BookAction...");
    5. return "books";
    6. }
    7. }
    1. public class StudentAction implements Action{
    2. @Override
    3. public String execute(HttpServletRequest req, HttpServletResponse resp) {
    4. System.out.println("StudentAction");
    5. return "students";
    6. }

    4.完善中央控制器

    为了便于理解,我们可以分步骤的,循序渐进的完善中央控制器:

    • 编写简单的请求分发实现功能
    • 实现通过配置文件来配置子控制器的功能
    • 完善请求参数处理功能

    1.请求分发功能

    1. @WebServlet("*.action")
    2. public class ActionDispatchServlet extends HttpServlet{
    3. //用于保存path与action子控制器的映射
    4. public static Map actionMap = new HashMap<>();
    5. static {
    6. actionMap.put("/studentAction", new StudentAction());
    7. actionMap.put("/bookAction", new BookAction());
    8. }
    9. @Override
    10. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    11. doPost(req, resp);
    12. }
    13. @Override
    14. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    15. String spath = req.getServletPath();
    16. String path = spath.split("\\.")[0];
    17. Action action = getActionByPath(path);
    18. String name = action.execute(req, resp);
    19. System.out.println(name);
    20. }
    21. }

    2.使用配置文件配置action

    在上面的示例中,在中央控制器中直接创建action子控制器,如果新增一个子控制器需要在中央控制器中添加,这样并不实用。 为了增加灵活性,可以将action转移到配置文件中配置,中央控制器通过配置来初始化action子控制器。

    (1)此时需要将config.xml文件的解析建模项目的功能集成进来。
    (ConfigModel,ActionModel,ForwardModel,ConfigModelFactory)

    (2)在项目的src目录下加入如下配置文件(config.xml)

    1. config[
    2. config (action*)>
    3. action (forward*)>
    4. forward EMPTY>
    5. action
    6. path CDATA #REQUIRED
    7. type CDATA #REQUIRED
    8. >
    9. forward
    10. name CDATA #REQUIRED
    11. path CDATA #REQUIRED
    12. redirect (true|false) "false"
    13. >
    14. ]>
    15. <config>
    16. <action path="/studentAction" type="com.zking.action.StudentAction">
    17. <forward name="students" path="/students.jsp" redirect="false"/>
    18. action>
    19. <action path="/bookAction" type="com.zking.action.BookAction">
    20. <forward name="books" path="/books.jsp" redirect="false"/>
    21. action>
    22. config>

    (3) 完善中央处理器,通过配置文件来获取子控制器配置

    1. @WebServlet("*.action")
    2. public class ActionDispatchServlet extends HttpServlet{
    3. private static ConfigModel configModel = ConfigModelFactory.getConfig();
    4. @Override
    5. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    6. doPost(req, resp);
    7. }
    8. @Override
    9. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    10. String spath = req.getServletPath();
    11. String path = spath.split("\\.")[0];
    12. Action action = getActionByPath(path);
    13. //请求转发
    14. String name = action.execute(req, resp);
    15. ForwardModel forward = configModel.find(path).find(name);
    16. if(forward.isRedirect()) {
    17. resp.sendRedirect(req.getContextPath()+forward.getPath());
    18. } else {
    19. req.getRequestDispatcher(forward.getPath()).forward(req, resp);
    20. }
    21. }
    22. /**
    23. * 通过path获取Action的实例
    24. * @param path
    25. * @return
    26. */
    27. private Action getActionByPath(String path) {
    28. ActionModel am = configModel.find(path);
    29. try {
    30. Classextends Action> ac = (Classextends Action>)Class.forName(am.getType());
    31. return ac.newInstance();
    32. } catch (ClassNotFoundException
    33. | InstantiationException
    34. | IllegalAccessException e) {
    35. e.printStackTrace();
    36. }
    37. return null;
    38. }
    39. }

    注: 本例的实现中Action子控制器是多例模式的,及每个请求对应一个Action实例

    3.请求参数处理

    (1)定义接口

    1. /**
    2. * 对于需要处理请求参数的Action可以通过实现该接口获取请求参数的
    3. * 处理能力,中央控制器将会使用该接口来获取Model对象,并统一处理
    4. * 参数
    5. * @author Administrator
    6. */
    7. public interface ModelDrive {
    8. Object getModel();
    9. }

    (2)在中央处理器中加入请求参数的处理能力

    1. @WebServlet("*.action")
    2. public class ActionDispatchServlet extends HttpServlet{
    3. private static ConfigModel configModel = ConfigModelFactory.getConfig();
    4. @Override
    5. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    6. doPost(req, resp);
    7. }
    8. @Override
    9. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    10. String spath = req.getServletPath();
    11. String path = spath.split("\\.")[0];
    12. Action action = getActionByPath(path);
    13. //设置查询参数
    14. if(action instanceof ModelDrive) {
    15. ModelDrive m = (ModelDrive)action;
    16. Object bean = m.getModel();
    17. try {
    18. BeanUtils.populate(bean,req.getParameterMap());
    19. } catch (IllegalAccessException | InvocationTargetException e) {
    20. throw new ActionDispatchSetParameterException("在中央控制器中处理参数的时候发生异常");
    21. }
    22. }
    23. //请求转发
    24. String name = action.execute(req, resp);
    25. ForwardModel forward = configModel.find(path).find(name);
    26. if(forward.isRedirect()) {
    27. resp.sendRedirect(req.getContextPath()+forward.getPath());
    28. } else {
    29. req.getRequestDispatcher(forward.getPath()).forward(req, resp);
    30. }
    31. }
    32. /**
    33. * 通过path获取Action的实例
    34. * @param path
    35. * @return
    36. */
    37. private Action getActionByPath(String path) {
    38. ActionModel am = configModel.find(path);
    39. try {
    40. Classextends Action> ac = (Classextends Action>)Class.forName(am.getType());
    41. return ac.newInstance();
    42. } catch (ClassNotFoundException
    43. | InstantiationException
    44. | IllegalAccessException e) {
    45. e.printStackTrace();
    46. }
    47. return null;
    48. }
    49. }

    (3) 处理请求参数中的null及空字符串转换问题

    方法一:在中央控制器中加入处理请求参数中的null及空字符串转换的代码

    1. @WebServlet("*.action")
    2. public class ActionDispatchServlet extends HttpServlet{
    3. private static ConfigModel .....
    4. /**
    5. * 当Servlet 容器启动Web应用时调用该方法。
    6. * 在调用完该方法之后,容器再对Filter 初始化,
    7. * 并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
    8. */
    9. static {
    10. ConvertUtils.register(new IntegerConverter(null), Integer.class);
    11. ConvertUtils.register(new FloatConverter(null), Float.class);
    12. ConvertUtils.register(new DoubleConverter(null), Double.class);
    13. ConvertUtils.register(new LongConverter(null), Long.class);
    14. ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
    15. }
    16. @Override
    17. protected void doGet......
    18. }

    注:省略的代码在前几个中央控制器中有写,这里我就不重复写了。

    方法二:加入一个监听器,在应用启动时注册转换器。

    1. /**
    2. * ServletContextListener接口为Servlet API中的接口,用于监听ServletContext对象的生命周期。
    3. * 当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent事件,该事件由
    4. * ServletContextListener来处理。
    5. * @author Administrator
    6. */
    7. @WebListener
    8. public class BeanUtilsListener implements ServletContextListener{
    9. /**
    10. * 当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,
    11. * 容器会先销毁所有的Servlet和Filter 过滤器。
    12. */
    13. @Override
    14. public void contextDestroyed(ServletContextEvent arg0) {
    15. // TODO Auto-generated method stub
    16. }
    17. /**
    18. * 当Servlet 容器启动Web应用时调用该方法。
    19. * 在调用完该方法之后,容器再对Filter 初始化,
    20. * 并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
    21. */
    22. @Override
    23. public void contextInitialized(ServletContextEvent arg0) {
    24. ConvertUtils.register(new IntegerConverter(null), Integer.class);
    25. ConvertUtils.register(new FloatConverter(null), Float.class);
    26. ConvertUtils.register(new DoubleConverter(null), Double.class);
    27. ConvertUtils.register(new LongConverter(null), Long.class);
    28. ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
    29. }
    30. }

    4.完善Action

    每个Action只能有一个execute方法,如果处理一个模块的增删改查则需要单独编写多个Action,这样会比较麻烦。如果在一个Action实例中可以处理多个请求方法,则框架会更加灵活。

    • 规定请求参数中必须包含一个“methodName”参数,用于指定处理请求的Action中的方法
    • 构建一个抽象类,该类实现Action子控制器接口,通过反射机制调用其子类中的方法,方法名有请求参数“methodName”指定。
    • 需要开发的Action子控制器,集成上一步构建的抽象类,编写的用于处理请求的方法名要与请求参数“methodName”指定的方法名匹配,同时需要HttpServletRequest和HttpServletResponse两个参数(保留该参数主要为了方面对请求的处理)

    (1) 构建抽象类

    1. public abstract class AbstractAction implements Action{
    2. //获取methodName所指定的方法,然后通过反射机制方法调用
    3. @Override
    4. public String execute(HttpServletRequest req, HttpServletResponse resp) {
    5. String methodName = req.getParameter("methodName");
    6. Classextends Action> clazz = (Classextends Action>)this.getClass();
    7. try {
    8. Method method = clazz.getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
    9. return (String)method.invoke(this, req,resp);
    10. } catch (NoSuchMethodException
    11. | SecurityException
    12. | IllegalAccessException
    13. | IllegalArgumentException
    14. | InvocationTargetException e) {
    15. throw new RuntimeException(e);
    16. }
    17. }
    18. }

    (2) 自定义的Action子控制器示例

    1. public class BookAction extends AbstractAction implements ModelDrive{
    2. private Book book = new Book();
    3. @Override
    4. public Object getModel() {
    5. return book;
    6. }
    7. public String getBooks(HttpServletRequest req, HttpServletResponse resp) {
    8. System.out.println("BookAction getBooks..."+book);
    9. return "books";
    10. }
    11. }

    注意:在请求中需要添加一个methodName的固定参数,该参数指定了需要调用的Action中的方法的名称。

    示例:http://localhost:8080/项目名称/bookAction.action?methodName=getBooks&id=1000

    getBooks是子控制器的方法名

    4.打包jar包

    将自定义mvc框架打成jar包,以便于在其他项目中使用。

    项目 --(右击)-->Export

    选择JAR 文件 

     

     

     这篇文章就到这里结束了(づ ̄ 3 ̄)づ,拜拜。

  • 相关阅读:
    AMS 和 WMS的关系是什么,他们是同一个进程吗?
    简化路径[中等]
    缓存与数据库双写一致性几种策略分析
    Spring Data JPA系列4——Spring声明式事务处理与多数据源支持
    thinkphp5.0.24反序列化链子分析
    是时候为Spring Boot 3.0做准备了
    MFC Windows 程序设计[236]之多样对话框(附源码)
    json转化成Map数据结构,Map转Array
    Golang GMP调度模型:实现高效协程调度和执行
    个人强化学习论文导航
  • 原文地址:https://blog.csdn.net/weixin_64938628/article/details/126018340