自定义mvc框架工作原理
什么是mvc
MVC全名:Model View Controller,其中Model(模型层)、View(视图层)、Controller(控制层)
三层架构和MVC的区别
三层架构是一个经典的分层思想,将开发模式分为三层,每个人专注自己擅长模块即可
MVC是一种设计模式,其目的是让视图和业务逻辑分开
一.优化中央控制器,子控制器
xml建模
jar包
中央控制器
package com.lsy.framework;
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 org.apache.commons.beanutils.PropertyUtils;
import org.dom4j.DocumentException;
import com.lsy.servlet.BookAction;
import com.lsy.servlet.GoodsAaction;
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet{
//在当前中央控制器中必然会有所有子控制器集合
//缺陷:如果有商品的增删改查-->意味着要改动代码-->代码的设计不够灵活
//思考:在不改动代码的情况下,中央控制器也能找到对应的子控制器去处理浏览器请求
//方案:我把加子控制器的逻辑/动作,放到配置文件中完成(Dbutil改连接信息是放在代码中完成/现在是放在Properties文件中完成)
//放在配置文件中完成的好处在于代码更加灵活,修改相关信息不用动代码
//ConfigModel对象又通过建模的知识,把所有的配置信息给读取过来了
//private Map<String, ActionSupport> actions=new HashMap<>();
//现在在xml中改
private ConfigModel configModel=null;
@Override
public void init() throws ServletException {
//在集合中就有了一个子控制器
//actions.put("/book", new BookAction());
//缺陷显示
//actions.put("/goods",new GoodsAaction());
try {
configModel=ConfigModelFactory.build();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//把子控制器与浏览器请求关联起来,“寻找”能够处理请求的子控制器
/**
* 思路
* 1、uri-->book
* 2、通过/book字符串在actions找到BookAction
* 3、调用BookAction的add,想要调用add,实际上只要统一调用execute就可以了
*/
//获取到浏览器的请求地址
String uri = req.getRequestURI();
//通过截取获取到请求地址
uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));
//通过/book字符串在actions找到BookAction
//原来在Map中寻找子控制器-->在配置文件中寻找子控制器
ActionModel actionModel = configModel.pop(uri);
String type = actionModel.getType();
ActionSupport action;
try {
action = (ActionSupport) Class.forName(type).newInstance();
action.execute(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//不需要在代码中动,在xml中动
<?xml version="1.0" encoding="UTF-8"?>
<config>
<!--
在这里每加一个配置,就相当于actions.put("/goods",new GoodsAaction());
这样就解决了代码灵活性的问题
-->
<action path="/book" type="com.lsy.servlet.BookAction">
<forward name="list" path="/bookList.jsp" redirect="false" />
<forward name="toEdit" path="/bookEdit.jsp" redirect="true" />
</action>
<action path="/goods" type="com.lsy.servlet.GoodsAction">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
<action path="/order" type="com.lsy.servlet.OrderAction">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
</config>
子控制器
package com.lsy.framework;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 子控制器用于处理浏览器请求
*/
public interface Action {
//这个方法就是add/ref进行向上抽取的方法
//作用:能够处理浏览器的“所有”请求,包括add/ref
public void execute(HttpServletRequest req,HttpServletResponse resp);
// private void add(HttpServletRequest req, HttpServletResponse resp) {
// System.out.println("--增加--");
//
// }
//
// private void ref(HttpServletRequest req, HttpServletResponse resp) {
// System.out.println("--ref--");
//
// }
}
ActionSupport
用于实现上面的action接口
作用:能够处理浏览器的“所有”请求,意思就是可以获取到当前的方法
package com.lsy.framework;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 作用:能够处理浏览器的“所有”请求,包括add/ref
*
*/
public class ActionSupport implements Action{
@Override
public void execute(HttpServletRequest req, HttpServletResponse resp) {
String methodName = req.getParameter("methodName");
try {
Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
m.setAccessible(true);
m.invoke(this,req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
BookAction继承ActionSupport
通过继承将方法反射调用过来,减少代码重复,方便
-
package com.lsy.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.lsy.framework.ActionSupport; public class BookAction extends ActionSupport{ //从父类继承了execute方法,就把反射动态调用方法的代码继承过来了 //当前子控制器在哪里调用?把子控制器与浏览器请求关联起来 //以下只需要关注业务 private void add(HttpServletRequest req, HttpServletResponse resp) { System.out.println("--增加--"); } private void list(HttpServletRequest req, HttpServletResponse resp) { System.out.println("--查询--"); } private void ref(HttpServletRequest req, HttpServletResponse resp) { System.out.println("--ref--"); } private void goods(HttpServletRequest req, HttpServletResponse resp) { System.out.println("--购物--"); } }
实体类代码冗余 Book实体类
模型驱动接口 ModelDriver<T> T是实体类,驱动接口可以进行参数封装
package com.lsy.framework; import org.apache.commons.beanutils.BeanUtils; import com.lsy.entity.Book; /** * 模型驱动接口作用,帮助中央控制器完成参数封装工程 * BeanUtils.populate(bean, req.getParameterMap());相当于下面代码 * Book book=new Book(); book.setBid(req.getParameter("bid")); book.setBname(req.getParameter("bname")); book.setPrice(req.getParameter("price")); book.setAthor(req.getParameter("athor")); book.setPublish(req.getParameter("publish")); * @param <T> */ public interface ModelDriver<T> { /** * GoodsAction-->goods * BookAction-->book * @return */ T getModel(); }
BookAction (extends ActionSupport implements ModelDriver<Book>)
继承ActionSupport实现模型驱动 T传入要封装的实体类
package com.lsy.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.lsy.entity.Book; import com.lsy.framework.ActionSupport; import com.lsy.framework.ModelDriver; public class BookAction extends ActionSupport implements ModelDriver<Book>{ //从父类继承了execute方法,就把反射动态调用方法的代码继承过来了 //当前子控制器在哪里调用?把子控制器与浏览器请求关联起来 Book book=new Book(); //以下只需要关注业务 private void add(HttpServletRequest req, HttpServletResponse resp) { // book.setBid(req.getParameter("bid")); // book.setBname(req.getParameter("bname")); // book.setPrice(req.getParameter("price")); // book.setAthor(req.getParameter("athor")); // book.setPublish(req.getParameter("publish")); System.out.println(book); System.out.println("--增加--"); } @Override public Book getModel() { // TODO Auto-generated method stub return book; } }
结果页面统一跳转
中央控制器
package com.lsy.framework; 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 org.apache.commons.beanutils.PropertyUtils; import org.dom4j.DocumentException; import com.lsy.servlet.BookAction; import com.lsy.servlet.GoodsAaction; @WebServlet("*.action") public class DispatchServlet extends HttpServlet{ //在当前中央控制器中必然会有所有子控制器集合 //缺陷:如果有商品的增删改查-->意味着要改动代码-->代码的设计不够灵活 //思考:在不改动代码的情况下,中央控制器也能找到对应的子控制器去处理浏览器请求 //方案:我把加子控制器的逻辑/动作,放到配置文件中完成(Dbutil改连接信息是放在代码中完成/现在是放在Properties文件中完成) //放在配置文件中完成的好处在于代码更加灵活,修改相关信息不用动代码 //ConfigModel对象又通过建模的知识,把所有的配置信息给读取过来了 //private Map<String, ActionSupport> actions=new HashMap<>(); //现在在xml中改 private ConfigModel configModel=null; @Override public void init() throws ServletException { //在集合中就有了一个子控制器 //actions.put("/book", new BookAction()); //缺陷显示 //actions.put("/goods",new GoodsAaction()); try { configModel=ConfigModelFactory.build(); } catch (Exception e) { e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //把子控制器与浏览器请求关联起来,“寻找”能够处理请求的子控制器 /** * 思路 * 1、uri-->book * 2、通过/book字符串在actions找到BookAction * 3、调用BookAction的add,想要调用add,实际上只要统一调用execute就可以了 */ //获取到浏览器的请求地址 String uri = req.getRequestURI(); //通过截取获取到请求地址 uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf(".")); //通过/book字符串在actions找到BookAction //原来在Map中寻找子控制器-->在配置文件中寻找子控制器 ActionModel actionModel = configModel.pop(uri); String type = actionModel.getType(); ActionSupport action; try { action = (ActionSupport) Class.forName(type).newInstance(); action.execute(req, resp); } catch (Exception e) { e.printStackTrace(); } } } //不需要在代码中动,在xml中动 <?xml version="1.0" encoding="UTF-8"?> <config> <!-- 在这里每加一个配置,就相当于actions.put("/goods",new GoodsAaction()); 这样就解决了代码灵活性的问题 --> <action path="/book" type="com.lsy.servlet.BookAction"> <forward name="list" path="/bookList.jsp" redirect="false" /> <forward name="toEdit" path="/bookEdit.jsp" redirect="true" /> </action> <action path="/goods" type="com.lsy.servlet.GoodsAction"> <forward name="failed" path="/login.jsp" redirect="false" /> <forward name="success" path="/main.jsp" redirect="true" /> </action> <action path="/order" type="com.lsy.servlet.OrderAction"> <forward name="failed" path="/login.jsp" redirect="false" /> <forward name="success" path="/main.jsp" redirect="true" /> </action> </config>
子控制器
package com.lsy.framework; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 子控制器用于处理浏览器请求 */ public interface Action { //这个方法就是add/ref进行向上抽取的方法 //作用:能够处理浏览器的“所有”请求,包括add/ref public void execute(HttpServletRequest req,HttpServletResponse resp); // private void add(HttpServletRequest req, HttpServletResponse resp) { // System.out.println("--增加--"); // // } // // private void ref(HttpServletRequest req, HttpServletResponse resp) { // System.out.println("--ref--"); // // } }
ActionSupport
-
package com.lsy.framework; import java.lang.reflect.Method; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 作用:能够处理浏览器的“所有”请求,包括add/ref * */ public class ActionSupport implements Action{ @Override public void execute(HttpServletRequest req, HttpServletResponse resp) { String methodName = req.getParameter("methodName"); try { Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class); m.setAccessible(true); m.invoke(this,req, resp); } catch (Exception e) { e.printStackTrace(); } } }
BookAction
package com.lsy.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.lsy.framework.ActionSupport; public class BookAction extends ActionSupport{ //从父类继承了execute方法,就把反射动态调用方法的代码继承过来了 //当前子控制器在哪里调用?把子控制器与浏览器请求关联起来 //以下只需要关注业务 private void add(HttpServletRequest req, HttpServletResponse resp) { System.out.println("--增加--"); } private void list(HttpServletRequest req, HttpServletResponse resp) { System.out.println("--查询--"); } private void ref(HttpServletRequest req, HttpServletResponse resp) { System.out.println("--ref--"); } private void goods(HttpServletRequest req, HttpServletResponse resp) { System.out.println("--购物--"); } }
xml配置
<?xml version="1.0" encoding="UTF-8"?> <config> <action path="/book" type="com.lsy.servlet.BookAction"> <forward name="list" path="/bookList.jsp" redirect="false" /> <forward name="toEdit" path="/bookEdit.jsp" redirect="true" /> </action> <action path="/goods" type="com.lsy.servlet.GoodsAction"> <forward name="failed" path="/login.jsp" redirect="false" /> <forward name="success" path="/main.jsp" redirect="true" /> </action> </config>
总之,了解思路就行了,知道运行的思路,前端发送请求,后台进行处理与跳转,进行返回,还有一些减少重复代码的方法,可能比较绕,慢慢理解