目录
一、中央控制器动态加载存储子控制器
在之前所分享的自定义MVC原理中,浏览器通过中央控制器找到对应的子控制器存在着一定的缺陷 例如:假设我们现在有许多实体类,那么我们就需要将他们一个一个的放入map集合中,会出现大量的相似代码,且不便捷
actions.put("/book", new BookAction());
actions.put("/order", new OrderAction());
优化:
我们会使用到之前所学到的Xml建模,actionModel是configModel的子控制器,所以我们定义一个ConfigModel
private ConfigModel configModel;
//程序启动时只会启动一次
@Override
public void init() throws ServletException {
//actions.put("/book", new BookAction());
//actions.put("/order", new OrderAction());
try {
configModel = ConfigModelFactory.bulid();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
以前获取子控制器路径的方法
String url = req.getRequestURI();
//拿到book
url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
ActionSupport action = actions.get(url);
action.excute(req, resp);
现在通过ConfigModel获取子控制器路径
String url = request.getRequestURI();
//要拿到/book,就是最后一个/到最后一个.的位置
url=url.substring(url.lastIndexOf("/"),url.lastIndexOf("."));
//相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
ActionModel actionModel = configModel.pop(url);
if(actionModel==null) {
throw new RuntimeException("action配置错误");
}
String type = actionModel.getType();//type是配置文件中Action子控制器的全路径名
try {
Action action = (Action) Class.forName(type).newInstance();
action.execute(request, response);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
优化思路:先拿到config.xml文件的全路径名,然后再将获取子控制器路径的url放入configModel中,这样我们就拿到了子控制器Actionmodel,接下来我们只要拿到Action的全路径名Type就可以了,我们之后再想添加实体类,就只需要在xml文件中进行配置即可
二、参数传递封装优化
当我们接收前台传过来的值时,我们需要一个一个接收并放入实体类中,如果字段较多的话还会造成大量代码以及可能会发生错误
String bid = request.getParameter("bid");
String bname = request.getParameter("bname");
String price = request.getParameter("price");
Book book = new Book();
book.setBid(Integer.parseInt(bid));
book.setBname(bname);
book.setPrice(Float.parseFloat(price));
优化:
1.定义ModelDriven模型驱动接口类
public interface ModelDriven<T> {
//拿到将要封装的类实例 ModelDriven.getModel==new Book();
T getModel();
}
2.在Bookaction中实现ModelDriven接口,在调用getModel方法
3.在中央控制器中实现ModelDriven接口,给Book参数赋值
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//http://localhost:8080/mvc/book.action?methodName=list
String url = request.getRequestURI();
//要拿到/book,就是最后一个/到最后一个.的位置
url=url.substring(url.lastIndexOf("/"),url.lastIndexOf("."));
//相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
ActionModel actionModel = configModel.pop(url);
if(actionModel==null) {
throw new RuntimeException("action配置错误");
}
String type = actionModel.getType();//type是配置文件中Action子控制器的全路径名
try {
Action action = (Action) Class.forName(type).newInstance();
//正式调用方法前,book的属性要被赋值
if(action instanceof ModelDriven) {
ModelDriven md =(ModelDriven) action;
//model指的是Bookaction中的类实例Book
Object model = md.getModel();
//要给Model中的属性赋值,要接受前端jsp传来的参数
//将前端所有参数值封装进实体类
BeanUtils.populate(model, request.getParameterMap());
}
action.execute(request, response);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
三、对执行结果转发重定向优化
页面跳转有两种方式:转发和重定向
重定向:resp.sendRedirect(path);
转发: req.getRequestDispatcher(path).forward(req, resp);
这些代码往往要写很多次,因此通过配置config文件来解决此类问题;例如这一次我跳转增加页面时是转发,跳转编辑界面是重定向,
思路:
1、当点击增加或者编辑时,首先跳转的是中央控制器类(DispatchServlet):获取到url,url将决定要跳到哪一个实体类,
2、之后进入ActionSupprot(子控制器接口实现类)通过methodname要调用什 么方法,
3、再然后进入到BookAction中调用methodname方法,找到对应的返回值,
4、通过返回值在进入到config文件找到path属性,之后在中央控制器中进行判 断,来决定是重定向还是转发。
1、中央控制器DispatchServlet
String path = forwardModel.getPath();
//拿到是否需要转发的配置
boolean redirect = forwardModel.isRedirect();
if(redirect) {//是重定向
//pageContext.request.contextpath
response.sendRedirect(request.getServletContext().getContextPath()+path);
}
request.getRequestDispatcher(path).forward(request, response);
2、ActionSupport子控制器接口实现类
package com.zjy.framework;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ActionSupport implements Action{
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) {
//为了区分当前请求的目的 ,增删改查的目的 就从前台将调用的方法传达到后台
String methodNmae = request.getParameter("methodName");
//methodName可以是add也可以是del或者其他
//前台传递什么方法 就调用当前类的对应方法
try {
Method m = this.getClass()//BookServlet.class
.getDeclaredMethod(methodNmae, HttpServletRequest.class,HttpServletResponse.class);
m.setAccessible(true);
//调用当前类实例的methodName方法
return (String) m.invoke(this, request,response);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
3、BookAction
private String add(HttpServletRequest request, HttpServletResponse response) {
System.out.println("在同一个Servlet中调用add方法");
return "failed";
}
private String list(HttpServletRequest request, HttpServletResponse response) {
System.out.println("在同一个Servlet中调用list方法");
return "success";
}
4、config文件
四、框架配置文件可变
为了使我们的框架更加灵活,我们首先在中央控制器DispatchServlet的初始化Init方法中配置文件地址
try {
//配置地址
//getInitParameter的作用是拿到web.xml中的servlet信息配置的参数
String configLoaction = this.getInitParameter("configLocation");
if(configLoaction == null || "".equals(configLoaction))
configModel = ConfigModelFactory.bulid();
else
configModel = ConfigModelFactory.bulid(configLoaction);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
然后再web.xml中进行配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>MVC</display-name>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>com.zjy.framework.DispatcherServlet</servlet-class>
<init-param>
<param-name>configLocation</param-name>
<param-value>/zjy.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>