• J2EE基础-自定义MVC(中)


    目录

    前言

    一、让中央控制器动态加载存储子控制器

    二、 参数传递封装优化

    三、 对于方法执行结果转发重定向优化

    四、框架配置文本优化


    前言

    上次我们认识了自定义MVC原理(http://t.csdn.cn/S5TWs),今天了解的内容是自定义mvc框架实现(基于上篇文章进行优化)

    一、让中央控制器动态加载存储子控制器

    通过建模我们可以知道,最终configModel对象会包含config.xml中所有子控制器信息,同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可

     config.xml:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <config>
    3. <action path="/book" type="com.oyang.servlet.BookAction">
    4. <forward name="failed" path="/demo2.jsp" redirect="false" />
    5. <forward name="success" path="/demo3.jsp" redirect="true" />
    6. </action>
    7. <action path="/order" type="com.oyang.servlet.OrderAction">
    8. <forward name="failed" path="/demo2.jsp" redirect="false" />
    9. <forward name="success" path="/demo3.jsp" redirect="true" />
    10. </action>
    11. </config>

     优化DispatcherServlet: 

    1. package com.oyang.framework;
    2. import java.io.IOException;
    3. import java.util.HashMap;
    4. import java.util.Map;
    5. import javax.servlet.ServletException;
    6. import javax.servlet.annotation.WebServlet;
    7. import javax.servlet.http.HttpServlet;
    8. import javax.servlet.http.HttpServletRequest;
    9. import javax.servlet.http.HttpServletResponse;
    10. import org.apache.commons.beanutils.BeanUtils;
    11. import org.apache.commons.beanutils.PropertyUtils;
    12. import com.oyang.servlet.BookAction;
    13. /**
    14. * 中央控制器:
    15. * 主要职能,接收浏览器请求,找到对应的处理人
    16. * @author yang
    17. *
    18. */
    19. //@WebServlet("*.action")
    20. public class DispatcherServlet extends HttpServlet{
    21. //private Map<String, Action> actions=new HashMap<String,Action>();
    22. /**
    23. * 通过建模我们可以知道,最终configModel对象会包含config.xml中的所有子控制器信息,
    24. * 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
    25. */
    26. private ConfigModel configModel;
    27. //程序启动时 ,只会加载一次
    28. @Override
    29. public void init() throws ServletException {
    30. //actions.put("/book",new BookAction());
    31. //actions.put("/order",new BookAction());
    32. try {
    33. //配置地址
    34. //getInitParameter的作用是拿到web.xml中的Servlet信息配置的参数
    35. String configLocation = this.getInitParameter("configLocation");
    36. if(configLocation==null || "".equals(configLocation))
    37. configModel=ConfigModelFactory.bulid();
    38. else
    39. configModel=ConfigModelFactory.bulid(configLocation);
    40. } catch (Exception e) {
    41. e.printStackTrace();
    42. }
    43. }
    44. @Override
    45. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    46. doPost(req, resp);
    47. }
    48. //只要是以.action结尾的每次请求都会被doPost截取
    49. @Override
    50. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    51. http://localhost:8080/mvc/book.action?methodName=list
    52. String uri = req.getRequestURI();
    53. // 要拿到/book,就是最后一个/到最后一个.为止
    54. uri= uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
    55. /**
    56. * 思路:
    57. * 以前我们从map集合中获取,那样的话就行不通了。那么该怎么办呢?此时我们就需要修改xml配置文件了。
    58. * 通过配置文件拿到全路径名,再通过反射实例化
    59. */
    60. //Action action = actions.get(uri);
    61. //取到.action之后 Action action = actions.get(uri);里面定义了对应的方法
    62. //相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
    63. ActionModel actionModel = configModel.pop(uri);
    64. if(actionModel==null) {
    65. throw new RuntimeException("action config eroer 配置错误");
    66. }
    67. String type = actionModel.getType();//拿到type值
    68. //拿到type值之后就能通过路径名反射实例化 ------type是Action子控制器的全路径名
    69. try {
    70. Action action = (Action) Class.forName(type).newInstance();
    71. //此时的action 是bookAction
    72. if(action instanceof ModelDriven) {
    73. ModelDriven md=(ModelDriven) action;
    74. //action指的是bookAction,而bookAction又实现了ModelDriven接口,然后把bookAction转换成ModelDriven接口
    75. Object model = md.getModel();//model指的是bookAction中的book实例
    76. //给model中的属性赋值,要接收前端jsp传递过来的参数
    77. //PropertyUtils.getProperty(bean, name);//从某一个对象里面取某一个值
    78. //将前端所有参数值封装进实体类
    79. BeanUtils.populate(model, req.getParameterMap());
    80. System.out.println(model);
    81. }
    82. //正式调用方法之前,book中的属性要被赋值
    83. // action.execute(req, resp);
    84. String retult = action.execute(req, resp);
    85. ForwardModel forwardModel = actionModel.pop(retult);
    86. if(forwardModel==null) {
    87. throw new RuntimeException("forward config error 配置错误");
    88. }
    89. // /bookList.jsp/index.jsp
    90. String path = forwardModel.getPath();
    91. //拿到是否需要转发的配置
    92. boolean redirect = forwardModel.getRedirect();
    93. if(redirect)
    94. resp.sendRedirect(req.getServletContext().getContextPath()+path);
    95. else
    96. req.getRequestDispatcher(path).forward(req, resp);
    97. } catch (Exception e) {
    98. // TODO Auto-generated catch block
    99. e.printStackTrace();
    100. } //Action的实例
    101. }
    102. //由原来的actions.get(uri)变成了从模型对象里取
    103. }

     ConfigModel:

    1. package com.oyang.framework;
    2. import java.util.HashMap;
    3. import java.util.Map;
    4. /**
    5. * 根标签对应的对象
    6. * @author yang
    7. *
    8. * @date 2022年6月15日上午9:04:05
    9. */
    10. public class ConfigModel {
    11. private Map<String, ActionModel> aMap=new HashMap<String, ActionModel>();
    12. public void push(ActionModel actionModel) {
    13. aMap.put(actionModel.getPath(), actionModel);
    14. }
    15. public ActionModel pop(String path) {
    16. return aMap.get(path);
    17. }
    18. }

    ConfigModelFactory :

    1. package com.oyang.framework;
    2. import java.io.InputStream;
    3. import java.util.List;
    4. import org.dom4j.Document;
    5. import org.dom4j.DocumentException;
    6. import org.dom4j.Element;
    7. import org.dom4j.io.SAXReader;
    8. /**
    9. * 23种设计模式之工厂模式
    10. * sessionFactory
    11. * ConfigModelFactory 就是用来生产configmodel对象的
    12. * 生产出来的Configmodel对象就包含了config.xml中的配置内容
    13. * @author yang
    14. *
    15. *此地生产configmodel有配置信息?
    16. *1.解析config.xml中的配置信息
    17. *2.将对应的配置信息分别加载进不同的模型对象中
    18. * @date 2022年6月15日上午9:55:56
    19. */
    20. public class ConfigModelFactory {
    21. public static ConfigModel bulid(String path) throws Exception {
    22. InputStream in = ConfigModelFactory.class.getResourceAsStream(path);
    23. SAXReader sr=new SAXReader();
    24. Document doc = sr.read(in);
    25. List<Element> actionEles = doc.selectNodes("/config/action");
    26. ConfigModel con=new ConfigModel();
    27. for (Element actionM : actionEles) {
    28. ActionModel actionModel = new ActionModel();
    29. actionModel.setPath(actionM.attributeValue("path"));
    30. actionModel.setType(actionM.attributeValue("type"));
    31. //将forwardmodel赋值并且添加到actionmodel中
    32. List<Element> forwardEles = actionM.selectNodes("forward");
    33. for (Element element : forwardEles) {
    34. ForwardModel forwardModel = new ForwardModel();
    35. forwardModel.setName(element.attributeValue("name"));
    36. forwardModel.setPath(element.attributeValue("path"));
    37. // redirect:只能是false|true,允许空,默认值为false
    38. forwardModel.setRedirect("true".equals(element.attributeValue("redirect")));
    39. actionModel.push(forwardModel);
    40. }
    41. con.push(actionModel);
    42. }
    43. return con;
    44. }
    45. public static ConfigModel bulid() throws Exception {
    46. String defaultPath="/config.xml";
    47. return bulid(defaultPath);
    48. }
    49. }

    ForwardModel :

    1. package com.oyang.framework;
    2. /**
    3. * 对应forward标签
    4. * @author yang
    5. *
    6. * @date 2022年6月15日上午9:05:38
    7. */
    8. public class ForwardModel {
    9. private String name;
    10. private String path;
    11. private boolean redirect;
    12. public String getName() {
    13. return name;
    14. }
    15. public void setName(String name) {
    16. this.name = name;
    17. }
    18. public String getPath() {
    19. return path;
    20. }
    21. public void setPath(String path) {
    22. this.path = path;
    23. }
    24. public boolean getRedirect() {
    25. return redirect;
    26. }
    27. public void setRedirect(boolean b) {
    28. this.redirect = b;
    29. }
    30. }

    效果:

    二、 参数传递封装优化

    同常的Servlet:

    1. private void add(HttpServletRequest request, HttpServletResponse response) {
    2. String bid = request.getParameter("bid");
    3. String bname = request.getParameter("bname");
    4. String price = request.getParameter("price");
    5. Book b=new Book(Integer.valueOf(bid), bname, Float.valueOf(price));
    6. //BookDao.add(b);
    7. System.out.println(book);
    8. //上述问题:如果一张表有20乃至50行数据,那岂不是要写那么多行去接收?如果有10张20条数据以上的表,那一个增加方法就得写很多行,甚至还有可能写错。大大的限制了我们的开发速度和效率
    9. //为了避免上述问题,如果上述代码可以不写就好了,那就不会这么麻烦了。能否让它们默认就接收那么多值呢?那我们就去生产这样的一个框架
    10. System.out.println("在同一个Servlet中调用add新增方法");
    11. }

    上述问题:如果一张表有20乃至50行数据,那岂不是要写那么多行去接收?如果有10张20条数据以上的表,那一个增加方法就得写很多行,甚至还有可能写错。大大的限制了我们的开发速度和效率

    解决方案:
    为了避免上述问题,如果上述代码可以不写就好了,那就不会这么麻烦了。能否让它们默认就接收那么多值呢?那我们就去生产这样的一个框架

    建立一个模型驱动接口,该接口目的是为了接受前台JSP传递的参数,并且封装到实体类中

    ModelDriven<T> :

    1. package com.oyang.framework;
    2. /**
    3. * 模型驱动接口:接收前台JSP传递的参数,并且封装到实体类中
    4. * @author yang
    5. *
    6. * @date 2022年6月28日上午9:02:29
    7. */
    8. public interface ModelDriven<T> {
    9. //拿到将要封装的类实例 ModelDriven.getModel()//当你要调用这个方法的时候等价于-->new Book();
    10. //此时new 的book是没有值的,我们去封装一下值
    11. T getModel();
    12. }

    此时new 的book是没有值的,我们去封装一下值

     BooKAction实现模型驱动接口:

    1. package com.oyang.servlet;
    2. import javax.servlet.http.HttpServletRequest;
    3. import javax.servlet.http.HttpServletResponse;
    4. import com.oyang.entity.Book;
    5. import com.oyang.framework.Action;
    6. import com.oyang.framework.ActionSupport;
    7. import com.oyang.framework.ModelDriven;
    8. public class BookAction extends ActionSupport implements ModelDriven<Book>{
    9. private Book book=new Book();
    10. private void list(HttpServletRequest request, HttpServletResponse response) {
    11. System.out.println("在同一个Servlet中调用list查询方法");
    12. }
    13. private void load(HttpServletRequest request, HttpServletResponse response) {
    14. System.out.println("在同一个Servlet中调用load回显方法");
    15. }
    16. private void edit(HttpServletRequest request, HttpServletResponse response) {
    17. System.out.println("在同一个Servlet中调用edit修改方法");
    18. }
    19. private void del(HttpServletRequest request, HttpServletResponse response) {
    20. System.out.println("在同一个Servlet中调用del删除方法");
    21. }
    22. private void add(HttpServletRequest request, HttpServletResponse response) {
    23. System.out.println("在同一个Servlet中调用add新增方法");
    24. }
    25. @Override
    26. public Book getModel() {
    27. return book;
    28. }
    29. }

     将DispatcherServlet进行近一步的优化:

    1. package com.oyang.framework;
    2. import java.io.IOException;
    3. import java.util.HashMap;
    4. import java.util.Map;
    5. import javax.servlet.ServletException;
    6. import javax.servlet.annotation.WebServlet;
    7. import javax.servlet.http.HttpServlet;
    8. import javax.servlet.http.HttpServletRequest;
    9. import javax.servlet.http.HttpServletResponse;
    10. import org.apache.commons.beanutils.BeanUtils;
    11. import org.apache.commons.beanutils.PropertyUtils;
    12. import com.oyang.servlet.BookAction;
    13. /**
    14. * 中央控制器:
    15. * 主要职能,接收浏览器请求,找到对应的处理人
    16. * @author yang
    17. *
    18. */
    19. //@WebServlet("*.action")
    20. public class DispatcherServlet extends HttpServlet{
    21. //private Map<String, Action> actions=new HashMap<String,Action>();
    22. /**
    23. * 通过建模我们可以知道,最终configModel对象会包含config.xml中的所有子控制器信息,
    24. * 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
    25. */
    26. private ConfigModel configModel;
    27. //程序启动时 ,只会加载一次
    28. @Override
    29. public void init() throws ServletException {
    30. //actions.put("/book",new BookAction());
    31. //actions.put("/order",new BookAction());
    32. try {
    33. //配置地址
    34. //getInitParameter的作用是拿到web.xml中的Servlet信息配置的参数
    35. String configLocation = this.getInitParameter("configLocation");
    36. if(configLocation==null || "".equals(configLocation))
    37. configModel=ConfigModelFactory.bulid();
    38. else
    39. configModel=ConfigModelFactory.bulid(configLocation);
    40. } catch (Exception e) {
    41. e.printStackTrace();
    42. }
    43. }
    44. @Override
    45. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    46. doPost(req, resp);
    47. }
    48. //只要是以.action结尾的每次请求都会被doPost截取
    49. @Override
    50. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    51. http://localhost:8080/mvc/book.action?methodName=list
    52. String uri = req.getRequestURI();
    53. // 要拿到/book,就是最后一个/到最后一个.为止
    54. uri= uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
    55. /**
    56. * 思路:
    57. * 以前我们从map集合中获取,那样的话就行不通了。那么该怎么办呢?此时我们就需要修改xml配置文件了。
    58. * 通过配置文件拿到全路径名,再通过反射实例化
    59. */
    60. //Action action = actions.get(uri);
    61. //取到.action之后 Action action = actions.get(uri);里面定义了对应的方法
    62. //相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
    63. ActionModel actionModel = configModel.pop(uri);
    64. if(actionModel==null) {
    65. throw new RuntimeException("action config eroer 配置错误");
    66. }
    67. String type = actionModel.getType();//拿到type值
    68. //拿到type值之后就能通过路径名反射实例化 ------type是Action子控制器的全路径名
    69. try {
    70. Action action = (Action) Class.forName(type).newInstance();
    71. //此时的action 是bookAction
    72. if(action instanceof ModelDriven) {
    73. ModelDriven md=(ModelDriven) action;
    74. //action指的是bookAction,而bookAction又实现了ModelDriven接口,然后把bookAction转换成ModelDriven接口
    75. Object model = md.getModel();//model指的是bookAction中的book实例
    76. //给model中的属性赋值,要接收前端jsp传递过来的参数
    77. //PropertyUtils.getProperty(bean, name);//从某一个对象里面取某一个值
    78. //将前端所有参数值封装进实体类
    79. BeanUtils.populate(model, req.getParameterMap());
    80. // System.out.println(model);
    81. }
    82. //正式调用方法之前,book中的属性要被赋值
    83. // action.execute(req, resp);
    84. String retult = action.execute(req, resp);
    85. ForwardModel forwardModel = actionModel.pop(retult);
    86. if(forwardModel==null) {
    87. throw new RuntimeException("forward config error 配置错误");
    88. }
    89. // /bookList.jsp/index.jsp
    90. String path = forwardModel.getPath();
    91. //拿到是否需要转发的配置
    92. boolean redirect = forwardModel.getRedirect();
    93. if(redirect)
    94. resp.sendRedirect(req.getServletContext().getContextPath()+path);
    95. else
    96. req.getRequestDispatcher(path).forward(req, resp);
    97. } catch (Exception e) {
    98. // TODO Auto-generated catch block
    99. e.printStackTrace();
    100. } //Action的实例
    101. }
    102. //由原来的actions.get(uri)变成了从模型对象里取
    103. }

     效果:

    我们可以看到原本Book没有值的当再一次点击的时候Book中又有值。 他大大减少了代码量,不管之后有多少属性,都可以接受属性值和封装

    三、 对于方法执行结果转发重定向优化

    对方法的执行结果进行优化,如果增加成功就用转发,失败就用重定向。 

    编辑.xml配置文件:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <config>
    3. <action path="/book" type="com.oyang.servlet.BookAction">
    4. <forward name="failed" path="/demo2.jsp" redirect="false" />
    5. <forward name="success" path="/demo3.jsp" redirect="true" />
    6. </action>
    7. <action path="/order" type="com.oyang.servlet.OrderAction">
    8. <forward name="failed" path="/demo2.jsp" redirect="false" />
    9. <forward name="success" path="/demo3.jsp" redirect="true" />
    10. </action>
    11. </config>

    对子控制器进行优化 Action :

    1. package com.oyang.framework;
    2. import javax.servlet.http.HttpServletRequest;
    3. import javax.servlet.http.HttpServletResponse;
    4. /**
    5. * 子控制器
    6. * 对应请求的处理人
    7. * @author yang
    8. *
    9. */
    10. public interface Action {
    11. String execute(HttpServletRequest request, HttpServletResponse response);
    12. }

     ActionSupport类:

    1. package com.oyang.framework;
    2. import java.lang.reflect.Method;
    3. import javax.servlet.http.HttpServletRequest;
    4. import javax.servlet.http.HttpServletResponse;
    5. public class ActionSupport implements Action {
    6. @Override
    7. public String execute(HttpServletRequest request, HttpServletResponse response) {
    8. String methodName = request.getParameter("methodName");
    9. // methodName可能是add/del/edit/list/load/...
    10. // 前台传递什么方法,就调用当前类的对应方法
    11. try {
    12. Method m = this.getClass()// 当前类.class
    13. .getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
    14. // 打开权限
    15. m.setAccessible(true);
    16. // 调用当前类实例的methodName 方法
    17. return (String) m.invoke(this, request, response);
    18. } catch (Exception e) {
    19. // TODO Auto-generated catch block
    20. e.printStackTrace();
    21. }
    22. return null;
    23. }
    24. }

    DispatcherServlet:

    上述代码有,在此标记出来 

    BookAction:(更改返回值)

    1. package com.oyang.servlet;
    2. import javax.servlet.http.HttpServletRequest;
    3. import javax.servlet.http.HttpServletResponse;
    4. import com.oyang.entity.Book;
    5. import com.oyang.framework.Action;
    6. import com.oyang.framework.ActionSupport;
    7. import com.oyang.framework.ModelDriven;
    8. public class BookAction extends ActionSupport implements ModelDriven<Book>{
    9. private Book book=new Book();
    10. private String list(HttpServletRequest request, HttpServletResponse response) {
    11. System.out.println("在同一个Servlet中调用list查询方法");
    12. return "success";
    13. }
    14. private void load(HttpServletRequest request, HttpServletResponse response) {
    15. System.out.println("在同一个Servlet中调用load回显方法");
    16. }
    17. private void edit(HttpServletRequest request, HttpServletResponse response) {
    18. System.out.println("在同一个Servlet中调用edit修改方法");
    19. }
    20. private void del(HttpServletRequest request, HttpServletResponse response) {
    21. System.out.println("在同一个Servlet中调用del删除方法");
    22. }
    23. private String add(HttpServletRequest request, HttpServletResponse response) {
    24. // String bid = request.getParameter("bid");
    25. // String bname = request.getParameter("bname");
    26. // String price = request.getParameter("price");
    27. // Book b=new Book(Integer.valueOf(bid), bname, Float.valueOf(price));
    28. //BookDao.add(b);
    29. // System.out.println(book);
    30. //上述问题:如果一张表有20乃至50行数据,那岂不是要写那么多行去接收?如果有10张20条数据以上的表,那一个增加方法就得写很多行,甚至还有可能写错。大大的限制了我们的开发速度和效率
    31. //为了避免上述问题,如果上述代码可以不写就好了,那就不会这么麻烦了。能否让它们默认就接收那么多值呢?那我们就去生产这样的一个框架
    32. System.out.println("在同一个Servlet中调用add新增方法");
    33. return "failed";
    34. }
    35. @Override
    36. public Book getModel() {
    37. return book;
    38. }
    39. }

    新建两个jsp界面,用于演示跳转转发&重定向

     

    我们点击新增后的运行结果:

     

    四、框架配置文本优化

    以后我们使用的框架有很多,很有可能会重名,但是换了名字程序就跑不了了

    对此问题我们只需要更改web.xml文件的配置就行了

     

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
    3. <display-name>Oyang_mvc</display-name>
    4. <servlet>
    5. <servlet-name>mvc</servlet-name>
    6. <servlet-class>com.oyang.framework.DispatcherServlet</servlet-class>
    7. <init-param><!-- 可以把值传到 configLocation中-->
    8. <param-name>configLocation</param-name>
    9. <param-value>/oyang.xml</param-value>
    10. </init-param>
    11. </servlet>
    12. <servlet-mapping>
    13. <servlet-name>mvc</servlet-name>
    14. <url-pattern>*.action</url-pattern>
    15. </servlet-mapping>
    16. </web-app>

     试试运行结果:

    点击增加

     跳转到转发界面

     

     看一下控制台打印的值

     敬请期待下篇J2EE基础-自定义MVC(下),下篇将会优化的更多


     OK,今日的学习就到此结束啦,如果对个位看官有帮助的话可以留下免费的赞哦(收藏或关注也行),如果文章中有什么问题或不足以及需要改正的地方可以私信博主,博主会做出改正的。个位看官,小陽在此跟大家说拜拜啦

  • 相关阅读:
    flutte: 可滚动列表
    [k8s] 常见yml配置和详细解释
    Android 3D Launcher锁定IMU界面
    Centos8安装CDH解决不兼容问题
    ApiSix网关环境搭建及简单使用(Windows)
    诊断DLL——Visual Studio安装与dll使用
    Leetcode 第 372 场周赛题解
    基于TCP的聊天系统
    渗透测试学习day3
    Java8新特性
  • 原文地址:https://blog.csdn.net/weixin_65211978/article/details/125499671