Custom MVC Architecture 【Medium】

directory

I. Introduction

2. Improve the MVC architecture

1. Problem analysis

2. Solve sub-controller initialization

2. Solve the code redundancy problem of the jump page

3. Solve the problem of obtaining request parameters for entity encapsulation

4. Summary


I. Introduction

In the custom MVC architecture [Part 1], we learned what is the difference between the MVC architecture and the three-tier architecture and the evolution process of the MVC version iteration, but in the previous article, there are still some problems left. In this article, I will lead you from the previous Continue to optimize on the basis of this article! !

2. Improve the MVC architecture

1. Problem analysis

       First of all, our first problem is the initial configuration of the sub-controller . I demonstrated in the last version of the previous article "Reflection Enhanced Version" that if you need to operate on other tables in the project, you must write a sub-controller and to configure.

 But in the end, we want to package the custom MVC architecture we wrote into a jar package, so we can't modify it. We can't do the ability to predict and write sub-controllers in advance. I don't know what kind of tables and tables will be in the future. Attribute field, so we want to "automate" the initialization of the central controller (DispatchServlet).

       Secondly, the second problem is that no matter what operation we have in the sub-controller, we need to echo the page , and we need to redirect response.sendRedirect(); or forward request.getRequestDispatcher().forward(); operation (ajax way) these codes are redundant .

       Finally, some operations need to obtain request parameters for entity encapsulation . When performing new operations and modification operations, it is necessary to obtain request parameters and perform entity encapsulation, as shown in the figure below.

 If we have more than a dozen fields in a table, if we want to get the request parameters, it will undoubtedly be more than a dozen lines of code, and secondly, there may be problems with filling in errors in the constructor. If you don't use the constructor to use the set method to assign values ​​​​in sequence, although the problem of filling in errors is avoided, it also greatly increases our code size.

Tips:

What is a jar package

JAR (Java ARchive) is a compressed file format for storing and distributing Java class files, resource files, and other related files. A JAR file with a .jar extension is a commonly used file format on the Java platform for packaging and distributing Java applications and libraries.

Why rack packages cannot be modified

Since the class files in a JAR file are compiled binary code, the source code cannot be modified directly. In the case where you want to modify the source code, you need to access the original Java source code file, modify it, and recompile to generate a new class file. You can then use the newly compiled class files to create or update a new JAR file.

2. Solve sub-controller initialization

In fact, the solution is very simple, which is to use XML constraints (XML Schema or DTD) to define and verify the structure and content of the MVC configuration file. The XML file can describe the relationship and behavior among the model (Model), view (View) and controller (Controller) in MVC. After obtaining the information in the configuration file, perform reflection instantiation and dynamic processing.

If you don't understand XML constraints and modeling, you can read the XML trilogy written by the author:

" Basic Overview of DTD Constraints "

" Dom4j Framework Parsing XML "

" XML modeling is enough to read this article "

Well, back to the topic, we first write an xml file and model and parse the initialization data into the Model.

① Write XML file

<?xml version="1.0" encoding="UTF-8"?>
	
<config>
	<action path="/books" type="com.xw.servlet.BookAction">
		<forward name="forward" path="/forward.jsp" redirect="false" />
		<forward name="redirect" path="/redirect.jsp" redirect="true" />
	</action>

	<action path="/goods" type="com.xw.servlet.GoodsAction">
		<forward name="forward" path="/forward.jsp" redirect="false" />
		<forward name="redirect" path="/redirect.jsp" redirect="true" />
	</action>
</config>

Note: This xml file is stored in the root directory, first create a Source Folder folder and put our config.xml file in it.

 

Just configure the required sub-controllers, and it is the same for anyone who wants to use our custom MVC in the future.

Kind tips:

The path in the action tag : is the url path intercepted by the DispatchServlet class.

The type: in the action tag is the full path name of the child controller .

The name: in the forward tag is the return value of the processing result of the sub-controller to determine whether to forward or redirect (the solution to problem 2).

The path: in the forward tag is the page to jump to.

The redirect in the forward tag: indicates whether to jump, true jumps, false does not jump.

②Modeling of xml files and parsing xml initialization data to modeling

ConfigModel: Modeling of config tags

package com.xw.model;

import java.util.HashMap;
import java.util.Map;

/**config标签实体对象
 * @author 索隆
 *
 */
public class ConfigModel {

	private Map<String, ActionModel> ConfigMap=new HashMap<String, ActionModel>();
	
	public ConfigModel() {
		// TODO Auto-generated constructor stub
	}
	
	//将ActionModel放入ConfigModel属性中
	public void push(ActionModel a) {
		ConfigMap.put(a.getPath(), a);
	}
	
	//根据ActionModel的path属性查找指定ConfigMap
	public ActionModel pop(String path) {
		return ConfigMap.get(path);
	}

	@Override
	public String toString() {
		return "ConfigModel [ConfigMap=" + ConfigMap + "]";
	}
	
}

 ActionModel: Modeling of action tags

package com.xw.model;

import java.util.HashMap;
import java.util.Map;

/**action标签实体对象
 * @author 索隆
 *
 */
public class ActionModel {
	private String path;
	private String type;
	private Map<String, ForwardModel> ActionMap = new HashMap<>();

	public ActionModel() {
		// TODO Auto-generated constructor stub
	}

	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;
	}

	// 将ForwardModel传入ActionModle属性
	public void push(ForwardModel f) {
		ActionMap.put(f.getName(), f);
	}
	

	public Map<String, ForwardModel> getActionMap() {
		return ActionMap;
	}

	public void setActionMap(Map<String, ForwardModel> actionMap) {
		ActionMap = actionMap;
	}

	// 根据ForwardModel的name属性找到指定的ActionMap
	public ForwardModel pop(String name) {
		return ActionMap.get(name);
	}
	
	//根据ActionModel中的path属性查询全部的ForardModel
	public ForwardModel Vive(String path) {
		
		return null;
		
	}

	@Override
	public String toString() {
		return "ActionModel [path=" + path + ", type=" + type + ", ActionMap=" + ActionMap + "]";
	}
	

}

ForwardModel: modeling of forward tags

package com.xw.model;

/**
 * Forward标签对象实体
 * 
 * @author 索隆
 *
 */
public class ForwardModel {
	private String name;
	private String path;
	private boolean redirect;

	public ForwardModel() {
		// 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 isRedirect() {
		return redirect;
	}



	public void setRedirect(boolean redirect) {
		this.redirect = redirect;
	}



	@Override
	public String toString() {
		return "ForwardModel [name=" + name + ", path=" + path + ", redirect=" + redirect + "]";
	}

}

Parse xml and initialize data to model

package com.xw.model;

import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.eclipse.jdt.core.BuildJarIndex;

/**
 * Config.xml的“工厂”,用于初始化数据
 * 
 * @author 索隆
 *
 */
public class ConfigFactory {

	/**
	 * 初始化数据
	 * 
	 * @param xmlPath
	 *            需要解析的xml
	 * @return ConfigModel 实体
	 * @throws Exception
	 */
	public static ConfigModel build(String xmlPath) throws Exception {
		// 定义ConfigModel对象
		ConfigModel cm = new ConfigModel();

		// 获取配置文件并转换成流对象
		InputStream is = ConfigFactory.class.getResourceAsStream(xmlPath);
		// 利用dom4j解析流
		SAXReader sa = new SAXReader();
		// 读取流对象
		Document read = sa.read(is);

		// 获取config标签下的所有action标签
		List<Element> configNodes = read.selectNodes("/config/action");
		// 遍历所有action标签
		for (Element configNode : configNodes) {

			// 实例化ActionModel对象
			ActionModel am = new ActionModel();
			// 将解析后的内容添加到ActionModel实体
			am.setPath(configNode.attributeValue("path"));
			am.setType(configNode.attributeValue("type"));

			// 获取action标签下的所有forward标签
			List<Element> forwardNodes = configNode.selectNodes("forward");
			for (Element element : forwardNodes) {
				// 实例化ForwardModle对象
				ForwardModel fm = new ForwardModel();
				// 将解析后的内容添加到ForwardModle实体
				fm.setName(element.attributeValue("name"));
				fm.setPath(element.attributeValue("path"));
				fm.setRedirect(!"false".equals(element.attributeValue("redirect")));
				am.push(fm);
			}
			cm.push(am);
			
		}
		
		return cm;
	}

	public static void main(String[] args) throws Exception {
		//测试是否成功初始化
		ConfigModel build = ConfigFactory.build("/config.xml");
		//模拟DispatchServlet截取到的url看是否拿到指定全路径名
		ActionModel pop = build.pop("/books");
		 System.out.println("/books的子控制器全路径是"+pop.getType());

	}
}

Print the results of the test:

The final central controller (DispatchServlet) class optimization code is placed later.

2. Solve the code redundancy problem of the jump page

In the same way, we continue to use the solution of the config.xml configuration file for optimization. We only need to change the code echoed by the page that we usually write into a string, which is the name attribute in the forward tag explained above.

 

③ Change the return type of the operation method of the sub-controller to "String" and return the corresponding string forward or redirect

BookAction sub-controller

package com.xw.servlet;

import java.util.Map;

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

import com.xw.entity.Book;
import com.xw.framework.ActionSupport;
import com.xw.framework.ModelDeivern;


public class BookAction extends ActionSupport  {

	
	private String list(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本五反射机制优化的查询——book");
		request.setAttribute("xw", "xw");
		return "forward";
	}

	private String del(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本五反射机制优化的删除——book");
		request.setAttribute("xw", "xw");
		return "redirect";
	}

	private String upd(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本五反射机制优化的修改——book");
		request.setAttribute("xw", "xw");
		return "redirect";
		
	}

	private String add(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本五反射机制优化的新增——book");
		request.setAttribute("xw", "xw");
		return "redirect";
		
	}

	
}

GoodsAction sub-controller

package com.xw.servlet;

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

import com.xw.entity.Goods;
import com.xw.framework.ActionSupport;
import com.xw.framework.ModelDeivern;


public class GoodsAction extends ActionSupport {
	
	private String list(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本五反射机制优化的查询——goods");
		request.setAttribute("xw", "xw");
		return "forward";
	}

	private String del(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本五反射机制优化的删除——goods");
		request.setAttribute("xw", "xw");
		return "redirect";
	}

	private String upd(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本五反射机制优化的修改——goods");
		request.setAttribute("xw", "xw");
		return "redirect";
		
	}

	private String add(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本五反射机制优化的新增——goods");
		request.setAttribute("xw", "xw");
		return "redirect";
		
	}

	
	
}

3. Solve the problem of obtaining request parameters for entity encapsulation

To optimize request parameters for entity encapsulation, the following four steps must be completed:
1. There must be a class attribute object corresponding to the table
2. To obtain all attributes and parameters
3. Encapsulate the parameter value into the object corresponding to the table
4. To Make it common to all sub-controllers 

So how is it done? Simple, we define a model-driven interface to make child controls generic

package com.xw.framework;

/**
 * 模型驱动接口,让子控制通用
 * 
 * @author Java方文山
 *
 */
public interface ModelDeivern<T> {
	T getModel();
}

Let our sub-controllers implement the interface and implement the methods that must override the interface. We only need to pass the table (entity object) to be operated.

BookAction sub-controller implements ModelDeivern

package com.xw.servlet;

import java.util.Map;

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

import com.xw.entity.Book;
import com.xw.framework.ActionSupport;
import com.xw.framework.ModelDeivern;


public class BookAction extends ActionSupport  implements ModelDeivern<Book>{
	private Book book=new Book();
	
	private String list(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本四反射机制优化的查询——book");
		request.setAttribute("xw", "xw");
		return "forward";
	}

	private String del(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本四反射机制优化的删除——book");
		request.setAttribute("xw", "xw");
		return "redirect";
	}

	private String upd(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本四反射机制优化的修改——book");
		request.setAttribute("xw", "xw");
		return "redirect";
		
	}

	private String add(HttpServletRequest request, HttpServletResponse response) {
		
		System.out.println("测试新增"+book);
		System.out.println("我是版本四反射机制优化的新增——book");
		request.setAttribute("xw", "xw");
		return "redirect";
		
	}

	/**
	 * 因为实现类必须重写这个方法以及传递泛型,到时候谁用就是谁
	 */
	 
	@Override
	public Book getModel() {
		return book;
	}
	
}

The GoodsAction sub-controller implements ModelDeivern

package com.xw.servlet;

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

import com.xw.entity.Goods;
import com.xw.framework.ActionSupport;
import com.xw.framework.ModelDeivern;


public class GoodsAction extends ActionSupport implements ModelDeivern<Goods>{
	private Goods goods=new Goods();
	private String list(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本四反射机制优化的查询——goods");
		request.setAttribute("xw", "xw");
		return "forward";
	}

	private String del(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本四反射机制优化的删除——goods");
		request.setAttribute("xw", "xw");
		return "redirect";
	}

	private String upd(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("我是版本四反射机制优化的修改——goods");
		request.setAttribute("xw", "xw");
		return "redirect";
		
	}

	private String add(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("测试新增"+goods);
		System.out.println("我是版本四反射机制优化的新增——goods");
		request.setAttribute("xw", "xw");
		return "redirect";
		
	}

	@Override
	public Goods getModel() {
		return goods;
	}
	
}

Why should the optimization code of the central controller (DispatchServlet) class be explained at the end? If you still remember the concept of custom MVC, you have already thought of it. Our central controller (DispatchServlet) class is the class that distributes request operations. , and the classes (sub-controllers) written above are all those who receive the operation requests of the central controller (DispatchServlet) class to do things. The above three problems must be judged and processed in the central controller (DispatchServlet) class to distribute requests to sub-controllers device. Let's look at the optimized code.

After the central controller (DispatchServlet) is optimized

package com.xw.framework;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.management.RuntimeErrorException;
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.w3c.dom.ranges.RangeException;

import com.xw.model.ActionModel;
import com.xw.model.ConfigFactory;
import com.xw.model.ConfigModel;
import com.xw.model.ForwardModel;
import com.xw.servlet.BookAction;
import com.xw.servlet.GoodsAction;

/**
 * 中央控制器拦截请求根据请求找到子控制器
 */
@WebServlet("*.do")
public class DispatchServlet extends HttpServlet {
	// 获取配置文件中的子控制器
	private ConfigModel ConfigModel;

	@Override
	public void init() throws ServletException {
		// ConfigFactory是Config.xml的“工厂”,用于解析Config.xml文件并完成ConfigModel初始化数据
		ConfigFactory ConfigFactory = new ConfigFactory();
		try {
			ConfigModel=ConfigFactory.build("/config.xml");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 获取到url请求
		String url = request.getRequestURI();
		// 截取指定需要操作的表
		url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
		// 根据path也就是截取路径(url)找到配置的type(子控制器)
		ActionModel ActionModel = ConfigModel.pop(url);
		// 防止一些人配置没写完善这里做一个非空判断如果请求路径未配置就抛出一个自定义异常给他
		if (ActionModel == null)
			throw new RuntimeException("Config is not configured yet, please configure it first.");

		// 拿到ActionModel里面的type值(type值就是子控制器的全路径名)
		String type = ActionModel.getType();
		try {
			//根据全路径名获取类类并反射实例化
			Action action = (Action) Class.forName(type).newInstance();
			//查看该类是否实现ModelDeivern接口
			if(action instanceof ModelDeivern) {
				ModelDeivern md=(ModelDeivern)action;
				//获取泛型传递的实体对象
				Object model = md.getModel();
				//获取请求参数的所有的属性及参数
				Map<String, String[]> parameterMap = request.getParameterMap();
				//使用工具类将参数值封装到表对应的对象中
				BeanUtils.populate(model, parameterMap);
			}
			
			
			//调用子控制器
			String execute = action.execute(request, response);
			
			
			//判读是重定向还是转发-根据返回值找到指定ForwardModel
			ForwardModel pop = ActionModel.pop(execute);
			//如果是ajax不需要配置xml,所以ForwardModel有值的时候才进行跳转判断
			if(pop!=null) {
				//拿到redirect进行相对应的判断
				boolean redirect = pop.isRedirect();
				//拿到path进行相对应的页面跳转
				String path = pop.getPath();
				if(redirect) {
					//true重定向
					//注意这里会丢失项目路径所以要request.getContextPath()
					response.sendRedirect(request.getContextPath()+path);
				}else {
					//false转发
					request.getRequestDispatcher(path).forward(request, response);
				}		
			}			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

Notice:

① Redirection will lose the project path here, so request.getContextPath() must be spliced.

② Since the xml file for initial data transmission is in the root directory, "/" should be added.

So far, our three problems have been solved. Let's take a look at the operation test.


 

 Our pages can be redirected or forwarded jump pages and can dynamically encapsulate entities.

4. Summary

Using XML file configuration and constraints to perform reflection operations can automatically configure files and solve the problem of code redundancy when writing page jumps. Define a "model-driven class". Whoever wants to write operations in the future must fill in the entity class in the generic type, and then get the entity class. We can reflect the instance through the entity class, dynamically obtain attributes and attribute assignment operations, and reduce Eliminates the drawbacks of our own encapsulated entities.

That's all for this article, look forward to my next update! !

Guess you like

Origin blog.csdn.net/weixin_74318097/article/details/131489086