一、中央控制器动态加载存储子控制器
思路:
为了防止没写一个servlet就要配置一个地址,所以我们通过建模来实现重复代码的配置(这边博主前面J2EE–XML的博客已经写了有具体的博客),假设我们的配置文件名为config.xml,那么通过建模可以得到,最终configModel对象会包含config.xml中所有子控制,同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
配置文件:config.xml(type属性值里面放子控制器要处理的servlet的路径)
<?xml version="1.0" encoding="UTF-8"?>
<config>
<action path="/book" type="com.xlb.servlet.BookAction">
<forward name="failed" path="/demo3.jsp" redirect="true" />
<forward name="success" path="/demo2.jsp" redirect="false" />
</action>
</config>
配置action标签 ActionModel.java
package com.xlb.framework;
import java.util.HashMap;
import java.util.Map;
/**
* action标签
* @author 波哥
*
* 2022年6月14日 下午10:58:53
*/
public class ActionModel {
//<action path="/regAction" type="test.RegAction">
private String path;
private String type;
private Map<String,ForwardModel> fMap=new HashMap<>();
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;
}
//两个行为,增加forwardModule对象,查找forwardModel对象
//将一个新的forward标签对象加入容器
public void push(ForwardModel forwardModel) {
fMap.put(forwardModel.getName(),forwardModel);
}
public ForwardModel pop(String name) {
return fMap.get(name);
}
}
配置config标签 ConfigModel.java
package com.xlb.framework;
import java.util.HashMap;
import java.util.Map;
/**
* config标签
* @author 波哥
*
* 2022年6月14日 下午10:59:25
*/
public class ConfigModel {
private Map<String,ActionModel> aMap=new HashMap<>();
public void push(ActionModel actionModel) {
aMap.put(actionModel.getPath(), actionModel);
}
public ActionModel pop(String path) {
return aMap.get(path);
}
}
配置forward标签 ForwardModel.java
package com.xlb.framework;
/**
* forward标签
* @author 波哥
*
* 2022年6月14日 下午10:59:11
*/
public class ForwardModel {
//<forward name="failed" path="/login.jsp" redirect="false" />
private String name;
private String path;
private boolean redirect;
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;
}
}
以前我们是通过map集合来拿到子控制器,现在我们只需要调用配置问器即可,所有我们定义一个ConfigModel,因为定义一开始是空的,所有我们在初始化时就给ConfigModel初始赋值
现在的中央控制器
package com.xlb.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 com.xlb.servlet.BookAction;
/**
* 中央控制器
* 主要职能:接收浏览器请求,找到对应的处理人
* @author 波哥
*
* 2022年6月25日 上午9:34:52
*/
//@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{
//根据键拿到值
//private Map<String, Action> actions=new HashMap<String, Action>();
/*
* 通过建模可以得到,最终configModel对象会包含config.xml中所有子控制器信息(non-Javadoc)
* 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
*/
private ConfigModel configModel;
//初始化方法 (程序启动时,只会被加载一次)
@Override
public void init() throws ServletException {
//actions.put("/book", new BookAction());
//初始化ConfigModel
try {
//配置地址
//getInitParameter的作业是拿到web.xml中的servlet信息配置的参数
String configLocation = this.getInitParameter("configLocation");
if(configLocation == null || "".equals(configLocation) ) {
//如果没有传 就用原来的
configModel = ConfigModelFactory.bulid();
}else {
configModel = ConfigModelFactory.bulid(configLocation);
}
} 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 {
//拿到请求地址
String uri = req.getRequestURI(); //http://localhost:8080/mvc/book.action?methodName=list
//要拿到 /book (就是最后一个斜杠到最后一个点的位置)
uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//Action action = actions.get(uri);
//相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
//首先拿到type值(全路径名)
ActionModel actionModel = configModel.pop(uri);
if(actionModel == null) {
throw new RuntimeException("action 配置错误");
}
//正式调用方法前,book中属性要被赋值
//返回一个方法返回的结果
action.execute(req, resp);
//action.execute(req, resp);
}
}
那么我们就实现了中央控制器动态加载存储子控制器
二、参数传递封装优化
因为我们传参数可能需要传入很多参数,那么就会有很多req.getp…,而且还特别容易写错,所以为了避免这种错,我们就写一个泛类接口来拿到该类的所有属性,作用是模型驱动接口接收前台JSP传递的参数,并且封装,那么我们在在中央控制器要调用方法之前给我们要加入的实体赋值,然后再传入到要接收的子控制器,就可以完成参数传递封装优化
模型驱动接口ModelDriven.java
package com.xlb.framework;
/**
* 作用:模型驱动接口接收前台JSP传递的参数,并且封装
* @author 波哥
*
* 2022年6月28日 上午10:20:01
*/
public interface ModelDriven<T> {
//拿到将要封装的类实例 ModelDriven.getModel() --> new Book();
T getModel();
}
然后我们的子控制器来实现ModelDriven接口,实现这个接口那么就会要实现这个接口所有的方法
BookAction.java
package com.xlb.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.xlb.entity.Book;
import com.xlb.framework.Action;
import com.xlb.framework.ActionSupport;
import com.xlb.framework.ModelDriven;
public class BookAction extends ActionSupport implements ModelDriven<Book>{
private Book book = new Book();
private void load(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用load方法");
}
private void list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用list方法");
}
private void edit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用edit方法");
}
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 b=new Book(Integer.valueOf(bid), bname, Float.valueOf(price));
System.out.println("在同一个servlet中调用add方法");
}
}
中央控制器
package com.xlb.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 com.xlb.servlet.BookAction;
/**
* 中央控制器
* 主要职能:接收浏览器请求,找到对应的处理人
* @author 波哥
*
* 2022年6月25日 上午9:34:52
*/
//@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{
//根据键拿到值
//private Map<String, Action> actions=new HashMap<String, Action>();
/*
* 通过建模可以得到,最终configModel对象会包含config.xml中所有子控制器信息(non-Javadoc)
* 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
*/
private ConfigModel configModel;
//初始化方法 (程序启动时,只会被加载一次)
@Override
public void init() throws ServletException {
//actions.put("/book", new BookAction());
//初始化ConfigModel
try {
configModel = 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 {
//拿到请求地址
String uri = req.getRequestURI(); //http://localhost:8080/mvc/book.action?methodName=list
//要拿到 /book (就是最后一个斜杠到最后一个点的位置)
uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//Action action = actions.get(uri);
//相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
//首先拿到type值(全路径名)
ActionModel actionModel = configModel.pop(uri);
if(actionModel == null) {
throw new RuntimeException("action 配置错误");
}
//type是Action子控制器的全路径名
String type = actionModel.getType();
try {
Action action = (Action)Class.forName(type).newInstance();
// action 现在是 bookAction
if(action instanceof ModelDriven) {//实现了这个接口
//
ModelDriven md = (ModelDriven)action;
//md指的是类实例 book
Object model = md.getModel();
//要给model中的属性赋值(未知属性) 要接收前端jsp参数 req.getParameterMap();里面都有
//利用工具 类 把前端的值封装到实体类里面去
BeanUtils.populate(model, req.getParameterMap());
System.out.println(model);
}
//正式调用方法前,book中属性要被赋值
action.execute(req, resp);
//action.execute(req, resp);
}
}
那么到这我们的参数传递封装优化就完成了
三、返回值页面跳转优化
因为我们每次调用不同的方法,可能都会跳转页面,跳转无法接收重定向和转发,刚好我们的配置文件可以来判断该用户的请求要进行什么样的跳转操作,配置文件里面参数path就是要跳转的路径,而redirect就是是否是重定向,如果为true接收重定向,如果为false接收否,那么在这我们就要给我们的抽象方法抽象定义,因为我们要返回一个值来判断改请求是转发还是重定向,所有我们的子控制器的抽象方法就为
//不知道这个事情具体该怎么做,单必须要做,这样我们就用抽象 String execute(HttpServletRequest req, HttpServletResponse resp);
返回一个String,相应的实现这个接口的类也要改变
ActionSupport.java
package com.xlb.framework;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author 波哥
*
* 2022年6月25日 上午9:40:09
*/
public class ActionSupport implements Action{
@Override
public String execute(HttpServletRequest req, HttpServletResponse resp) {
//为了区分当前请求的目的,增删改查的目的,就从前台讲要调用的方法名传递到后台
String methodName = req.getParameter("methodName");
//methodName可能是add/del/edit/list/load/xxx/....
//前台传递什么方法,就调用当前类的对应方法 (利用反射)
try {
//拿到方法对象
Method m = this.getClass().getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
//打开访问权限
m.setAccessible(true);
return (String)m.invoke(this, req , resp);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
我们的BookAction.java
package com.xlb.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.xlb.entity.Book;
import com.xlb.framework.Action;
import com.xlb.framework.ActionSupport;
import com.xlb.framework.ModelDriven;
public class BookAction extends ActionSupport implements ModelDriven<Book>{
private Book book = new Book();
private void load(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用load方法");
}
private void list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用list方法");
}
private void edit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("在同一个servlet中调用edit方法");
}
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 b=new Book(Integer.valueOf(bid), bname, Float.valueOf(price));
System.out.println("在同一个servlet中调用add方法");
return "failed";
}
@Override
public Book getModel() {
return book;
}
}
中央控制器
package com.xlb.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 com.xlb.servlet.BookAction;
/**
* 中央控制器
* 主要职能:接收浏览器请求,找到对应的处理人
* @author 波哥
*
* 2022年6月25日 上午9:34:52
*/
//@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{
//根据键拿到值
//private Map<String, Action> actions=new HashMap<String, Action>();
/*
* 通过建模可以得到,最终configModel对象会包含config.xml中所有子控制器信息(non-Javadoc)
* 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
*/
private ConfigModel configModel;
//初始化方法 (程序启动时,只会被加载一次)
@Override
public void init() throws ServletException {
//actions.put("/book", new BookAction());
//初始化ConfigModel
try {
configModel = 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 {
//拿到请求地址
String uri = req.getRequestURI(); //http://localhost:8080/mvc/book.action?methodName=list
//要拿到 /book (就是最后一个斜杠到最后一个点的位置)
uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//Action action = actions.get(uri);
//相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
//首先拿到type值(全路径名)
ActionModel actionModel = configModel.pop(uri);
if(actionModel == null) {
throw new RuntimeException("action 配置错误");
}
//type是Action子控制器的全路径名
String type = actionModel.getType();
try {
Action action = (Action)Class.forName(type).newInstance();
// action 现在是 bookAction
if(action instanceof ModelDriven) {//实现了这个接口
//
ModelDriven md = (ModelDriven)action;
//md指的是类实例 book
Object model = md.getModel();
//要给model中的属性赋值(未知属性) 要接收前端jsp参数 req.getParameterMap();里面都有
//利用工具 类 把前端的值封装到实体类里面去
BeanUtils.populate(model, req.getParameterMap());
System.out.println(model);
}
//正式调用方法前,book中属性要被赋值
//返回一个方法返回的结果
String result = action.execute(req, resp);
//先拿到action对象 在拿到里面forward标签
//拿到ForwardModel
ForwardModel forwardModel = actionModel.pop(result);
//假设配置错误
if(forwardModel == null) {
throw new RuntimeException("Forward 配置错误");
}
//path表示要跳转的路径
String path = forwardModel.getPath();
//拿到redirect进行判断
boolean redirect = forwardModel.isRedirect();
if(redirect) {//为true
resp.sendRedirect(req.getServletContext().getContextPath()+path);
}
else {
//拿到是否需要转发的配置
req.getRequestDispatcher(req.getServletContext().getContextPath() +path).forward(req, resp);
}
} catch (Exception e) {
e.printStackTrace();
}
//action.execute(req, resp);
}
}
到这我们的返回值页面跳转优化就优化完成了
四、框架配置文件
因为最终我们的所欲类都要打包成一个jar包,而jar包等下是不能改变的,所以我们只能通过改版web.xml来配置我们的中央控制器,来体现我们的框架流畅性
配置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>T280_mvc</display-name>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>com.xlb.framework.DispatcherServlet</servlet-class>
<init-param>
<!-- 可以把值传到中央控制器 -->
<param-name>configLocation</param-name>
<param-value>/wuyanzu.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
我们要在中央控制器初始化前就判断该配置文件地址是否已经传入,如果传入,就用该传入的,如果没有传入,就用之前的
中央控制器
package com.xlb.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 com.xlb.servlet.BookAction;
/**
* 中央控制器
* 主要职能:接收浏览器请求,找到对应的处理人
* @author 波哥
*
* 2022年6月25日 上午9:34:52
*/
//@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{
//根据键拿到值
//private Map<String, Action> actions=new HashMap<String, Action>();
/*
* 通过建模可以得到,最终configModel对象会包含config.xml中所有子控制器信息(non-Javadoc)
* 同时为了解决中央控制器能够动态加载保存子控制器的信息,那么我们只需要引入configModel对象即可
*/
private ConfigModel configModel;
//初始化方法 (程序启动时,只会被加载一次)
@Override
public void init() throws ServletException {
//actions.put("/book", new BookAction());
//初始化ConfigModel
try {
//配置地址
//getInitParameter的作业是拿到web.xml中的servlet信息配置的参数
String configLocation = this.getInitParameter("configLocation");
if(configLocation == null || "".equals(configLocation) ) {
//如果没有传 就用原来的
configModel = ConfigModelFactory.bulid();
}else {
configModel = ConfigModelFactory.bulid(configLocation);
}
} 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 {
//拿到请求地址
String uri = req.getRequestURI(); //http://localhost:8080/mvc/book.action?methodName=list
//要拿到 /book (就是最后一个斜杠到最后一个点的位置)
uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
//Action action = actions.get(uri);
//相比于上一种从map集合获取子控制器,当前需要获取config.xml中的全路径名,然后反射实例化
//首先拿到type值(全路径名)
ActionModel actionModel = configModel.pop(uri);
if(actionModel == null) {
throw new RuntimeException("action 配置错误");
}
//type是Action子控制器的全路径名
String type = actionModel.getType();
try {
Action action = (Action)Class.forName(type).newInstance();
// action 现在是 bookAction
if(action instanceof ModelDriven) {//实现了这个接口
//
ModelDriven md = (ModelDriven)action;
//md指的是类实例 book
Object model = md.getModel();
//要给model中的属性赋值(未知属性) 要接收前端jsp参数 req.getParameterMap();里面都有
//利用工具 类 把前端的值封装到实体类里面去
BeanUtils.populate(model, req.getParameterMap());
System.out.println(model);
}
//正式调用方法前,book中属性要被赋值
//返回一个方法返回的结果
String result = action.execute(req, resp);
//先拿到action对象 在拿到里面forward标签
//拿到ForwardModel
ForwardModel forwardModel = actionModel.pop(result);
//假设配置错误
if(forwardModel == null) {
throw new RuntimeException("Forward 配置错误");
}
//path表示要跳转的路径
String path = forwardModel.getPath();
//拿到redirect进行判断
boolean redirect = forwardModel.isRedirect();
if(redirect) {//为true
resp.sendRedirect(req.getServletContext().getContextPath()+path);
}
else {
//拿到是否需要转发的配置
req.getRequestDispatcher(req.getServletContext().getContextPath() +path).forward(req, resp);
}
} catch (Exception e) {
e.printStackTrace();
}
//action.execute(req, resp);
}
}
那么到这我们的框架配置文件也就完成了