目录
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。 它是一种软件设计典范。
用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
提高了程序的可维护性、可移植性、可扩展性与可重用性,降低了程序的开发难度。它主要分模型、视图、控制器三层。
- 模型(model): 它是应用程序的主体部分,主要包括业务逻辑模块(web项目中的dao类)和数据模块(pojo类)。pojo一般可以叫做实体域模型,dao和service称为过程域模型。
- 视图(view): 用户与之交互的界面、在web中视图一般由jsp,html组成,其他的还包括android,ios等等。
- 控制器(controller): 接收来自界面的请求 并交给模型进行处理 在这个过程中控制器不做任何处理只是起到了一个连接的做用。
不足的地方:
- 增加系统结构和实现的复杂性。对于简单的界面,严格遵守MVC,需要使模型、视图与控制器分离,增加系统复杂性
- 视图和控制器之间的关系太过紧密了
核心组件说明:
- 中央控制器(ActionServlet): 复杂接收所有的请求,并分别给控制器具体处理。
- 自控制器(Action):负责处理中央处理器分配的请求
- 视图(view): jsp页面,负责显示
- 模型(Model): 负责业务处理逻辑
通过servlet来实现一个中央控制器,负责所有请求的接收。(后续中央控制器在再将请求转发给各个子控制器,此处可以先把请求接进来,转发功能后面再加)
- /**
- * 中央控制器,负责接收所有的请求并分别给控制器具体处理
- * @author Administrator
- */
- @WebServlet("*.action")
- public class ActionDispatchServlet extends HttpServlet {
-
- @Override
- public void doGet(HttpServletRequest req, HttpServletResponse resp) {
- doPost(req, resp);
- }
-
- @Override
- public void doPost(HttpServletRequest req, HttpServletResponse resp) {
- System.out.println("dopost ..... ");
- }
-
- }
Action接口定义了每个子控制器需要遵循的行为,使得所有的子控制器都有一个同一的抽象类型,所以我们可以在中央控制器中使用Action接口类型来引用所有的子控制器。这样就为用户扩展自定义的子控制器提供了条件。
- /**
- * 每个子控制器必须实现该接口,负责处理中央处理器分配的请求
- * @author Administrator
- */
- public interface Action {
-
- /**
- * 处理请求
- * @param req 请求
- * @param resp 响应
- * @return String 返回转发或重定向的jsp页面名称
- */
- String exeute(HttpServletRequest req, HttpServletResponse resp);
-
- }
为了方便调试,我这里实现了两个子控制器(= ̄ω ̄=)
- public class BookAction implements Action {
-
- @Override
- public String getBooks(HttpServletRequest req, HttpServletResponse resp) {
- System.out.println("BookAction...");
- return "books";
- }
-
- }
- public class StudentAction implements Action{
-
- @Override
- public String execute(HttpServletRequest req, HttpServletResponse resp) {
- System.out.println("StudentAction");
- return "students";
- }
为了便于理解,我们可以分步骤的,循序渐进的完善中央控制器:
- @WebServlet("*.action")
- public class ActionDispatchServlet extends HttpServlet{
-
- //用于保存path与action子控制器的映射
- public static Map
actionMap = new HashMap<>(); -
- static {
- actionMap.put("/studentAction", new StudentAction());
- actionMap.put("/bookAction", new BookAction());
- }
-
-
- @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 {
-
- String spath = req.getServletPath();
- String path = spath.split("\\.")[0];
-
- Action action = getActionByPath(path);
-
- String name = action.execute(req, resp);
-
- System.out.println(name);
-
- }
-
-
-
- }
在上面的示例中,在中央控制器中直接创建action子控制器,如果新增一个子控制器需要在中央控制器中添加,这样并不实用。 为了增加灵活性,可以将action转移到配置文件中配置,中央控制器通过配置来初始化action子控制器。
(1)此时需要将config.xml文件的解析和建模项目的功能集成进来。
(ConfigModel,ActionModel,ForwardModel,ConfigModelFactory)
(2)在项目的src目录下加入如下配置文件(config.xml)
- config[
- config (action*)>
- action (forward*)>
- forward EMPTY>
- action
- path CDATA #REQUIRED
- type CDATA #REQUIRED
- >
- forward
- name CDATA #REQUIRED
- path CDATA #REQUIRED
- redirect (true|false) "false"
- >
- ]>
- <config>
- <action path="/studentAction" type="com.zking.action.StudentAction">
- <forward name="students" path="/students.jsp" redirect="false"/>
- action>
- <action path="/bookAction" type="com.zking.action.BookAction">
- <forward name="books" path="/books.jsp" redirect="false"/>
- action>
- config>
(3) 完善中央处理器,通过配置文件来获取子控制器配置
- @WebServlet("*.action")
- public class ActionDispatchServlet extends HttpServlet{
-
- private static ConfigModel configModel = ConfigModelFactory.getConfig();
-
-
- @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 {
-
- String spath = req.getServletPath();
- String path = spath.split("\\.")[0];
-
- Action action = getActionByPath(path);
-
-
- //请求转发
- String name = action.execute(req, resp);
-
- ForwardModel forward = configModel.find(path).find(name);
- if(forward.isRedirect()) {
- resp.sendRedirect(req.getContextPath()+forward.getPath());
- } else {
- req.getRequestDispatcher(forward.getPath()).forward(req, resp);
- }
- }
-
- /**
- * 通过path获取Action的实例
- * @param path
- * @return
- */
- private Action getActionByPath(String path) {
- ActionModel am = configModel.find(path);
-
- try {
- Class extends Action> ac = (Class extends Action>)Class.forName(am.getType());
- return ac.newInstance();
- } catch (ClassNotFoundException
- | InstantiationException
- | IllegalAccessException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- }
注: 本例的实现中Action子控制器是多例模式的,及每个请求对应一个Action实例
(1)定义接口
- /**
- * 对于需要处理请求参数的Action可以通过实现该接口获取请求参数的
- * 处理能力,中央控制器将会使用该接口来获取Model对象,并统一处理
- * 参数
- * @author Administrator
- */
- public interface ModelDrive {
-
- Object getModel();
-
- }
(2)在中央处理器中加入请求参数的处理能力
- @WebServlet("*.action")
- public class ActionDispatchServlet extends HttpServlet{
-
- private static ConfigModel configModel = ConfigModelFactory.getConfig();
-
- @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 {
-
- String spath = req.getServletPath();
- String path = spath.split("\\.")[0];
-
- Action action = getActionByPath(path);
-
- //设置查询参数
- if(action instanceof ModelDrive) {
- ModelDrive m = (ModelDrive)action;
- Object bean = m.getModel();
-
- try {
- BeanUtils.populate(bean,req.getParameterMap());
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new ActionDispatchSetParameterException("在中央控制器中处理参数的时候发生异常");
- }
-
- }
-
- //请求转发
- String name = action.execute(req, resp);
-
- ForwardModel forward = configModel.find(path).find(name);
- if(forward.isRedirect()) {
- resp.sendRedirect(req.getContextPath()+forward.getPath());
- } else {
- req.getRequestDispatcher(forward.getPath()).forward(req, resp);
- }
- }
-
- /**
- * 通过path获取Action的实例
- * @param path
- * @return
- */
- private Action getActionByPath(String path) {
- ActionModel am = configModel.find(path);
-
- try {
- Class extends Action> ac = (Class extends Action>)Class.forName(am.getType());
- return ac.newInstance();
- } catch (ClassNotFoundException
- | InstantiationException
- | IllegalAccessException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- }
(3) 处理请求参数中的null及空字符串转换问题
方法一:在中央控制器中加入处理请求参数中的null及空字符串转换的代码
- @WebServlet("*.action")
- public class ActionDispatchServlet extends HttpServlet{
-
- private static ConfigModel .....
-
- /**
- * 当Servlet 容器启动Web应用时调用该方法。
- * 在调用完该方法之后,容器再对Filter 初始化,
- * 并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
- */
- static {
- ConvertUtils.register(new IntegerConverter(null), Integer.class);
- ConvertUtils.register(new FloatConverter(null), Float.class);
- ConvertUtils.register(new DoubleConverter(null), Double.class);
- ConvertUtils.register(new LongConverter(null), Long.class);
- ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
- }
-
- @Override
- protected void doGet......
-
-
-
- }
注:省略的代码在前几个中央控制器中有写,这里我就不重复写了。
方法二:加入一个监听器,在应用启动时注册转换器。
- /**
- * ServletContextListener接口为Servlet API中的接口,用于监听ServletContext对象的生命周期。
- * 当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent事件,该事件由
- * ServletContextListener来处理。
- * @author Administrator
- */
- @WebListener
- public class BeanUtilsListener implements ServletContextListener{
-
- /**
- * 当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,
- * 容器会先销毁所有的Servlet和Filter 过滤器。
- */
- @Override
- public void contextDestroyed(ServletContextEvent arg0) {
- // TODO Auto-generated method stub
- }
-
- /**
- * 当Servlet 容器启动Web应用时调用该方法。
- * 在调用完该方法之后,容器再对Filter 初始化,
- * 并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
- */
- @Override
- public void contextInitialized(ServletContextEvent arg0) {
- ConvertUtils.register(new IntegerConverter(null), Integer.class);
- ConvertUtils.register(new FloatConverter(null), Float.class);
- ConvertUtils.register(new DoubleConverter(null), Double.class);
- ConvertUtils.register(new LongConverter(null), Long.class);
- ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
- }
-
-
- }
每个Action只能有一个execute方法,如果处理一个模块的增删改查则需要单独编写多个Action,这样会比较麻烦。如果在一个Action实例中可以处理多个请求方法,则框架会更加灵活。
(1) 构建抽象类
- public abstract class AbstractAction implements Action{
-
- //获取methodName所指定的方法,然后通过反射机制方法调用
- @Override
- public String execute(HttpServletRequest req, HttpServletResponse resp) {
- String methodName = req.getParameter("methodName");
- Class extends Action> clazz = (Class extends Action>)this.getClass();
- try {
- Method method = clazz.getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
- return (String)method.invoke(this, req,resp);
- } catch (NoSuchMethodException
- | SecurityException
- | IllegalAccessException
- | IllegalArgumentException
- | InvocationTargetException e) {
- throw new RuntimeException(e);
- }
-
- }
-
-
- }
(2) 自定义的Action子控制器示例
- public class BookAction extends AbstractAction implements ModelDrive{
-
- private Book book = new Book();
-
- @Override
- public Object getModel() {
- return book;
- }
-
- public String getBooks(HttpServletRequest req, HttpServletResponse resp) {
- System.out.println("BookAction getBooks..."+book);
- return "books";
- }
-
-
- }
注意:在请求中需要添加一个methodName的固定参数,该参数指定了需要调用的Action中的方法的名称。
示例:http://localhost:8080/项目名称/bookAction.action?methodName=getBooks&id=1000
getBooks是子控制器的方法名
将自定义mvc框架打成jar包,以便于在其他项目中使用。
项目 --(右击)-->Export

选择JAR 文件


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