自定义MVC框架

本文深入探讨自定义MVC框架的设计与实现,包括MVC模式的原理、基本架构搭建、核心控制器与子控制器的交互机制,以及如何通过XML配置和反射技术增强框架功能,实现CRUD操作的统一管理和动态调用。

** 自定义MVC框架**
1.什么是MVC?

  1. List item

MVC:Model(模型) - View(视图) - Controller(控制器)的缩写。
是一种软件设计典范(模式),用一种业务逻辑、数据、界面显示分离的方法组织代码。

Model1模式:JSP+JDBC 代码重用性低,维护差,可读性差。
Model2模式:MVC 核心思想:各司其职,提高代码的重用,提高维护性和可读性。
2.MVC结构
M -> 实体域模型(名词)、过程域模型(动词,如实体类要做CRUD)
V -> jsp / html 表现层/模板页.html .flt
C -> servlet / action

注意:由上而下,上层依赖下层,不能跨层。
开发PC端和移动端:M、C代码可以共用,V可以写成JSP\IOS\ANDROID……。

3.自定义MVC框架原理
在这里插入图片描述
4.自定义MVC框架基本架构
4.1.创建Web项目、导包、建包
(1)创建动态Web项目;
(2)导入MySQL数据库驱动包、JSTL标签包等;
(3)在src下创建util、entity、dao、biz、action、freamework等包。
在这里插入图片描述

4.2.创建前端JSP页面
在Webcontent中创建index.jsp,编写实现请求.action文件。代码如下:
……

< body>
 <a href="$ {pageContext.request.contextPath}/addAction.action">添加</a>
<a href="$ {pageContext.request.contextPath}/delAction.action">删除</a>
<a href="$ {pageContext.request.contextPath}/updateAction.action">修改</a>
<a href="$ {pageContext.request.contextPath}/selectAction.action">查询</a>
/body>
……

4.3.创建子控制器类:Action
作用:作为所有子控制器实现类(xxxAction)的基类。
在framework包中创建Action类,该类为抽象类,其中编写一个execute()抽象方法。如下:
public abstract class Action {
//执行方法

public abstract String execute() throws ServletException, IOException;
}

4.4.创建子控制器类的子类:XxxAction
作用:用于处理具体的业务逻辑,具体代码的实现。
在action包中创建XxxAction类,该类为Action抽象类的子类,实现Action类,并重写execute()方法。

AddAction.java代码如:

public class AddAction extends Action {
	@Override
	public String execute() throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("add被执行了");
		return null;
	}
}

DelAction.java代码如:
public class DelAction extends Action {
	@Override
	public String execute() throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("del被执行了");
		return null;
	}
}

中央控制器ActionServlet 与 子控制器类Action的区别:
中央控制器控制不同的请求,访问不同的子控制器类。(控制塔 与 飞机机长)
中央控制器控制所有的调度,子控制器处理具体的业务逻辑,但不是所有的类都是子控制器类,它需要有个execute()方法。(不是所有的人都能当机长)

4.5.创建核心控制器类:ActionServlet
作用:控制请求的调度,即根据不同的请求,访问不同的子控制器类。
例如:客户端请求不同地址http://localhost:8080/项目名/addAction.action,即访问对应的Action(AddAction.java)

思路:
(1)获得请求路径,并获得*.action,再获得*;
(2)将*做为Key,在Map集合根据Key查找相应的子控制器类XxxAction;
(3)将请求委托给子控制器处理;

代码如下:

//1.中央控制器类继承HttpServlet
public class ActionServlet extends HttpServlet {
//4.1声明Map集合,用于存储子控制器类
private Map<String, Action> actions = new HashMap<String, Action>();

@Override
public void init(ServletConfig config) throws ServletException {
//4.2 手动添加子控制器
actions.put("/addAction", new AddAction());
actions.put("/delAction", new DelAction());
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//3.获得请求地址(*.action)
String path = req.getServletPath(); 

//去掉.action,获得*
path = path.substring(0, path.indexOf(".action")); 

//4.将*做为键,在Map集合根据键查找相应的子控制器类xxxAction
Action action = actions.get(path);	
	
//5.将请求委托给子控制器处理
action.execute(req, resp);
}
}

4.6.配置web.xml
在web.xml中配置核心控制器ActionServlet,实现拦截*.action请求,执行ActionServlet中的代码。
或者使用注解的方式 @WebServlet (urlPatterns = “XXX.action”)

<servlet>
  	<servlet-name>ActionServlet</servlet-name>
  	<servlet-class>com.zking.mvc.framework.ActionServlet</servlet-class>
</servlet>
<servlet-mapping>
  	<servlet-name>ActionServlet</servlet-name>
  	<url-pattern>*.action</url-pattern>
</servlet-mapping>

在Servlet 3.0时可以使用注解:@WebServlet(name=””, urlpattrun=””)
4.7.测试
启动Tomcat,运行项止,实现请求XxxAction.action,通过ActionServlet转发访问相应的XxxAction类中的execute()方法。

上面实现了中央控制器根据不同的请求访问不同的子控制器类,但有个问题:子控制器需要在 ActionServlet中通过代码添加到Map<String, Action>中十分不方便,于是我们对自定义MVC框架进行增强:
5.增强1:将Action的信息配置到XML
通过XML配置Action的信息,并通过反射实例化Action子控制器实现类对象。
需要复制文件到项目(XML建模 + DOM4J解析 + 反射技术)

ActionServlet 核心控制器
config.xml Action配置
ForwardModel Forward模型
ActionModel Action模型
ConfigModel Config模型
ConfigModelFactory ConfigModel工厂类(用于创建配置模型对象)

5.1.XML配置Action信息

在src下创建config.xml文件,用于Action的配置:
<config>
<action path="/addAction" type="com.zking.mvc.action.AddAction">
<forward name="success" path="/login.jsp" redirect="true" />
……
</action>
……
</config>

5.2.XML建模
5.2.1.创建ForwardModel

public class ForwardModel implements Serializable {
private String name;
private String path;
private boolean redirect;
//省略set和get方法
}
5.2.2.创建ActionModel
public class ActionModel implements Serializable {
private String path;
private String type;
private Map<String, ForwardModel> forwardModels = new HashMap<String, ForwardModel>();
//省略set和get方法
public void put(ForwardModel forwardModel) {
this.forwardModels.put(forwardModel.getName(), forwardModel);
}
public ForwardModel get(String name) {
return this.forwardModels.get(name);
}
}
5.2.3.创建ConfigModel
public class ConfigModel implements Serializable {
private Map<String, ActionModel> actionModels = new HashMap<String, ActionModel>();
//省略set和get方法

//生产ActionModel对象
public void put(ActionModel actionModel) {
//获取ActionModel对象的path
String path = actionModel.getPath();
//验证请求路径格式是否正确
this.throwException(path);
//如果集合中包含该路径
if (actionModels.containsKey(path)) {
throw new RuntimeException("path[" + path + "]已存在");
}
//向集合中添加键值对,
actionModels.put(actionModel.getPath(), actionModel);
}

//销售ActionModel对象
public ActionModel get(String path) {
return actionModels.get(path);
}

//验证请求路径是否以/开头
private void throwException(String path) {
//设置匹配正则表达式
Pattern pattern = Pattern.compile("^/.+$");
//对请求的路径path进行匹配
Matcher matcher = pattern.matcher(path);
//是否匹配
boolean b = matcher.matches();
if (!b) {
//如果不匹配就抛出异常
throw new RuntimeException("path[" + path + "]必须以\"/\"开头");
}
}
}
5.2.4.创建ConfigModelFactory
public class ConfigModelFactory {
//定义config.xml文件
private static final String DEFAULT_PATH = "/config.xml";
	private static ConfigModel configModel;

//私有化构造方法
	private ConfigModelFactory() {
	}

//静态代理方法,返回ConfigModel对象
	public static ConfigModel createConfigModel() {
//实际调用了createConfigModel()获取ConfigModel对象
		return createConfigModel(DEFAULT_PATH);
	}

	@SuppressWarnings("unchecked")
	public static ConfigModel createConfigModel(String filePath) {
//如果configModel对象已存在,则返回该对象,不再创建新的ConfigModel对象

```

```

if (null != configModel) {
return configModel;
}
		try {
			configModel = new ConfigModel();
			// dom4j+xpath解析XML->Java Object
			InputStream is = ConfigModelFactory.class.getResourceAsStream(filePath);
			SAXReader saxReader = new SAXReader();
			Document document = saxReader.read(is);
			ActionModel actionModel = null;
			ForwardModel forwardModel = null;
			String path = null;
			String type = null;
			List<Element> forwardElementList = null;
			String name = null;
			String forwardPath = null;
			String redirect = null;
//从文档中查找config元素下的action元素
			List<Element> actionElementList = (List<Element>) document.selectNodes(" /config/action");
//遍历并实例化Action对象
			for (Element actionElement : actionElementList) {
//实例化ActionModel
				actionModel = new ActionModel();
//获取<action>中的path及type属性的值
				path = actionElement.attributeValue("path");
				type = actionElement.attributeValue("type");
//赋值
				actionModel.setPath(path);
				actionModel.setType(type);
//通过ConfigModel中的put()方法,将actionModel对象存入ConfigModel集合中
				configModel.put(actionModel);

	//查找action元素下的forward元素
forwardElementList = (List<Element>) actionElement.selectNodes("forward");
	//遍历并实例化Forward对象
for (Element forwardElement : forwardElementList) {
//实例化
					forwardModel = new ForwardModel();
//获得<forward>中的name、path、redirect属情的值
					name = forwardElement.attributeValue("name");
					forwardPath = forwardElement.attributeValue("path");
					redirect = forwardElement.attributeValue("redirect");
//赋值
					forwardModel.setName(name);
					forwardModel.setPath(forwardPath);
					forwardModel.setRedirect(redirect);
//通过ActionModel中的put()方法,将forwardModel对象存入ActionModel集合中
					actionModel.put(forwardModel);
				}
			}
			return configModel;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
//测试
	public static void main(String[] args) {
		ConfigModel configModel = ConfigModelFactory.createConfigModel();
		ActionModel actionModel = configModel.get("/addAction");
		ForwardModel forwardModel = actionModel.get("success");
		System.out.println(forwardModel.getName());
		System.out.println(forwardModel.getPath());
		System.out.println(forwardModel.isRedirect());
		System.out.println("ok");
	}
}

5.3.在init()中初始化加载、解析XML
作用:替代Map<String, Action>代码

在web.xml中央控制器类中添加初始化参数(可选项):

<servlet>
  	<servlet-name>ActionServlet</servlet-name>
  	<servlet-class>com.zking.mvc.framework.ActionServlet</servlet-class>
<init-param>
    <param-name>config</param-name>
    <param-value>/config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
  	<servlet-name>ActionServlet</servlet-name>
  	<url-pattern>/*</url-pattern>
</servlet-mapping>

在ActionServlet类中添加如下代码,用以声明配置模板,在init()方法中加载xml配置文件:

//中央控制器类继承HttpServlet
public class ActionServlet extends HttpServlet {
//声明configModel对象
private ConfigModel configModel; 

@Override
public void init() throws ServletException {
//读取web.xml中的<init-param>参数值(加载的xml配置文件)
	String config = this.getInitParameter("config"); 
	if (null != config && !"".equals(config)) {
		this.config = config;
	}
//调用ConfigModelFactory中的方法,传入xml配置文件路径
	this.configModel = ConfigModelFactory.createConfigModel(this.config);
}
……
}

5.4.编写查找Action的模型方法
在ActionSerlvet中创建findActionModel()方法,根据传入的XML文件路径,获得ActionModel对象。根据path获得相对应的ActionModel,如根据/loginAction获得模型->“com.zking.mvc.action.LoginAction”
/**

  • 根据path查找ActionModel对象(保存子控制器的配置信息)
    */

     private ActionModel findActionModel(String path) {
       //path是ActionServlet拦截的请求路径
      //根据path在众多<action>中找到相应的Action
      ActionModel actionModel = configModel.get(path);
      //如果actionModel为null表示config.xml中没有配置相应的<action>
      if(null == actionModel) {
      throw new RuntimeException("path所在的路径不对");
      }
      return actionModel;
      }
      5.5.编写反射实例化子控制器方法
      根据ActionModel的type,实例化对象。
      /**
      * 反射实例化子控制器
      */
      private Action createAction(String type) {
      try {
      Class c = Class.forName(type);
      Action action = (Action)c.newInstance();
      return action;
      }catch(Exception e) {
      throw new RuntimeException(e);
          }
      }
      5.6.修改doPost()
      修改ActionServlet类中doPost()方法中的代码:
      @Override
      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      //http://localhost:8080/T218_mvc/addAction.action
      //1.获取请求路径,如/addAction.action
      String path = req.getServletPath();
      //2.截取路径,去掉.action,结果如:/addAction
      path = path.substring(0, path.indexOf(".action"));
      //3. 反射实例化子控制器
      ActionModel actionModel = findActionModel(path);
      Action action = createAction(actionModel.getType());
      //4. 将请求委托给子控制器处理
      action.execute();
      }
    

    5.7.测试
    总结:以后子控制器都由config.xml文件配置,无需在ActionServlet中硬编码实现。

    问题:每个Action在执行完后都会做跳转动作,跳转无非就是重定向或转发,这里进行强化,Action只返回一个结果码,配置文件自动给你做跳转。
    6.增强2:通过Action结果码控制页面的跳转
    6.1.什么是结果码?

    ActionServlet的doPost()方法中最后将请求委托给子控制器Action后,Action的子类中的execute()执行完后会返回一个String字符串给ActionServlet,返回的这个String字符串,就称为“结果码”。

    如AddAction中的代码:

      public class AddAction extends Action {
      	@Override
      	public String execute() throws ServletException, IOException {
      		// TODO Auto-generated method stub
      		System.out.println("add被执行了");
      		return "success"; //success就是结果码
      	}
      }
    

    6.2.结果码有什么用?
    结果码可以作为跳转的URL,比如:子控制器执行execute()后成功返回"success.jsp"。

      public class AddAction extends Action {
      	@Override
      	public String execute() throws ServletException, IOException {
      		// TODO Auto-generated method stub
      		System.out.println("add被执行了");
      		return "success.jsp"; //success.jsp可以控制跳转的页面
      	}
      }
    

也可以作为一个编码,与XML配置中中的name进行匹配,跳转到path路径。

6.3.修改doPost()
修改ActionServlet类中doPost()方法中的代码:

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//http://localhost:8080/T218_mvc/addAction.action
//1.获取请求路径,如/addAction.action
String path = req.getServletPath();
//2.截取路径,去掉.action,结果如:/addAction
path = path.substring(0, path.indexOf(".action"));
//3. 反射实例化子控制器
ActionModel actionModel = findActionModel(path);
Action action = createAction(actionModel.getType());
//4. 将请求委托给子控制器处理,返回execute()执行的结果码
String code = action.execute();
}
6.4.编写处理结果码方法processCode
在ActionServlet中添加processCode()方法,用于处理XxxAction返回的结果码,并进行页面跳转。
/**
* 处理结果码,接收ActionModel和结果码code
*/
private void processCode(ActionModel actionModel, String code, HttpServletRequest req, HttpServletResponse resp)  throws ServletException, IOException {
//1.结果码为null,表示不做跳转,代码执行到此结束
if(null == code) {
return;
}
//2.获得结果码对应的forward对象
ForwardModel forwardModel = actionModel.get(code); 
if(null == forwardModel) {
//表示<action>下未配置相应的<forward>
throw new RuntimeException("Action[" + actionModel.getPath() + "]找不到forward[" + name + "]对象");
}
//3.获取config.xml中<forward>中的path,指定跳转的目标地址
String forwardPath = forwardModel.getPath();
if(forwardModel.getRedirect()) {  //重定向
resp.sendRedirect(req.getContextPath() + forwardPath);
}else{   //转发
req.getRequestDispatcher(forwardPath).forward(req, resp);
}
}
注意:跳转时要使用request和response对象,因此在ActionServlet的String code = action.execute()处,传递req与resp对象到processCode()方法中来。
还有ActionModel与结果码,如下:
// 4. 将请求委托给子控制器处理
String code = action.execute();
// 5. 处理结果码,进行页面跳转
this.processCode(actionModel, code, req, resp);
6.5.测试
注意:config.xml的<action>中要嵌入<forward>元素。

问题:一般我们针对某一张表进行CRUD操作,都会用一个Servlet来处理,但现在我们有AddAction、DelAction、UpdateAction、SelectAction四个来处理,这样会造成文件过多,现在我们进一步增强MVC框架,让对某张表的CRUD操作放到同一个Action中。
7.增强3:使用一个Action实现CRUD
作用:将一组相关的操作放到一个Action中,使用反射中的动态调用方法实现。
7.1.问题
例:学习Servlet时会为CRUD分别建立一个Servlet文件;或者创建一个Servlet文件,增删改查都进入该Servlet,根据判断参数值的不同,操作不同的方法。如:
http://localhost:8080/项目名/userAction.action?methodName=add -> 执行UserAction中的add()
http://localhost:8080/项目名/userAction.action?methodName=del -> 执行UserAction中的del()

以前我们可能会在接收methodName参数后进行判断,分页调用不同的方法。如:

//接收请求methodName的参数值
String methodName = request.getParameter("methodName");
//判断调用不同的方法
if(“add”.equals(methodName)){
    add();
}else if(“del”.equals(methodName)){
    del();
}……

这种思路存在缺陷,如果业务逻辑比较多,调用的方法不仅仅只有CRUD,就会有很多的if判断。
我们增强这处,希望输入http://localhost:8080/项目名/helloAction.action?methodName=add,通过反射操作add(),无需if判断。

7.2.创建DispatcherAction
在Action类与XxxAction类中间添加一层DispatcherAction类。
作用:派遣Action类调用相应的方法。

framework包中创建DispatcherAction类,继承Action类,并重写execute()方法并用final修饰。如:
public class DispatcherAction extends Action {
//注意:execute()方法用final修饰,不让子控制器类重写execute(),但可以继承。
@Override
public final String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//获得操作的方法
//如请求:http://localhost:8080/项目名/userAction.action?methodName=add
String methodName = req.getParameter("methodName");  //add
//获得当前类的类对象
Class c = this.getClass();
//通过反射获得请求参数methodName的方法:add()
Method method = c.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); //getMethod获取公开的,不获取私有的,因为最终调的是XxxAction
//调用方法,返回结果码
String code = (String)method.invoke(this, req, resp);
//返回结果码
return code;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

7.3.XxxAction类继承DispatcherAction
在action包中创建UserAction类,继承DispatcherAction类,添加CRUD方法。如:

public class UserAction extends DispatcherAction{
//增加的方法
public String add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
……
System.out.println("执行了add方法");
return "success";
}

//删除的方法
public String del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
……
System.out.println("执行了del方法");
return "success";
}

//修改的方法
public String update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
……
System.out.println("执行了update方法");
return "success";
}

//查询的方法
public String query(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
……
System.out.println("执行了query方法");
return "success";
}
}
7.4.config.xml中配置UserAction(用转发)
<action path="/userAction" type="com.zking.mvc.action.UserAction">
<forward name="success" path="/success.jsp" redirect="false" />
</action>

7.5.前端提交
前端的提交有几种形式:
(1)直接在地址传递方法名:http://localhost:8080/项目名/userAction.action?methodName=add
(2)通过js传递方法名:
<script type="text/javascript">
function doSubmit(method){
//表单数据提交
document.f1.methodName.value = method;
document.f1.submit();
}
</script>
<form name="f1" action="userAction.action">
用户名:<input type="text" name="username" /><br/>
密 码:<input type="password" name="password" /><br>
<input type="hidden" name="methodName" value="" />
<input type="button" value="提交" onclick="doSubmit('add')" />	
</form>

注意:Struts2框架前端并非是这种写法,而是:
<form name="f1" action="calAction.action">
	num1<input type="text" name="num1" ><br>
	num1<input type="text" name="num2" ><br>
	
	<input type="submit" name="method:add" value="+">
	<input type="submit" name="method:minus" value="-"> 
	<input type="submit" name="method:mul" value="*">
	<input type="submit" name="method:div" value="/">
</form>

7.6.处理请求方法
在DispatcherAction添加getMethodName(),用于处理接收的参数。
说白了,即获取“method:add”,再去除前面的“method:”,后面的就是方法名。
//如果请求的是:http://localhost:8080/项目名/userAction.action?methodName=add
private String getMethodName(HttpServletRequest req) {
String methodName = req.getParameter("methodName");
//为null表示请求没有带methodName参数
if (null == methodName) {
//获取所有请求参数
Map<String, String[]> parameterMap = req.getParameterMap();
//遍历所有参数的Key
for (String key : parameterMap.keySet()) {
//如果key中包含method:字符
if (-1 != key.indexOf("method:")) {
//使用空字符串替换method:字符(删除)
methodName = key.replace("method:", "");
break;
}
}
}
return methodName;
}

8.增强4:利用ModelDriver接口对Java对象进行赋值(反射读写方法)

看下列代码:

public String add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //getN次获得表单值
    Float num1 = Float.parseFloat(req.getParameter("num1"));
    Float num2 = Float.parseFloat(req.getParameter("num2"));

//setN次给对象赋值
//还有类型转换
……

}

对Java对象进行赋值,需要get表单数据,set赋值对象。像get和set方法代码多,但没营养,我们可以将这块进行增强。
例如:CalAction中的add等方法代码,需要获值,需要类型转换,request.getParameter()不可缺少,如果赋值或获值,还要setN次,getN次,一些都是一大堆代码。现在只需要一行代码就能实现。

1.创建CalBean类
用于封装表单值(与表单值相同)。属性名必须与表单控件name相同。

public class CalBean{
private Float num1;
private Float num2;
// 无参构造方法
// 封装set,get
}
2.导入反射工具类(反射时用过)
commons-beanutils-1.8.0.jar和commons-logging.jar
作用:动态取值(获取表单数据)。
3.修改子Action(如CalAction)方法
public class CalAction extends DispatcherAction{

public String minus(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// Float num1 = Float.parseFloat(req.getParameter("num1"));
		// Float num2 = Float.parseFloat(req.getParameter("num2"));

		// 利用反射机制对Java对象进行赋值,参数名与属性名对应
		CalBean calBean = new CalBean();
		try {
             // 获得表单参数,通过反射赋值给CalBean对象。
//原理:对Map集合做了个for循环,然后把参数名取出来做属性名,再对java对象calBean一个个进行反射赋值。
		     Map<String, String[]> parameterMap = req.getParameterMap();
             // 反射赋值
		     BeanUtils.populate(calBean, parameterMap);
		} catch (Exception e) {
		     e.printStackTrace();
		}

		Float num3 = calBean.getNum1() - calBean.getNum2();
		req.setAttribute("num3", num3);
		return "rs";
	}
}

上面代码还是有问题,因为其他CURD方法也要写上面这块代码,麻烦,处理方法:
4.创建接口:ModelDriver
作用:给Java对象赋值。
它单独使用是没有作用的,要与ActionServlet配合工作,用来给java对象赋值用。
public interface ModelDriver{ //使用泛型,如传过来是CalBean就是CalBean
public K getModel();
}
5.子Action操作
让子Actoin(如CalAction)实现ModelDriver接口,并私有化一个CalBean对象。

public class CalAction extends DispatcherAction implements ModelDriver<CalBean>{
			
//2. 实例化一个CalBean对象,让getModel返回(必须要实例化,用来接收)
private CalBean calBean = new CalBean();
			
//1. 重写getModel方法(必须要返回)
@Override
public CalBean getModel(){
    retrun calBean;
}

//3. 加减乘除方法修改
public String minus(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Float num3 = calBean.getNum1() - calBean.getNum2();
    req.setAttribute("num3", num3);
    return "rs";
}

public String add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Float num3 = calBean.getNum1() + calBean.getNum2();
    req.setAttribute("num3", num3);
    return "rs";
}

……

}

6.赋值完成,接下来完成ActionServlet的写法:

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp){
……
Action action = this.createAction(actionModel.getType());
//ActionServlet与ModelDriver配置工作,用来给Java对象赋值
//判断Action是否实现ModelDriver接口
if(action instanceof ModelDriver){	//CalAction 实现 ModelDriver
				//将Action转换成ModelDriver
				ModelDriver modelDriver = (ModelDriver)action;
				//获得CalBean对象
				Object obj = modelDriver.getModel();
				try{
					Map<String, String[]> parameterMap = req.getParameterMap();
					BeanUtils.populate(obj, parameterMap);
				}catch(Exception e){
					e.printStackTrace();
				}
			}

提取成processModelDriver()方法:

// 处理ModelDirver
		private void processModelDriver(HttpServletRequest req, Action action) {
		    // 判断Action是否实现 ModelDriver接口
		    if (action instanceof ModelDriver) {
			ModelDriver modelDriver = (ModelDriver) action;
			Object obj = modelDriver.getModel();  //该对象就是用来接收参数的对象
			if (null != obj) {
				try {
					// 通过反射机制赋值
					Map<String, String[]> parameterMap = req.getParameterMap();
					BeanUtils.populate(obj, parameterMap);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		    }
		}

再调用processModelDriver()方法:

@Override
		protected void doPost()
		……
		// 3. 
		// 6. 根据ModelDirver接口,对Java对象赋值(注意该代码的位置)
		this.processModelDriver(req, action);
		// 4.

以后要做Java对象赋值,创建一个类,实现ModelDriver接口,实例化一个Bean对象,重写getModel()方法返回这个Bean

注1:本项目中Action多例模式?因为Action的属性要用来接收参数
反射实例化子对象

9.实战案例

  1. 创建项目
  2. 导包:mvc.jar 核心包,还有其他需要的数据库驱动包、JSTL等等。
  3. 在项目中建包,将工具类等复制过去,并测试数据库是否连接上。
  4. 在src下创建config.xml和dtd,一般都会复制。可以将config.xml名改成config.xml,注意修改下加载代码
  5. 在web.xml中设置过滤器和中央控制器配置
EncodingFiter com.zking.test.util.EncodingFiter EncodingFiter /*
<!-- 中央控制器 -->
<servlet>
	<servlet-name>ActionServlet</servlet-name>
	<servlet-class>com.zking.mvc.framework.ActionServlet</servlet-class>
	<init-param>
		<param-name>config</param-name>
		<param-value>/config.xml</param-value>
	</init-param>
</servlet>
<servlet-mapping>
	<servlet-name>ActionServlet</servlet-name>
	<url-pattern>*.action</url-pattern>
</servlet-mapping>

6. 写实体类及dao(CURD)—将BaseDao放进来
7. 可以写个测试类测试下CURD,XXXtest

private BookDAO bookDAO = new BookDAO();

private Book book;

@Before
void setUp() throws Exception {
	book = new Book();
}

@Test
void testAdd() {
	book.setBookName("zz");
	book.setPrice(77f);
	bookDAO.add(book);
}

@Test
void testEdit() {
	book.setBookId(167);
	book.setBookName("zzzzz");
	book.setPrice(777f);
	bookDAO.edit(book);
}

@Test
void testDel() {
	book.setBookId(167);
	bookDAO.del(book);
}

@Test
void testLoad() {
	book.setBookId(165);
	Book b = bookDAO.load(book);
	System.out.println(b);
}

@Test
void testList() {
	PageBean pageBean = new PageBean();
	pageBean.setPage(1);
	pageBean.setRows(8);
	pageBean.setPagination(false);

	List<Book> bookList = bookDAO.list(book, null);
	for (Book b : bookList) {
		System.out.println(b);
	}
}

8. 编写子Action,如BookAction,继承DispatcherAction,实现ModelDriver
编写CURD操作,增删改用重定向,查询用转发。

public class BookAction extends DispatcherAction implements ModelDriver {

private BookDAO bookDAO = new BookDAO();

private Book book = new Book();

public BookAction() {
	super();
}

@Override
public Book getModel() {
	return book;
}

public String add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	bookDAO.add(book);
	req.getSession().setAttribute("message", "书本新增成功");
	return "success";// 重定向
}

public String edit(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	bookDAO.edit(book);
	req.getSession().setAttribute("message", "书本修改成功");
	return "success";// 重定向
}

public String del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	bookDAO.del(book);
	req.getSession().setAttribute("message", "书本删除成功");
	return "success";// 重定向
}

public String load(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	Book b = bookDAO.load(book);
	req.setAttribute("b", b);

	// 动态跳转,edit/detail
	String forward = req.getParameter("forward");
	if (null == forward) {
		forward = "edit";
	}
	return forward;
}

public String list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	//查询考虑分页
	PageBean pageBean = new PageBean();
	pageBean.setRequest(req);
	req.setAttribute("pageBean", pageBean);

	List<Book> bookList = bookDAO.list(book, pageBean);
	req.setAttribute("bookList", bookList);
	return "list";// 重定向
}

}

9. 在config.xml中配置Action

<config>
	    <action path="/bookAction" type="com.zking.test.action.BookAction">
		<forward name="list" path="/listBook.jsp"/>
		<forward name="edit" path="/editBook.jsp"/>
		<forward name="detail" path="/detailBook.jsp"/>
		<forward name="success" path="/bookAction.action?methodName=list" redirect="true"/>
	    </action>
	</config>
  1. 分页的代码复制过来,修改下路径

//copy都出错了,可不能怪小编我咯

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值