1.MVC模式
1.1.什么是MVC
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
MVC结构
-
Model:是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。
-
View:是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。
-
Controller:是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式
-
Model:常用javabean去实现,通过各种类来对数据库的数据进行获取,并封装在对象当中。
-
View:常用JSP来实现,通过可直接观察的JSP页面来展示我们从数据库中获取的数据。
-
Controller:常用servlet来实现,通过servlet来获取经过javabean包装过的对象(已存入数据库中的数据),然后再发送数据传输到JSP界面。
1)不能跨层调用; 2)只能由上往下进行调用:View -> Controller -> Model
1.2.三层架构和MVC的区别
-
三层架构是基于业务逻辑来分的,而MVC是基于页面来分的;
-
三层是种软件架构,通过接口实现编程,MVC模式是一种复合设计模式,一种解决方案;
-
三层架构模式是体系结构模式,MVC是设计模式;
-
三层架构模式又可归于部署模式,MVC可归于表示模式。
2.自定义MVC工作原理
3.自定义MVC实现
基于IDEA创建mvc项目。
示例一:
-
创建ActionServlet核心控制器并继承HttpServlet,接受请求、分发请求。
-
创建Action抽象子控制器并定义execute方法,用于处理具体的核心业务逻辑处理。
-
创建HelloAction并继承抽象类Action,重写execute方法,实现业务逻辑处理。
-
完成在ActionServlet中的init方法中的请求路径与子控制器类的对应配置。
-
完成ActionServlet中的doPost方法的请求处理。
示例二:
-
创建AddAction并继承抽闲类Action,重写execute方法,实现加法逻辑处理。
-
在ActionServlet中的init方法中完成AddAction子控制器类与请求路径的对应关系配置。
-
创建add.jsp和rs.jsp页面,处理加法操作及结果展示。
4.自定义MVC增强
4.1.XML解析建模
4.1.1.导入XML解析建模工具类
具体代码请参考开头中《XML之解析建模》
4.1.2.导入mvc.xml
将mvc.xml导入到自定义MVC项目src根路径下。
4.1.3.修改ActionServlet中init初始化方法
在ActionServlet的init方法中将原有Map集合方式替换成XML建模方式
private Map<String,Action> actions=new HashMap<String,Action>();
private ConfigModel configModel;
@Override
public void init() throws ServletException {
//Map集合方式
actions.put("/helloAction", new HelloAction());
actions.put("/addAction",new AddAction());
//XML建模方式
configModel=ConfigModelFactory.createConfigModel();
}
4.1.4.修改ActionServlet逻辑处理流程
修改ActionServlet中的doPost方法的请求处理业务流程,由于获取请求路径及截取请求路径名的操作不变。
-
根据请求路径名获取ActionModel
/**
* 3、根据请求路径名获取对应的ActionModel(type和path)
* @param path 对应config.xml中action节点的path属性(请求路径名)
* @return 返回config.xml中action节点所对应的建模实体类ActionModel(path和type)
*/
private ActionModel findAction(String path){
ActionModel actionModel = configModel.get(path);
if(null==actionModel) throw new RuntimeException("ActionModel不存在!");
return actionModel;
}
注:这里经常会引发ActionModel不存在的问题,请认真仔细核查mvc.xml的配置
-
反射机制实例化子控制器类
/**
* 5、根据ActionModel中的type属性完成反射实例化操作
* @param type 来自于ActionModel建模中实体类中的type属性对应的config.xml中action中全路径名
* @return
*/
private Action createAction(String type){
try {
//获取类对象
Class cla = Class.forName(type);
//返回反射实例化实例化对象
return (Action) cla.newInstance();
} catch (Exception e) {
throw new RuntimeException("反射机制实例化Action子控制器类错误!");
}
}
-
将请求委托给子控制器类处理
//获取请求路径名
//http://localhost:8080/项目名/请求路径.action
String requestURI = req.getServletPath();
int start=requestURI.indexOf("/");
int end=requestURI.lastIndexOf(".");
//获取的是/和.action中的请求路径
String actionName = requestURI.substring(start, end);
//根据请求路径名获取子类控制器Action
//例如:actionName=/HelloAction -->HelloAction extends Action
Action action = actions.get(actionName);
//将请求委托给所对应的子控制器Action并执行execute方法
action.execute(req,resp);
-
根据请求结果码跳转页面
private void goToPage(HttpServletRequest req,HttpServletResponse resp,ActionModel actionModel,String jieguoma) throws IOException,ServletException {
//1、判断请求结果码是否为空
if(null==jieguoma)
return;
//2、根据请求结果码获取ForwardModel对应的建模实体类
ForwardModel forwardModel = actionModel.get(jieguoma);
//3、判断ForwardModel是否为空
if(null==forwardModel)
throw new RuntimeException("forwardModel不存在!!!!!!");
//4、转发或重定向
//isRedirect:检查服务器是否收到的HTTP响应状态码表示一个重定向
if(forwardModel.isRedirect())
resp.sendRedirect(req.getContextPath()+forwardModel.getPath());
else
req.getRequestDispatcher(forwardModel.getPath()).forward(req,resp);
}
4.2.反射调用方法
创建DispatcherServlet(该类专用于反射调用方法),并继承抽象子控制器类Action,重写execute方法,实现反射方法调用。
/**
* 该子控制器不做核心业务逻辑层,只负责反射调用方法
*/
public class DispatcherAtion extends Action {
@Override
public final String execute(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
try {
//1、前端必须要传入一个方法名的参数,告知等会要去的
String methodName=req.getParameter("methodName");
//2、获取类对象
Class<? extends DispatcherAtion> cls = this.getClass();
//反射调用方法
Method method = cls.getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
//设置权限
method.setAccessible(true);
//调用目标并返回结果
Object resultCode = method.invoke(this, req, resp);
//目标方法有可能无返回的方法,resultCode为null
return null==resultCode?null:resultCode.toString();
} catch (Exception e) {
throw new RuntimeException("反射调用方法异常:"+e.getMessage());
}
}
}
1.该类中的execute方法是final的,意味着子类无法重写; 2.只要是继承了DispatcherServlet的子控制器类,在调用方法时都必须要传递methodName(可以自行拟定)方法名参数,告知DispatcherServlet类你要调用的具体方法;
重要问题:
-
为什么在该类中不需要利用反射机制实例化子控制器类?
-
该类中的this代表谁?
最后,在需要的子控制器类中继承DispatcherServlet类即可,并提供一组方法(增删改查),所有方法都与DispatcherServlet中的execute方法具体相同的参数和返回值,只有方法名不同而已。
4.3.反射赋值
创建接口DriverModel<T>,提供getBean方法。
/**
* 反射赋值
* 注意:只要是子控制器实现了该接口,则表示需要在调用
* @param <T>
*/
public interface DriverModel<T> {
public T getBean();
}
再次修改ActionServlet中的业务逻辑处理流程,在反射调用方法之前,请先进行反射参数赋值操作。
/**
* 在目标方法执行之前来完成反射赋值操作
*/
private void setValue(HttpServletRequest rep,Action action){
try {
//判断你子控制器Action是否实现了DriedModel接口
if(action instanceof DriverModel){
DriverModel dm = (DriverModel) action;
Object bean = dm.getBean();
//调用的三方的方法
BeanUtils.populate(bean,rep.getParameterMap());
}
} catch (Exception e) {
throw new RuntimeException("反射赋值异常");
}
}
最后将setParameter反射赋值方法添加到ActionServlet中的doPost方法中。(必须添加在反射调用方法之前进行反射赋值,如果放置到反射调用方法之后则无意义)
......
//7、在目标方法执行之前反射赋值
this.setValue(req,action);
//5、将请求委托给具体的子控制器类取处理,并返回请求结果码
String jieguoma= action.execute(req,resp);
......
针对需要进行反射赋值的具体子控制器类,实现该接口DriverModel。
父类的execute方法举办相同的参数和返回类型
除了方法名不相同以外
注意:此时该类可以完成多件事情
public class CalAction extends DispatcherAtion implements DriverModel<CalBean> {
//注意:一定要提前在子控制器类实例化,不然报错
private CalBean calBean=new CalBean();
@Override
public CalBean getBean() {
//实例化完毕后,一定要在此数返回
return calBean;
}
}
只要实现了DriverModel接口,则必须要对实体类进行初始化,并在getModel()方法中返回实例化后的对象。重要!!重要!!重要!!
5.自定义MVC最终版
5.1.什么是框架
框架(Framework),是一套完整的解决方案,使用框架的时候,需要把你的代码放到框架合适的地方,框架会在合适的时机调用你的代码。
特点:
-
框架规定了自己的编程方式,是一套完整的解决方案
-
使用框架的时候,由框架控制一切,我们只需要按照规则写代码
-
框架的侵入性很高(从头到尾)
总之,框架具备入侵性、只是一个半成品,但是框架提供了完整的解决方案,对于代码编写效率得到了大大提升。
5.2.将自定义MVC框架打成jar包
修改web.xml
<!-- 中央控制器 -->
<servlet>
<servlet-name>ServletAction</servlet-name>
<!-- 核心控制器全路径名 -->
<servlet-class>com.zking.mvc.framework.ActionServlet</servlet-class>
<!-- 添加自定义配置 -->
<init-param>
<param-name>config</param-name>
<param-value>/mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ServletAction</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
修改ActionServlet中的init方法
@Override
public void init() throws ServletException {
//读取web.xml文件中的init-param配置文件路径
String path = this.getInitParameter("config");
//XML建模方式
configModel=ConfigModelFactory.createConfigModel(path);
}
最后,基于IDEA将自定义MVC代码打成jar包。
5.3 综合案例
基于IDEA重新创建一个新的mvcPlus
项目,并将自定义MVC的jar包和相关依赖包导入到项目,完成综合案例。
-
导入自定义MVC的jar包和相关依赖包
-
导入mvc.xml
-
配置web.xml
-
创建BookAction子控制器并继承DispatcherServlet,实现DriverModel接口,定义CRUD方法
-
创建前端JSP页面实现书本管理的CRUD操作
-
基于自定义通用分页标签完成实现书本分页查询