カスタムMVCアーキテクチャ【中】

ディレクトリ

I.はじめに

2. MVC アーキテクチャの改善

1. 問題分析

2. サブコントローラーの初期化を解決する

2. ジャンプページのコード冗長問題を解決

3. エンティティのカプセル化のためのリクエストパラメータの取得の問題を解決する

4. まとめ


I.はじめに

カスタム MVC アーキテクチャ [前編]では、MVC アーキテクチャと 3 層アーキテクチャの違いと、MVC バージョンの反復の進化プロセスを学びましたが、前の記事ではまだいくつかの問題が残っています。この記事は、前回の記事から導きます この記事をもとに引き続き最適化を進めていきましょう!

2. MVC アーキテクチャの改善

1. 問題分析

       まず第一に、最初の問題は、サブコントローラーの初期設定です。前の記事「リフレクション拡張バージョン」の最後のバージョンで、プロジェクト内の他のテーブルを操作する必要がある場合は、サブコントローラーを作成する必要があることを示しました。 -controller を使用して設定します。

 しかし、最終的には、作成したカスタム MVC アーキテクチャを jar パッケージにパッケージ化したいので、変更することはできません。事前にサブコントローラーを予測して作成する機能も実行できません。わかりません。属性フィールドなので、中央コントローラー (DispatchServlet) の初期化を「自動化」したいと考えています。

       次に、2 番目の問題は、サブコントローラーでどのような操作を行っても、 page をエコーする必要があり、 response.sendRedirect();またはforward request.getRequestDispatcher().forward();操作をリダイレクトする必要があることです。 (ajax 方式) これらのコードは冗長です

最後に、一部の操作ではエンティティのカプセル化のためのリクエスト パラメータを取得する       必要があります。新規操作や変更操作を実行する場合は、次の図に示すように、リクエスト パラメータを取得してエンティティのカプセル化を行う必要があります。

 テーブルに 12 を超えるフィールドがある場合、リクエスト パラメーターを取得したい場合は、間違いなく 12 行を超えるコードが必要になります。次に、コンストラクターでエラーを埋める際に問題が発生する可能性があります。コンストラクターを使用せずに set メソッドを使用して値を順番に割り当てると、エラーを埋める問題は回避されますが、コード サイズも大幅に増加します。

チップ:

ジャーパッケージとは何ですか

JAR (Java ARchive) は、Java クラス ファイル、リソース ファイル、およびその他の関連ファイルを保存および配布するための圧縮ファイル形式です。.jar 拡張子を持つ JAR ファイルは、Java アプリケーションおよびライブラリをパッケージ化および配布するために Java プラットフォームで一般的に使用されるファイル形式です。

ラックパッケージを変更できない理由

JAR ファイル内のクラス ファイルはコンパイルされたバイナリ コードであるため、ソース コードを直接変更することはできません。ソース コードを変更する場合は、元の Java ソース コード ファイルにアクセスして変更し、再コンパイルして新しいクラス ファイルを生成する必要があります。その後、新しくコンパイルされたクラス ファイルを使用して、新しい JAR ファイルを作成または更新できます。

2. サブコントローラーの初期化を解決する

実際、解決策は非常に簡単です。XML 制約 (XML スキーマまたは DTD) を使用して、MVC 構成ファイルの構造と内容を定義および検証することです。XMLファイルはMVCにおけるモデル(Model)、ビュー(View)、コントローラー(Controller)の関係や動作を記述することができます。設定ファイルの情報を取得した後、リフレクションのインスタンス化と動的処理を実行します。

XML の制約とモデリングが理解できない場合は、著者が書いた XML 三部作を読むことができます。

DTD 制約の基本概要

Dom4j フレームワークの XML 解析

XML モデリングについてはこの記事を読むだけで十分です

さて、トピックに戻りますが、最初に XML ファイルとモデルを作成し、初期化データをモデルに解析します。

①XMLファイルの書き込み

<?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>

注: この XML ファイルはルート ディレクトリに保存されます。まずソース フォルダー フォルダーを作成し、その中に config.xml ファイルを置きます。

 

必要なサブコントローラーを設定するだけで、将来カスタム MVC を使用したい場合も同様です。

親切なヒント:

アクション タグ内のパス: は、DispatchServlet クラスによってインターセプトされた URL パスです。

action タグの type: は、子コントローラのフルパス名です

forward タグ内の name: は、サブコントローラが転送するかリダイレクトするかを決定する処理結果の戻り値です(問題 2 の解決策)。

forward タグ内の path: は、ジャンプ先のページです。

forward タグ内のリダイレクト:ジャンプするかどうかを示します。true はジャンプし、false はジャンプしません。

②XMLファイルのモデリングとXML初期化データの解析によるモデリング

ConfigModel: config タグのモデリング

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: アクションタグのモデリング

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: forward タグのモデリング

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 + "]";
	}

}

XML を解析し、データをモデルに初期化する

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());

	}
}

テストの結果を出力します。

最後の中央コントローラー (DispatchServlet) クラスの最適化コードは後で配置されます。

2. ジャンプページのコード冗長問題を解決

同様に、最適化のために config.xml 構成ファイルのソリューションを引き続き使用し、通常文字列に書き込むページによってエコーされるコード (説明した forward タグの name 属性) を変更するだけで済みます。その上。

 

③ サブコントローラーの操作メソッドの戻り値の型を「String」に変更し、対応する文字列をフォワードまたはリダイレクトで返します。

BookAction サブコントローラー

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 サブコントローラー

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. エンティティのカプセル化のためのリクエストパラメータの取得の問題を解決する

エンティティのカプセル化のリクエスト パラメータを最適化するには、次の 4 つの手順を完了する必要があります。
1. テーブルに対応するクラス属性オブジェクトが必要です。
2. すべての属性とパラメータを取得します
。 3. テーブルに対応するオブジェクトにパラメータ値をカプセル化します。
4.全サブコントローラーに共通化するには 

では、それはどのように行われるのでしょうか? シンプルに、子コントロールを汎用にするモデル駆動型インターフェイスを定義します。

package com.xw.framework;

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

サブコントローラーにインターフェイスを実装させ、インターフェイスをオーバーライドする必要があるメソッドを実装し、操作対象のテーブル (エンティティ オブジェクト) を渡すだけで済みます。

BookAction サブコントローラーは 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;
	}
	
}

GoodsAction サブコントローラーは 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;
	}
	
}

なぜ最後にセントラル コントローラー (DispatchServlet) クラスの最適化コードを説明する必要があるのでしょうか? カスタム MVC の概念をまだ覚えている方は、すでに考えているでしょう。セントラル コントローラー (DispatchServlet) クラスは、リクエストの操作を分散するクラスです。 、、上に書いたクラス(サブコントローラ)は全て中央コントローラ(DispatchServlet)クラスの操作要求を受け取って何かを行うものであり、上記3つの問題は中央コントローラ(DispatchServlet)クラスで判断し処理する必要がある。リクエストをサブコントローラーデバイスに分散します。最適化されたコードを見てみましょう。

中央コントローラー (DispatchServlet) が最適化された後

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

}

知らせ:

① リダイレクトによりここでプロジェクトのパスが失われるため、request.getContextPath() を接続する必要があります。

②初期データ送信用のxmlファイルはルートディレクトリにあるので「/」を追加します。

ここまでで 3 つの問題が解決したので、動作テストを見てみましょう。


 

 私たちのページはジャンプ ページにリダイレクトまたは転送でき、エンティティを動的にカプセル化できます。

4. まとめ

XML ファイルの構成と制約を使用してリフレクション操作を実行すると、ファイルが自動的に構成され、ページ ジャンプを記述する際のコードの冗長性の問題が解決されます。「モデル駆動クラス」を定義します。将来オペレーションを書きたい人は、ジェネリック型でエンティティ クラスを入力し、エンティティ クラスを取得する必要があります。エンティティ クラスを通じてインスタンスを反映し、属性と属性を動的に取得できます。代入操作と、独自のカプセル化されたエンティティの欠点を排除します。

この記事はここまでです。次回の更新をお楽しみに!

おすすめ

転載: blog.csdn.net/weixin_74318097/article/details/131489086