目录
一.让中央控制器动态加载存储子控制器
昨天的代码是我每添加一个子控制器我都要在中央控制器中去添加代码,也就是说代码是死的,现在就是我们要让它变活
①首先我们要写一个jar包
②配置文件
③debug运行一下,我们来看一下我们打的断点运行
4我们示范一下报500错误
DispatcherServlet代码块:主要改了中央控制器
package com.jiangwenjuan.framework;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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 com.jiangwenjuan.web.BookAction;
/**
* 中央控制器:
* 主要职能:接收浏览器请求,找到对应的处理人
* @author 蒋文娟
*
* @date 2022年6月25日 上午11:36:12
*/
// 只要以action结尾的都会传递过来
@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{
//这里就有一个变量 map集合我通过一个新的那肯定找到一个键值对的形式
// private Map<String, Action> actions = new HashMap<String, Action>();
/*
* 通过建模我们可以知道,最终configModel对象会包含config.xml中的所有子控制器信息,
* 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
*/
private ConfigModel comfigModel;
// 程序启动时,只会加载一次
@Override
public void init() throws ServletException {
// 这里肯定不能定s后面在优化 这里就用了建模
// actions.put("/book", new BookAction());
// 这里就不在是定死的了
try {
comfigModel = ConfigModelFactory.bulid();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//那么在这里面会接收浏览器所以的请求
// http:localhost:8080:/J_mvc/book.action?methodName=list
//拿到路径
String uri = req.getRequestURI();
//那么现在我们要把/book截取出来
//拿到/book,就是最后一个/到最好一个.的位置
uri = uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//接收传递过来的地址
// Action action = actions.get(uri);
// 相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后放射实例化
ActionModel actionModel = comfigModel.pop(uri);
if(actionModel == null) {
throw new RuntimeException("action 配置错误");
}
//然后我们就可以拿到type值
String type = actionModel.getType();
try {
// type是Action子控制器的全路径名
Action action = (Action) Class.forName(type).newInstance();
action.execute(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
config.xml代码块:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 哪个类里面包含了整个配置文件中的所有信息,就是ConfigModelFactory代码块 -->
<config>
<action path="/book" type="com.jiangwenjuan.web.BookAction">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
<action path="order" type="com.jiangwenjuan.web.OrderServlet">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
</config>
二.参数传递封装优化
1.存在问题
我们继续在demo1.jsp加一段代码,这里我们就只在增加加了数据
<h3>参数传递封装优化</h3>
<a href="${pageContext.request.contextPath }/book.action?methodName=add&bid=989898&bname=laoliu&price=89">增加</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=del">删除</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=deit">修改</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=list">查询</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=load">回显</a>
运行出来:
2.解决问题
参数传递封装优化为了就是解决这个参数传递过多
这里就会存在问题:什么问题呢,如果说这个表有20个字段,那么就要写41行,上面20,下面20
一般来说bookAction操作book表,处理book请求,如果说我们这个传参不写,我这个book默认就能接受到这些参数值,假设有这么一个类,能实现。
我们就去创建一个ModelDriven.java
package com.jiangwenjuan.framework;
/**
* 模型驱动接口加了个泛型
* 模型驱动接口:接收前台JSP传递的参数,并且封装到实体类中
* @author 蒋文娟
*
* @date 2022年6月28日 上午9:17:26
*/
public interface ModelDriven<T> {
// 拿到将要被封装的类实例 ModeDriven.getModel() ---等价--->new book();
T getModel();
}
写一个实体类book
package com.jiangwenjuan.entity;
public class Book {
private int bid;
private String bname;
private float price;
public int getBid() {
return bid;
}
public void setBid(int bid) {
this.bid = bid;
}
public String getBname() {
return bname;
}
public void setBname(String bname) {
this.bname = bname;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public String toString() {
return "Book [bid=" + bid + ", bname=" + bname + ", price=" + price + "]";
}
}
bookAction.java,封装一个book,实现ModelDriven《Book》,实现方法即可
package com.jiangwenjuan.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jiangwenjuan.entity.Book;
import com.jiangwenjuan.framework.Action;
import com.jiangwenjuan.framework.ActionSupport;
import com.jiangwenjuan.framework.ModelDriven;
/**
* 子类
* @author 蒋文娟
*
* @date 2022年6月25日 上午11:45:04
*/
public class BookAction extends ActionSupport implements ModelDriven<Book>{
// 我把我将要封装的实体类提出来了
private Book book = new Book();
//现在就可以直接在这里写方法了
private void load(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用 查询单个 方法");
}
private void list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用 list 方法");
}
private void deit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用 deit 方法");
}
private void del(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用 del 方法");
}
private void add(HttpServletRequest req, HttpServletResponse resp) {
//接收数据
// String bid = req.getParameter("bid");
// String bname = req.getParameter("bname");
// String price = req.getParameter("price");
// Book book = new Book();
// book.setBid(Integer.valueOf(bid));
// book.setBname(bname);
// book.setPrice(Float.valueOf(price));
// bookDao.add(book)
System.out.println("在同一个servlet中调用 add 方法");
}
@Override
public Book getModel() {
// TODO Auto-generated method stub
return book;
}
}
DispatcherServlet代码块:
package com.jiangwenjuan.framework;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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.apache.commons.beanutils.PropertyUtils;
import com.jiangwenjuan.web.BookAction;
/**
* 中央控制器:
* 主要职能:接收浏览器请求,找到对应的处理人
* @author 蒋文娟
*
* @date 2022年6月25日 上午11:36:12
*/
// 只要以action结尾的都会传递过来
@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{
//这里就有一个变量 map集合我通过一个新的那肯定找到一个键值对的形式
// private Map<String, Action> actions = new HashMap<String, Action>();
/*
* 通过建模我们可以知道,最终configModel对象会包含config.xml中的所有子控制器信息,
* 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
*/
private ConfigModel comfigModel;
// 程序启动时,只会加载一次
@Override
public void init() throws ServletException {
// 这里肯定不能定s后面在优化 这里就用了建模
// actions.put("/book", new BookAction());
//那么你还要一个order就直接在下面写
// actions.put("/order", new BookAction());
try {
comfigModel = ConfigModelFactory.bulid();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//那么在这里面会接收浏览器所以的请求
// http:localhost:8080:/J_mvc/book.action?methodName=list
//拿到路径
String uri = req.getRequestURI();
//那么现在我们要把/book截取出来
//拿到/book,就是最后一个/到最好一个.的位置
uri = uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//接收传递过来的地址
// Action action = actions.get(uri);
// 相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后放射实例化
ActionModel actionModel = comfigModel.pop(uri);
if(actionModel == null) {
throw new RuntimeException("action 配置错误");
}
//然后我们就可以拿到type值
String type = actionModel.getType();
try {
// type是Action子控制器的全路径名
Action action = (Action) Class.forName(type).newInstance();
// action 是bookAction instanceof:这个代表你实现了哪个类哪个接口
if(action instanceof ModelDriven) {
//我把这个action向下转型
ModelDriven md = (ModelDriven) action;
// model指的是bookAction中的book实例
Object model = md.getModel();
// 要给model中的属性赋值,要接收前端jsp的参数 req.getparameterMap()
//从某一个对象中取到某一个值
// PropertyUtils.getProperty(bean, name)
//将前端所有参数值封装进实体类
BeanUtils.populate(model, req.getParameterMap());
System.out.println(model);
}
// 正式调用方法前,book中的属性要被赋值
action.execute(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行出来:
三. 对于方法执行结果转发重定向优化
当我们点击删除的是不是重定向到当前页面,要不就是重定向要不就是转发,上面只是处理我浏览器发的请求,但至于我处理完了之后是转发还是重定向,我们上面还没有做。目前上面只是把转发和重定向写死了,就会没有价值,只能跳转到一个界面
最终希望我不需要改中央控制器,我就能控制bookaction新增方法,书籍的修改以及删除方法,也能跳到查询方法,当我点击修改的方法,我希望做一个数据回显。查询的结果是转发还重定向。最终我要控制跳转的路径,怎么控制。
在我们的配置文件:成功了怎么样,失败了怎么样,如果为false就转发,为true就重定向
<?xml version="1.0" encoding="UTF-8"?>
<!-- 哪个类里面包含了整个配置文件中的所有信息 -->
<config>
<action path="/book" type="com.jiangwenjuan.web.BookAction">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
<action path="order" type="com.jiangwenjuan.web.OrderServlet">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
</config>
子控制器参加一个返回值:
package com.jiangwenjuan.framework;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 子控制器:
* 对应请求的处理人
* @author 蒋文娟
*
* @date 2022年6月25日 上午11:39:01
*/
public interface Action {
//封装
String execute(HttpServletRequest req, HttpServletResponse resp);
}
接着在去ActionSupport.java代码块:
package com.jiangwenjuan.framework;
import java.io.IOException;
import java.lang.reflect.Method;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 支持
* @author 蒋文娟
*
* @date 2022年6月25日 上午11:48:57
*/
public class ActionSupport implements Action{
//你写了我就给你调用
@Override
public String execute(HttpServletRequest req, HttpServletResponse resp) {
//为了区分当前请求的目的,增删改查的目的,就从前台将要调用的方法名传递到后台
String methodName = req.getParameter("methodName");//假设这个方法名就叫parameter
// methodName可能是add/del/edit/list/load/xxx/yyy/aaa...
// 前台传递什么方法,就调用当前类的对应方法 //方法对象
try { //BookServlet.class
Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
//打开访问权限
m.setAccessible(true);
//调用当前类实例的methodName 方法,你传什么调什么
return (String) m.invoke(this, req,resp);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
在DispatcherServlet会返回一个结果:
package com.jiangwenjuan.framework;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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.apache.commons.beanutils.PropertyUtils;
import com.jiangwenjuan.web.BookAction;
/**
* 中央控制器:
* 主要职能:接收浏览器请求,找到对应的处理人
* @author 蒋文娟
*
* @date 2022年6月25日 上午11:36:12
*/
// 只要以action结尾的都会传递过来
@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{
//这里就有一个变量 map集合我通过一个新的那肯定找到一个键值对的形式
// private Map<String, Action> actions = new HashMap<String, Action>();
/*
* 通过建模我们可以知道,最终configModel对象会包含config.xml中的所有子控制器信息,
* 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
*/
private ConfigModel comfigModel;
// 程序启动时,只会加载一次
@Override
public void init() throws ServletException {
// 这里肯定不能定s后面在优化 这里就用了建模
// actions.put("/book", new BookAction());
//那么你还要一个order就直接在下面写
// actions.put("/order", new BookAction());
try {
comfigModel = ConfigModelFactory.bulid();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//那么在这里面会接收浏览器所以的请求
// http:localhost:8080:/J_mvc/book.action?methodName=list
//拿到路径
String uri = req.getRequestURI();
//那么现在我们要把/book截取出来
//拿到/book,就是最后一个/到最好一个.的位置
uri = uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//接收传递过来的地址
// Action action = actions.get(uri);
// 相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后放射实例化
ActionModel actionModel = comfigModel.pop(uri);
if(actionModel == null) {
throw new RuntimeException("action 配置错误");
}
//然后我们就可以拿到type值
String type = actionModel.getType();
try {
// type是Action子控制器的全路径名
Action action = (Action) Class.forName(type).newInstance();
// action 是bookAction instanceof:这个代表你实现了哪个类哪个接口
if(action instanceof ModelDriven) {
//我把这个action向下转型
ModelDriven md = (ModelDriven) action;
// model指的是bookAction中的book实例
Object model = md.getModel();
// 要给model中的属性赋值,要接收前端jsp的参数 req.getparameterMap()
//从某一个对象中取到某一个值
// PropertyUtils.getProperty(bean, name)
//将前端所有参数值封装进实体类
BeanUtils.populate(model, req.getParameterMap());
System.out.println(model);
}
// 正式调用方法前,book中的属性要被赋值
// action.execute(req, resp);
String result = action.execute(req, resp);
ForwardModel forwardModel = actionModel.pop(result);
//如果配置文件配错了或者没有配
// if(forwardModel == null) {//可以选择加也可以选择不加
// throw new RuntimeException("forward config error");
// }
// /bookList.jsp /index.jsp
String path = forwardModel.getPath();
// 拿到是否需要转发的配置
boolean f = forwardModel.isRedirect();
if(f) {//如果为true就说明重定向
resp.sendRedirect(req.getServletContext() + path);
}
else {//为false就转发
req.getRequestDispatcher(path).forward(req, resp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
我们在配置文件中:就以第一个为例
<?xml version="1.0" encoding="UTF-8"?>
<!-- 哪个类里面包含了整个配置文件中的所有信息 -->
<config>
<action path="/book" type="com.jiangwenjuan.web.BookAction">
<forward name="success" path="demo2.jsp" redirect="false" />
<forward name="failed" path="/demo3.jsp" redirect="true" />
</action>
<action path="order" type="com.jiangwenjuan.web.OrderServlet">
<forward name="failed" path="/login.jsp" redirect="false" />
<forward name="success" path="/main.jsp" redirect="true" />
</action>
</config>
我们在bookAction纯写方法的我先说,当我点击查询的时候跳转到转发,而我点增加的时候跳转到重定向
package com.jiangwenjuan.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jiangwenjuan.entity.Book;
import com.jiangwenjuan.framework.Action;
import com.jiangwenjuan.framework.ActionSupport;
import com.jiangwenjuan.framework.ModelDriven;
/**
* 子类
* @author 蒋文娟
*
* @date 2022年6月25日 上午11:45:04
*/
public class BookAction extends ActionSupport implements ModelDriven<Book>{
// 我把我将要封装的实体类提出来了
private Book book = new Book();
//现在就可以直接在这里写方法了
private void load(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用 查询单个 方法");
}
private String list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用 list 方法");
return "success";
}
private void deit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用 deit 方法");
}
private void del(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用 del 方法");
}
private String add(HttpServletRequest req, HttpServletResponse resp) {
//接收数据
// String bid = req.getParameter("bid");
// String bname = req.getParameter("bname");
// String price = req.getParameter("price");
// Book book = new Book();
// book.setBid(Integer.valueOf(bid));
// book.setBname(bname);
// book.setPrice(Float.valueOf(price));
// bookDao.add(book)
System.out.println("在同一个servlet中调用 add 方法");
return "failed";
}
@Override
public Book getModel() {
// TODO Auto-generated method stub
return book;
}
}
在我们写了一个demo2.jsp和demo3.jsp,demo2是打印的是转发,demo3是重定向.
我们要去发起请求去运行在demo1
测试:我们预测的结果是当我点击新增会调用bookaction里面的add方法,add方法的返回值是failed而failed在配置文件中对应的是demo3.jsp,对应的是重定向。
点我点击list的时候查询数据必须是转发吧,list方法的返回值是success,在配置文件中对应的是demo2转发。
点击增加是重定向。点击查询的是转发
四.框架配置文件可变
我把配置文件换了一个名字mvc.xml,然后就不能跑了。因为我们在ConfigModelFactory所以不行
我想改什么名字就改什么名字也能正常运行,每一个框架都有配置文件就会重名,是不是不能让它重名,你改了名字但别人框架只认这个名字。这个框架的名字我们也去读取配置。
所以说我们要写一个有参的一个没参的,就用在这里。
在init()方法里面写,上面的这个
/@WebServlet("*.action")
配置文件不写在DispatcherServlet里面了,写到web.xml里面去
在这里面就写核心的中央控制器,我为什么要采取web.xml因为我要把我的框架配置文件的名字传到后台去,因为这个DispatcherServlet类它要打成jar包,init()是不允许改的,当你上面这个写si 了还怎么去传配置文件的名字啊,所以传不了,只能在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>J_mvc</display-name>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>com.jiangwenjuan.framework.DispatcherServlet</servlet-class>
<init-param>
<param-name>configLocation</param-name>
<param-value>/jwj.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
运行看一下有没有拿到jwj.xml
然后最终就跳转到重定向
那么这样我的代码就更加灵活了。
最终我的mvc的使用的类