Custom MVC framework

Posted by George Botley on Sun, 19 Dec 2021 16:17:47 +0100

Mind map

I. MVC

introduce

MVC: full name: ModelViewController Model layer view layer controller

Deductive process

1. A method to build a servlet

Disadvantages: too many repeated codes, time-consuming and laborious

As follows:

 

2. A servlet calls all methods

Advantages: compared with the previous code reduction, there are multiple classes reduced to one

Disadvantages: each time a new method is added, the original logic needs to be changed, and the code is too redundant

code:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String method=request.getParameter("methodName");
		if("add".equals(method)) {
			add(request,response);
		}else if("edit".equals(method)) {
			edit(request,response);
		}else if("del".equals(method)) {
			del(request,response);
		}else if("list".equals(method)) {
			list(request,response);
		}
		
	}

	private void list(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("listbookdao.......");
		
	}

	private void del(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("delbookdao.......");
		
	}

	private void edit(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("editbookdao.......");
		
	}

	private void add(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("addbookdao.......");
		
	}

}

Method call:

< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = add" > New</a>
< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = delete" > delete</a>
< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = edit" > Modify</a>
< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = list" > query</a>

3. Reflection optimization

Thinking: we can realize the demand without using the original logic
Solution:
Which method to call actually depends on the methodname, and if is an unnecessary condition
The methodname method is called dynamically and is the methodname method of the current class instance

Summary:
Reflection can fix the defects above that change the code to solve the requirements problem
Reflecting this code is equivalent to a central controller and does not directly process browser requests
Number of sub consoles processing browser requests

code:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String method=request.getParameter("methodName");
		try {
			Method m = this.getClass().getDeclaredMethod(method,HttpServletRequest.class, HttpServletResponse);
		    m.setAccessible(true);
		    m.invoke(this,request,response);
		
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void list(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("listbookdao.......");
		
	}

	private void del(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("delbookdao.......");
		
	}

	private void edit(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("editbookdao.......");
		
	}

	private void add(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("addbookdao.......");
		
	}

}

Test:

II. Custom MVC framework

1. Thinking and analysis

Central controller and sub controller
The principle of custom mvc framework is formed into code to form a framework
         ActionServlet-->DispatchServlet(Springmvc)
why:
          1. If you want to add, delete, modify and query in bookservlet, you must write reflection in doPost method and dynamically call the new method
To add, delete, modify and check GoodsServlet, you must write reflection in the doPost method and dynamically call the new method
Conclusion: the new method code of reflection dynamic call is repeated, but this paragraph is necessary, that is, it needs to be further optimized
Optimize the central controller mentioned last time
         2. For addition, deletion, modification and query in BookServlet, it is still necessary to write doget/doPost methods for each Servlet
However, in fact, the code useful for processing business is often newly added. We only need to focus on business (add/delete /...)
Optimize the sub controller mentioned last time
        3. Solve the remaining two problems
Entity class parameters accept code redundancy (req. Getparameter (""), especially when there are many entity class attributes, they are encapsulated in the entity class)
                 req.getParammeter("bid")/req.getParammeter("bname")/req.getParammeter("price")...
Jump (forwarding, redirection) about the result page
                 req.getdispather("/index.jsp").forward(req,resp);
                resp.sendredirect("/index.jsp");

2. MVC working principle diagram

2. Optimize the central controller and sub controller (modeling with XML (refer to the previous notes for details))

1. Used packages and jar packages

 

JSP interface used

 

Central controller DispathServlet:

package com.DHM.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;

/**
 * The central controller
 * Servlet implementation class DispatchServlet
 */
//In XXX Call in at the end of action
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet {
//	There must be all sub controllers in the current central controller
//	Defect: if there is an addition, deletion, modification and query of goods -- > it means to change the code -- > the design of the code is not flexible enough
//	Thinking: without changing the code, the central controller can also find the corresponding sub controller to process the browser request
//	Scheme: I put the logic / action of adding sub controllers into the configuration file to complete (Dbutil changed the connection information into the code / now it is completed in the Properties file)
//	The advantage of putting it in the configuration file is that the code is more flexible and there is no need to modify the relevant information
//	private Map<String, ActionSupport> actions = new HashMap<>();
//	The configModel object reads all the configuration information through the modeling knowledge
	
	private configModel configModel=null;
	
	private Map<String, ActionSupport> actions=new HashMap<String, ActionSupport>();
	/**
	 * Initialize all sub controllers to the current central controller
	 */
	@Override
		public void init() throws ServletException {
			// TODO Auto-generated method stub
//			actions.put("/book",new BookAction());
			
		try {
			configModel=configModelFactory.build();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
	
	}
	
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//		Associate the sub controller with the browser request and "find" the sub controller that can process the request
//		http://localhost:8080/book.action?methodName=add-->BookAction.add();
		/*
		 * thinking
		 * 1.uri-->/book
		 * 2.Find BookAction in actions through the / book string
		 * 3.Call add of BookAction. If you want to call add, you can actually call execute uniformly
		 */
//		Get the requested address of the browser	
		String uri = request.getRequestURI();
//		uri-->/books
		uri = uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//		Find BookAction in actions through the / book string
//		ActionSupport actrion = actions.get(url);
//		Find sub controller in Map -- > find sub controller in configuration file
		/*
		 * 1.Find the corresponding ActionModel object through / book
		 * 2.Get the full pathname of the class through the ActionModel object. Com DHM. foramework. BookAction
		 * 3.Reflect instanced objects
		 */
		ActionModel actionModel=configModel.pop(uri);
//		The full pathname of the class
		String type = actionModel.getType();
		ActionSupport action=null;
		
		try {
			action=(ActionSupport) Class.forName(type).newInstance();
            action.execute(request,response);
//			Complete entity class parameter encapsulation

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}

}

config.xml

<?xml version="1.0" encoding="UTF-8"?>
<config>

<!-- Each additional configuration here is equivalent to actions.put("/goods", new GoodsAction());
 This solves the problem of code flexibility 
-->
<action type="com.DHM.servlet.BookAction" path="/book">
     <forward path="/bookList.jsp" redirect="false" name="list"/>
     <forward path="/bookEdit.jsp" redirect="true" name="toEdit"/>
</action>

<action type="com.DHM.servlet.GoodsAction" path="/goods">
     <forward path="/login.jsp" redirect="false" name="failed"/>
     <forward path="/main.jsp" redirect="true" name="success"/>
</action>

<action type="com.DHM.servlet.OrderAction" path="/order">
     <forward path="/login.jsp" redirect="false" name="failed"/>
     <forward path="/main.jsp" redirect="true" name="success"/>
</action>

</config>

Sub controller (Action interface)

package com.DHM.framework;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Sub controller
 * Processing browser requests
 * Perform upward extraction and abstraction for add/ref/other
 * 
 * Servlet implementation class Action
 */
public interface Action {
//This method is the abstract method of add/ref/other for upward extraction
	public String execute(HttpServletRequest req,HttpServletResponse resp);
	

//	private void list(HttpServletRequest request, HttpServletResponse response) {
//		System.out.println("listbookdao.......");
//		
//	}
//
//	private void del(HttpServletRequest request, HttpServletResponse response) {
//		System.out.println("delbookdao.......");
//		
//	}
//
//	private void edit(HttpServletRequest request, HttpServletResponse response) {
//		System.out.println("editbookdao.......");
//		
//	}
}

ActionSupport (implement Action)

package com.DHM.framework;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * Function: be able to process "all" requests of the browser. Including add/ref/other
 * @author T440s
 *
 */
public class ActionSupport implements Action{

	
	private static final Class<?> HttpServletResponse = null;

	@Override
	public String execute(HttpServletRequest req, HttpServletResponse resp) {
          String method=req.getParameter("methodName");
          String res = null;
       try {
	        Method m = this.getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
	        m.setAccessible(true);
	        res = (String) m.invoke(this, req, resp);
       } catch (Exception e) {
	          e.printStackTrace();
       }
            return res;
		
	}
}

BookAction (inheriting ActionSupport)

package com.DHM.framework;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.DHM.entity.Book;

public class BookAction extends ActionSupport implements ModelDriver{

//	If you inherit the execute method from the parent class, you inherit the method that reflects the dynamic call
//	Where is the current automatic controller called? Associate the sub controller with the browser request
	public Book book=new Book();
	private void list(HttpServletRequest request, HttpServletResponse response) {

		System.out.println("listbookdao.......");
		
	}

	private void del(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("delbookdao.......");
		
	}

	private void edit(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("editbookdao.......");
		
	}

	private String add(HttpServletRequest request, HttpServletResponse response) {
//	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("bookDao.add(book)...");
	return "list";
		
	}

	@Override
	public Object getModel() {
		// TODO Auto-generated method stub
		return null;
	}
	
}

Central controller and sub controller optimization
< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = add" > New</a>
< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = list" > query</a>
< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = delete" > delete</a>
<hr>

3. Establish model driven interface (entity class parameters accept code redundancy)

Book entity class

package com.DHM.entity;

public class Book {
	 public String bid;
	  public String bname;
	  public String price;
	  public String athor;
	  public String publish;
	public String getBid() {
		return bid;
	}
	public void setBid(String bid) {
		this.bid = bid;
	}
	public String getBname() {
		return bname;
	}
	public void setBname(String bname) {
		this.bname = bname;
	}
	public String getPrice() {
		return price;
	}
	public void setPrice(String price) {
		this.price = price;
	}
	public String getAthor() {
		return athor;
	}
	public void setAthor(String athor) {
		this.athor = athor;
	}
	public String getPublish() {
		return publish;
	}
	public void setPublish(String publish) {
		this.publish = publish;
	}
	@Override
	public String toString() {
		return "Book [bid=" + bid + ", bname=" + bname + ", price=" + price + ", athor=" + athor + ", publish="
				+ publish + "]";
	}
}

DispathServlet central controller

package com.DHM.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;

/**
 * The central controller
 * Servlet implementation class DispatchServlet
 */
//In XXX Call in at the end of action
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet {
//	There must be all sub controllers in the current central controller
//	Defect: if there is an addition, deletion, modification and query of goods -- > it means to change the code -- > the design of the code is not flexible enough
//	Thinking: without changing the code, the central controller can also find the corresponding sub controller to process the browser request
//	Scheme: I put the logic / action of adding sub controllers into the configuration file to complete (Dbutil changed the connection information into the code / now it is completed in the Properties file)
//	The advantage of putting it in the configuration file is that the code is more flexible and there is no need to modify the relevant information
//	private Map<String, ActionSupport> actions = new HashMap<>();
//	The configModel object reads all the configuration information through the modeling knowledge
	
	private configModel configModel=null;
	
	private Map<String, ActionSupport> actions=new HashMap<String, ActionSupport>();
	/**
	 * Initialize all sub controllers to the current central controller
	 */
	@Override
		public void init() throws ServletException {
			// TODO Auto-generated method stub
//			actions.put("/book",new BookAction());
			
		try {
			configModel=configModelFactory.build();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
	
	}
	
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//		Associate the sub controller with the browser request and "find" the sub controller that can process the request
//		http://localhost:8080/book.action?methodName=add-->BookAction.add();
		/*
		 * thinking
		 * 1.uri-->/book
		 * 2.Find BookAction in actions through the / book string
		 * 3.Call add of BookAction. If you want to call add, you can actually call execute uniformly
		 */
//		Get the requested address of the browser	
		String uri = request.getRequestURI();
//		uri-->/books
		uri = uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//		Find BookAction in actions through the / book string
//		ActionSupport actrion = actions.get(url);
//		Find sub controller in Map -- > find sub controller in configuration file
		/*
		 * 1.Find the corresponding ActionModel object through / book
		 * 2.Get the full pathname of the class through the ActionModel object. Com DHM. foramework. BookAction
		 * 3.Reflect instanced objects
		 */
		ActionModel actionModel=configModel.pop(uri);
//		The full pathname of the class
		String type = actionModel.getType();
		ActionSupport action=null;
		
		try {
			action=(ActionSupport) Class.forName(type).newInstance();
//			Complete entity class parameter encapsulation
			if(action instanceof ModelDriver) {
//				The current sub controller implements the model driven interface
				ModelDriver m=(ModelDriver) action;
				Object bean=m.getModel();
//				All request parameters are here. You need to encapsulate all request parameters into Book/Goods/
				BeanUtils.populate(bean, request.getParameterMap());
//				Execute business logic bookaction The return value of the add method is "list"
				/*
				 * 1.If a book is added, jump to the book display page booklist JSP (forward)
				 * 2.Book editing jumps to the editing interface bookedit JSP (redirect)
				 */
				String res=action.execute(request, response);
				ForWardModel forwarModel=actionModel.pop(res);
				String path = forwarModel.getPath();
				boolean redirect = forwarModel.isRedirect();
				if(redirect) {
					response.sendRedirect(request.getContextPath()+path);
				}else {
					request.getRequestDispatcher(path).forward(request, response);
				}
				
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}

}

Modeldriver (Model Driven Interface)

package com.DHM.framework;

/**
 * Model driven interface
 * Function: help the central controller to complete the parameter packaging project
 *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"));
 *System.out.println(book);
 * @author Administrator
 *
 * @param <T>
 */
public interface ModelDriver<T> {
	/**
	 * GoodsAction-->goods
	 * BookAction-->book
	 * ...
	 * @return
	 */
	T getModel();
}

BookAction(extends ActionSupport implements ModelDriver)

package com.DHM.framework;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.DHM.entity.Book;

public class BookAction extends ActionSupport implements ModelDriver{

//	If you inherit the execute method from the parent class, you inherit the method that reflects the dynamic call
//	Where is the current automatic controller called? Associate the sub controller with the browser request
	public Book book=new Book();
	private void list(HttpServletRequest request, HttpServletResponse response) {

		System.out.println("listbookdao.......");
		
	}

	private void del(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("delbookdao.......");
		
	}

	private void edit(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("editbookdao.......");
		
	}

	private String add(HttpServletRequest request, HttpServletResponse response) {
//	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("bookDao.add(book)...");
	return "list";
		
	}

	@Override
	public Object getModel() {
		// TODO Auto-generated method stub
		return null;
	}
	
}

Interface

Reproducing parameters to deal with code redundancy
<form action="${pageContext.request.contextPath }/book.action?methodName=add" method="post">
Book id: < input type = "text" name = "bid" value = "2" > < br >
Book Name: < input type = "text" name = "bName" value = "2x" > < br >
Book price: < input type = "text" name = "price" value = "2A" > < br >
Author: < input type = "text" name = "athor" value = "2B" > < br >
Book publishing house: < input type = "text" name = "publish" value = "2C" > < br >
    <input type="submit">
</form>

Operation results

4. Jump (forwarding and redirection) of the result page

The central controller DispathServlet package, BookAction and xml are the same as above

Sub controller (function: it can handle "all" requests of the browser, including add/ref/other
The return value determines which page to jump to (the redirection / forwarding is determined by the central controller..)

package com.DHM.framework;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Sub controller
 * Processing browser requests
 * Perform upward extraction and abstraction for add/ref/other
 * 
 * Servlet implementation class Action
 */
public interface Action {
//This method is the abstract method of add/ref/other for upward extraction
	public String execute(HttpServletRequest req,HttpServletResponse resp);
	
}

ActionSupport (able to handle all browser requests, including add/ref/other)

package com.DHM.framework;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * Function: be able to process "all" requests of the browser. Including add/ref/other
 * @author T440s
 *
 */
public class ActionSupport implements Action{

	
	private static final Class<?> HttpServletResponse = null;

	@Override
	public String execute(HttpServletRequest req, HttpServletResponse resp) {
          String method=req.getParameter("methodName");
          String res = null;
       try {
	        Method m = this.getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
	        m.setAccessible(true);
	        res = (String) m.invoke(this, req, resp);
       } catch (Exception e) {
	          e.printStackTrace();
       }
            return res;
		
	}
}

Interface code

Solve the redundancy problem of jump code on result code page
< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = add" > New</a>
< a href = "${pagecontext. Request. Contextpath} / book. Action? Methodname = toedit" > go to the editing interface</a>

 

Topics: mvc