前言:
------相比于上次发布的自定义MVC的实现,这次它的优化点是什么呢??
------上篇在ActionServlet里面,每次新增一个都要在里面添加,我们最终要将这个做成架包来使用,所以里面的数据必须要是活的,所以这次的就是把之前的死数据变成活的!!
一.自定义MVC
自定义MVC就是去了解每个标签后的底层原理逻辑,也可以根据自己的开发需求去开发一架包来使用,这个对于架构师来说是需要掌握的
-------它的好处和用途:
- 分离关注点:MVC架构将应用程序的不同方面(数据、展示、控制逻辑)分离开来,使得代码更易于理解和维护。开发人员可以专注于特定领域的开发,提高开发效率。
- 可扩展性和可重用性:通过将应用程序分为模型、视图和控制器,可以更容易地添加新功能或修改现有功能,而不会影响其他部分的代码。这种模块化的结构使得代码更易于重用,可以更高效地开发和维护应用程序。
- 并行开发:MVC架构允许开发人员在不同的模块上并行工作,提高开发效率和团队协作能力。例如,模型开发人员可以同时开发数据存储和处理逻辑,视图开发人员可以同时进行用户界面设计,控制器开发人员可以编写业务逻辑。
- 测试和调试:MVC架构使得单元测试和调试更加容易。由于模型、视图和控制器是相互独立的,可以更容易地对每个部分进行测试和调试,确保代码的质量和可靠性。
- 代码复用:MVC架构中的组件可以根据需要进行重用,提高开发效率。例如,可以使用相同的模型和控制器来支持不同的视图,或者可以使用相同的视图来展示不同的模型数据
二.优化版
注意:查询必然转发,增删改必然重定向
下面这个是优化版需要用到的配置文件,要添加什么页面,就只需要改这个配置文件就可以了
先上代码:ActionServlet
--------因为要将这个做成架包,所以里面的东西不能是死的,不然就像第四点,就会有新的就会改变,
所以我们就用到了建模,将它变成活,到时候只要在配置文件里面添加即可
以前都是将子控制器放在Map集合中,现在要将其放在MVC.XML文件中
package com.yinzi.jar; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils; import com.yinzi.Servlet.UserServlet; import com.yinzi.jar.model.ActionModel; import com.yinzi.jar.model.ConfigModel; import com.yinzi.jar.model.ConfigModelFactory; import com.yinzi.jar.model.ForwordModel; /** * 中央控制器:负责接受请求,然后安排合适的servlet来执行请求 * @author yinzi * @2023年6月29日 */ @WebServlet("*.servlet") public class ActionServlet extends HttpServlet{ //3.因为要通过截取的/User 去找UserServlet,所以创建一个Map集合 // Map<String, Action> actionMap=new HashMap<String, Action>(); //因为要将这个做成架包,所以里面的东西不能是死的,不然就像第四点,就会有新的就会改变, // 所以我们就用到了建模,将它变成活,到时候只要在配置文件里面添加即可 // 以前都是将子控制器放在Map集合中,现在要将其放在MVC.XML文件中 private ConfigModel configmodel; //4.进行初始化,给configmodel赋值 @Override public void init() throws ServletException { try { //com.yinzi.jar.model.ConfigModel@62bfcdce configmodel=ConfigModelFactory.build();//调用方法 } catch (Exception e) { e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //http://localhost:8080/MVC/User.jsp //获取路径,截取 User 目的是为了知道需要调用哪个servlet里的方法 //1.获取路径 String uri = req.getRequestURI();///MVC/User.servlet //2.截取 User uri=uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));//截取的结果为:/User //5.获取 根据uri找到Servlet ActionModel actionmodel = configmodel.pop(uri); //判断,要是配置文件里没有这路径怎么办?? if(actionmodel==null) {//如果为空就抛出一个异常 throw new RuntimeException("action not config");//该action没有配置 } //然后拿到路径,配置文件type是路径 String type = actionmodel.getType(); //反射实例化 try { Action action = (Action) Class.forName(type).newInstance(); //判断userServlet有没有实现ModelDriver接口 if(action instanceof ModelDriver) { ModelDriver md=(ModelDriver) action; Object model = md.getModel(); //以键值对的形式 Map<String, String[]> map = req.getParameterMap(); //添加 BeanUtils.populate(model, map); } //调取 //具体业务执行的返回值 String execute = action.execute(req, resp); ForwordModel forwordModel = actionmodel.pop(execute); //判断是重定向还是转发 if(forwordModel!=null) { boolean redirect = forwordModel.getRedirect(); String path = forwordModel.getPath(); if(redirect) {//如果是重定向 resp.sendRedirect(req.getContextPath()+"/" +path); }else {//否则就是转发 req.getRequestDispatcher(path).forward(req,resp); } } } catch (Exception e) { e.printStackTrace(); } } }
几乎每一行俺都注释啦,应该是看的懂得,如果有什么不懂可以在下方评论区问俺哦~~
这个类主要是获取路径,从配置文件中获取,通过截取的获取路径进行执行浏览器传来的请求。这里面因为有些要带参数,所以要进行判断,是否为重定向,否则为转发
Action类
这个里面就是改变了方法的返回参数
package com.yinzi.jar; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 子控制器:是其他类增删改查类的父类,它去调浏览器请求的方法,所有这里运用到反射 * 来处理请求,通过它来调相应Servlet * @author yinzi * @2023年6月29日 * */ public class Action { protected String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取参数methodName String methodName = req.getParameter("methodName"); String res=""; try { Method m = this.getClass().getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class); //打开访问权限 m.setAccessible(true); res = (String) m.invoke(this, req,resp); } catch (Exception e) { e.printStackTrace(); } return res; } }
Servlet方法类
这个方法作为演示所以就没有写的很详细,简单的模拟了一下
package com.yinzi.Servlet; import java.io.IOException; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.tomcat.jni.User; import com.yinzi.jar.Action; import com.yinzi.jar.ModelDriver; /** * 用户的Servlet,在这个方法里写增删改查 * @author yinzi * @2023年6月29日 * */ public class UserServlet extends Action implements ModelDriver<User>{ private User user=new User(); public String add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Map<String, String[]> map = req.getParameterMap(); System.out.println("我是User新增"); req.setAttribute("content","呵呵"); // resp.sendRedirect("res.jsp"); return "NO"; } public String del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是User删除"); req.setAttribute("content","呵呵"); // resp.sendRedirect("res.jsp"); return "No"; } public String upd(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是User修改"); req.setAttribute("content","呵呵"); // resp.sendRedirect("res.jsp"); return "No"; } public String query(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是User查询所有"); req.setAttribute("content","呵呵"); // req.getRequestDispatcher("res.jsp"); return "Yes"; } @Override public User getModel() { return user; } }
三.Model
接下来是一些之前也发布过的建模,一些逻辑代码
ConfigModel类
package com.yinzi.jar.model; import java.util.HashMap; import java.util.Map; public class ConfigModel { private Map<String , ActionModel> amap=new HashMap<String, ActionModel>(); //将Action往Config里面放 压栈 public void push(ActionModel actionmodel) { amap.put(actionmodel.getPath(), actionmodel); } //将Action从Config里通过path值取出来 public ActionModel pop(String path) { return amap.get(path); } }
ConfigFactoryModel (工厂模型)
这里面的逻辑是比较强的,需要好好去理解,我们看到那个配置文件,有三个标签config,action,forword,这三者是层层包裹的,所以当config里面需要东西时,需要保证action里面有东西,那么就需要保证forword里面有东西。获取里面的属性
package com.yinzi.jar.model; import java.io.InputStream; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * 放数据 * @author yinzi * */ public class ConfigModelFactory { public static ConfigModel build(String xmlPath) throws Exception { ConfigModel conmodel = new ConfigModel(); //获取xml文件 InputStream in = ConfigModelFactory.class.getResourceAsStream(xmlPath); SAXReader sr=new SAXReader(); Document doc = sr.read(in); //要想ConfigModel有内容,要保证ActionModel里面有内容,然后将ActionModel的内容加入到ConfigModel中 List<Element> action = doc.selectNodes("/config/action"); for (Element actionele : action) { //将数据加到ActionModl中 ActionModel actionModel = new ActionModel(); //获得action的属性 actionModel.setPath(actionele.attributeValue("path")); actionModel.setType(actionele.attributeValue("type")); //要想ActionModel有内容,要保证ForwordModel里面有内容,然后将ForwordModel的内容加入到ActionModel中 List<Element> forword = actionele.selectNodes("forward"); for (Element forwordele : forword) { //将数据加到forwordModl中 ForwordModel forwordModel = new ForwordModel(); //获取forwrad属性 forwordModel.setName(forwordele.attributeValue("name")); forwordModel.setPath(forwordele.attributeValue("path")); forwordModel.setRedirect(!"false".equals(forwordele.attributeValue("redirect"))); //将forwrad放入action actionModel.push(forwordModel); } //然后将actionModel里的内容加到ConfigModel里面去 conmodel.push(actionModel); } return conmodel; } //调用有参的 public static ConfigModel build() throws Exception { return build("/mvc.xml"); } //测试 //静态方法只能调静态方法 public static void main(String[] args) throws Exception { ConfigModelFactory.build(); } }
ActionModel
package com.yinzi.jar.model; import java.util.HashMap; import java.util.Map; public class ActionModel { private String path; private String type; private Map<String, ForwordModel> fmap=new HashMap<String, ForwordModel>(); public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getType() { return type; } public void setType(String type) { this.type = type; } //将Forword往action里面放 压栈 public void push(ForwordModel forwordmodel) { fmap.put(forwordmodel.getName(), forwordmodel); } //将Forword从action里通过name值取出来 public ForwordModel pop(String name) { return fmap.get(name); } }
ForwordModel
package com.yinzi.jar.model; public class ForwordModel { private String name; private String path; private boolean redirect; public ForwordModel() { // TODO Auto-generated constructor stub } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public boolean getRedirect() { return redirect; } public void setRedirect(boolean redirect) { this.redirect = redirect; } public ForwordModel(String name, String path, boolean redirect) { super(); this.name = name; this.path = path; this.redirect = redirect; } @Override public String toString() { return "ForwordModel [name=" + name + ", path=" + path + ", redirect=" + redirect + "]"; } }
以上就是配置文件需要的Model
最后输出结果:
点击查询,则参数会带过来
其他三个则不会带