前言
之前的博文把自定义mvc的基本概念和流程以及初步准备工作已经介绍过了,这次给大家带来优化后的自定义mvc,这是之前的博文—>自定义mvc框架(一)
优化部分
之前的版本有很多地方需要优化,例如action存值,我定的是固定的值,等等…,这篇博文主要给大家介绍一下优化的地方和为什么要去这样优化…这里我按照的是业务逻辑运行流程去一步步优化的
增强优化一:子控制器的存值优化
之前action子控制器我是存的固定值(仅用作举例,这次用的是另外一张数据库表):
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
map=new HashMap<String,Action>();
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
}
那么我们可以通过xml配置文件的建模+工厂模式在主控制器初始化时实例化.
@Override
public void init() throws ServletException {
try {
this.configModel = ConfigModelFactory.createConfigModelFactory();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
需要实现完成业务逻辑的子控制器子类,只需要在xml文件中配置好就行:
<?xml version="1.0" encoding="UTF-8"?>
<!-- config标签:可以包含0~N个action标签 -->
<config>
<action path="/JobAction" type="com.xiaoyang.test.action.JobAction">
<!-- 默认false转发 -->
<forward name="edit" path="/edit.jsp" redirect="true" />
<forward name="detail" path="/detail.jsp" redirect="true" />
<forward name="success" path="/JobAction.action?opt=find" redirect="true" />
<forward name="list" path="/Job.jsp" redirect="false" />
</action>
</config>
可能大家对于这一步并没有直观感受优化点,这样看,这里仅作举例说明:
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
map=new HashMap<String,Action>();
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
map.put("/calc",new CalcAction());
map.put("/test",new TestAction());
}
那么如果我们实现业务逻辑的子控制器子类变多(眼睛快看失明了),需要我们一个一个在主控制器存值,页面的一个代码是非常多的,所以我们可以利用好xml配置文件的作用,将具体的子控制器子类配置到xml文件里去,可以减少我们页面的代码,那么在获取对应路径的子控制器子类时,只需要通过建模方法获取就行:
private Action findAction(String path) {
// 通过反射机制获取完整类名
try {
ActionModel am = configModel.get(path);
String type = am.getType();
// 获取对象
Class<?> c = Class.forName(type);
Action action = (Action) c.newInstance();
return action;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
增强优化二:实例化实体类对象优化
我们知道在之前处理一个业务逻辑的时候,经常需要去实例化一个实体类对象,例如模糊查询时,需要这样:
//获取表单值
String bookName = request.getParameter("bookName");
Book book=new Book();
book.setBname(bookName);
那么我们可以这样去优化:
//专门写一个方法处理modeldriver接口返回值
private void handerModelDriver(Action action,HttpServletRequest req) throws IllegalAccessException, InvocationTargetException {
Map<String, String[]> paramenter=req.getParameterMap();
//判断类是否实现某个接口
if(action instanceof ModelDriver) {
ModelDriver<?> modelDriver=(ModelDriver<?>)action;
Object model = modelDriver.getModel();
if(null!=model) {
BeanUtils.populate(model, paramenter);
}
}
}
我在查找到对应的子控制器子类后,判断是否实现ModelDriver
接口(这个接口主要用于返回实体类对象以及它的一个值)如果实现了这个类,利用反射(这里我用到的是一个工具jar,用于反射操作的)实例化Job对象(实例化一个实体类对象),又进一步减少了多余的代码.
增强优化三:怎么执行对应的方法
我们在用了自定义mvc框架后,所有的业务逻辑方法处理都是在放在了一个类:
public class JobAction extends Action{
/**
* 增加方法
* @param req
* @param resp
* @return
*/
public String add(HttpServletRequest req,HttpServletResponse resp) {
return null;
}
/**
* 删除方法
* @param req
* @param resp
* @return
*/
public String del(HttpServletRequest req,HttpServletResponse resp) {
return null;
}
/**
* 修改方法
* @param req
* @param resp
* @return
*/
public String edit(HttpServletRequest req,HttpServletResponse resp) {
return null;
}
/**
* 查询方法
* @param req
* @param resp
* @return
*/
public String select(HttpServletRequest req,HttpServletResponse resp) {
return null;
}
/**
* 加载单个方法
* @param req
* @param resp
* @return
*/
public String load(HttpServletRequest req,HttpServletResponse resp) {
return null;
}
}
那我们怎么具体的知道一个前台操作时,对应调用的一个方法呢?看流程:
<a href="JobAction.action?opt=find">查询全部岗位</a>
可以看到在请求数据时,会带一个参数opt=find
,那么我只需要通过子控制器子类进行反射,就可以调用到相对应的方法:
public final String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
try {
//反射调用方法
String methodName=this.getMethodName(req);
//拿到类对象
Class<? extends Action> c=this.getClass();
Method method = c.getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
String s =(String)method.invoke(this, req,resp);
return s;
} catch (Exception e) {
// TODO: handle exception
throw new RuntimeException(e);
}
}
public String getMethodName(HttpServletRequest req) {
String opt=req.getParameter("opt");
if(null==opt) {
throw new RuntimeException("参数opt不能为空");
}
return opt;
}
上图是在点击查询时,action
子控制器类获取到的参数,那么我在execute
方法反射调用就可以进行执行对应的方法了…
增强优化四:路径跳转
在没优化前,每次的跳转都是需要手动敲或者ctrl c+v一次的,然后跳转至对应的路径,那么在优化后,先看我们的配置文件信息:
<?xml version="1.0" encoding="UTF-8"?>
<!-- config标签:可以包含0~N个action标签 -->
<config>
<action path="/JobAction" type="com.xiaoyang.test.action.JobAction">
<!-- 默认false转发 -->
<forward name="edit" path="/edit.jsp" redirect="true" />
<forward name="detail" path="/detail.jsp" redirect="true" />
<forward name="success" path="/JobAction.action?opt=find" redirect="true" />
<forward name="list" path="/Job.jsp" redirect="false" />
</action>
</config>
那么这里面的forward
标签里的name就是我执行完对应的方法后返回的值:
public String find(HttpServletRequest req, HttpServletResponse resp) {
String companyName = req.getParameter("companyName");
job.setCompany(companyName);
//分页对象
PageBean pageBean = new PageBean();
//将请求存入分页中
pageBean.setRequest(req,pageBean);
//将pageBean分页数据存入请求
req.setAttribute("pageBean", pageBean);
//获取方法
List<Job> jobList = jd.find(job, pageBean);
//将分页查询获取到的列表数据存入请求
req.setAttribute("jobList", jobList);
//返回一个转发名
String frowardName = "list";
return frowardName;
}
根据返回的name
值,再根据forward
标签里的redirect
属性进行重定向还是转发判断,这里提到一点:增删改重定向,查询用转发.,具体实现代码:
private void handleForwardName(String actionPath,String forwardName,HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if(null==forwardName) {//返回参数forwardName,actionPath为跳转路径
return ;
}
//通过action路径找到对应的action
ActionModel actionModel = configModel.get(actionPath);
ForwardModel forwardModel = actionModel.get(forwardName);
if(forwardModel.isRedirect()) {//重定向
String url=req.getContextPath()+forwardModel.getPath();
resp.sendRedirect(url);
}else {
req.getRequestDispatcher(forwardModel.getPath()).forward(req, resp);
}
}
增强优化五:通用增删改
其实通用的增删改方法,也是利用反射进行完成的:
/**
* 通用增删改查
* @param sql 子类传递过来需要执行操作的sql
* @param t 子类类型(Job)
* @param attrs 子类对应的属性字段(id...)
* @return
* @throws SQLException
* @throws NoSuchFieldException
* @throws SecurityException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public <T> int execute(String sql,T t,String[] attrs) throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Connection con=DBAccess.getConnection();
PreparedStatement ps=con.prepareStatement(sql);
int loop=1;//定义loop值进行循环赋值操作
Field f=null;//反射调用属性实例化
for (String string : attrs) {
f=t.getClass().getDeclaredField(string);//属性循环遍历取出
f.setAccessible(true);//打开属性权限
ps.setObject(loop++, f.get(t));//将对应的属性字段进行ps执行赋值
}
int n=ps.executeUpdate();
DBAccess.close(con, ps, null);//关闭连接
return n;
}
JobDao
实现类进行增删改只需要这点代码:
/**
* 增加方法
* @param job
* @return
* @throws NoSuchFieldException
* @throws SecurityException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws SQLException
*/
public int Add(Job job) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, SQLException {
String sql = "insert into t_solr_job(id,job,company,address,salary,url,limits,time,descs,jobHandle,addressHandle) values(?,?,?,?,?,?,?,?,?,?,?)";
return super.execute(sql, job,new String[] {"id","job","company","address","salary","url","limits","time","descs","jobHandle","addressHandle"} );
}
/**
* 删除方法
* @param id
* @return
* @throws SQLException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws SecurityException
* @throws NoSuchFieldException
*/
public int del(Job j) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, SQLException {
String sql="delete from t_solr_job where id=? ";
return super.execute(sql, j, new String[] {"bid"});
}
/**
* 修改方法
* @param j
* @return
* @throws SQLException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws SecurityException
* @throws NoSuchFieldException
*/
public int edit(Job j,String id) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, SQLException {
String sql="update t_solr_job set id=?,job=?,company=?,address=?,salary=?,url=?,limits=? where id=?";
return super.execute(sql, j, new String[] {"job","company","address","salary","url","limits","time","descs","jobHandle","addressHandle","id"});
}
整体功能流程
案例
结合mysql+bootstrap+通用分页+自定义mvc
做了一个Job职位信息的增删改查:
主控制器:ActionServlet
package com.xiaoyang.framework;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import org.dom4j.DocumentException;
/**
* 中央控制器
*
* @author xiaoyang 2020年6月7日
*/
public class ActionServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
// private Map<String, Action> actionMap=new HashMap<String, Action>();
// 初始化方法
private ConfigModel configModel = null;
@Override
public void init() throws ServletException {
// actionMap.put("/addStudentAction", new AddStudentAction());
// actionMap.put("/DelStudentAction", new DelStudentAction());
try {
this.configModel = ConfigModelFactory.createConfigModelFactory();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
doPost(req, resp);
}
private Action findAction(String path) {
// 通过反射机制获取完整类名
try {
ActionModel am = configModel.get(path);
String type = am.getType();
// 获取对象
Class<?> c = Class.forName(type);
Action action = (Action) c.newInstance();
return action;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//专门写一个方法处理modeldriver接口返回值
private void handerModelDriver(Action action,HttpServletRequest req) throws IllegalAccessException, InvocationTargetException {
Map<String, String[]> paramenter=req.getParameterMap();
//判断类是否实现某个接口
if(action instanceof ModelDriver) {
ModelDriver<?> modelDriver=(ModelDriver<?>)action;
Object model = modelDriver.getModel();
if(null!=model) {
BeanUtils.populate(model, paramenter);
}
}
}
private void handleForwardName(String actionPath,String forwardName,HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if(null==forwardName) {
return ;
}
//通过action路径找到对应的action
ActionModel actionModel = configModel.get(actionPath);
ForwardModel forwardModel = actionModel.get(forwardName);
if(forwardModel.isRedirect()) {//重定向
String url=req.getContextPath()+forwardModel.getPath();
resp.sendRedirect(url);
}else {
req.getRequestDispatcher(forwardModel.getPath()).forward(req, resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
// 获取请求路径
String path = req.getServletPath();
path = path.substring(0, path.indexOf("."));
//找子控制器
Action action = this.findAction(path);
//处理modeldriver接口返回值
try {
this.handerModelDriver(action, req);
} catch (IllegalAccessException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 将请求委托给子控制器处理
String forwardName = action.execute(req, resp);
//根据转发对象名进行流程转发或者重定向
this.handleForwardName(path, forwardName, req, resp);
}
}
子控制器Action:
package com.xiaoyang.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 xiaoyang
* 2020年6月7日
*/
public abstract class Action {
public final String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
try {
//反射调用方法
String methodName=this.getMethodName(req);
//拿到类对象
Class<? extends Action> c=this.getClass();
Method method = c.getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
String s =(String)method.invoke(this, req,resp);
return s;
} catch (Exception e) {
// TODO: handle exception
throw new RuntimeException(e);
}
}
public String getMethodName(HttpServletRequest req) {
String opt=req.getParameter("opt");
if(null==opt) {
throw new RuntimeException("参数opt不能为空");
}
return opt;
}
//public abstract String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
}
业务处理(子控制器子类):JobAction
package com.xiaoyang.test.action;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.xiaoyang.framework.Action;
import com.xiaoyang.framework.ModelDriver;
import com.xiaoyang.test.dao.JobDao;
import com.xiaoyang.test.entity.Job;
import com.xiaoyang.test.util.PageBean;
import com.xiaoyang.test.util.StringUtils;
public class JobAction extends Action implements ModelDriver<Job> {
private JobDao jd = new JobDao();
private Job job = new Job();
@Override
public Job getModel() {
return job;
}
public String find(HttpServletRequest req, HttpServletResponse resp) {
String companyName = req.getParameter("companyName");
job.setCompany(companyName);
//分页对象
PageBean pageBean = new PageBean();
//将请求存入分页中
pageBean.setRequest(req,pageBean);
//将pageBean分页数据存入请求
req.setAttribute("pageBean", pageBean);
//获取方法
List<Job> jobList = jd.find(job, pageBean);
//将分页查询获取到的列表数据存入请求
req.setAttribute("jobList", jobList);
//返回一个转发名
String frowardName = "list";
return frowardName;
}
public String del(HttpServletRequest req,HttpServletResponse resp) {
//获取要删除的id值
//调用方法
try {
jd.del(job);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
//返回删除name值
return "success";
}
public String add(HttpServletRequest req,HttpServletResponse resp) {
//获取要删除的id值
//调用方法
try {
jd.Add(job);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
//返回删除name值
return "success";
}
public String edit(HttpServletRequest req,HttpServletResponse resp) {
//获取要修改的id值
String parameter = req.getParameter("id");
//调用方法
try {
jd.edit(job,parameter);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
//返回删除name值
return "success";
}
public String load(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException {
Job j = jd.find(job, null).get(0);
// req.setAttribute("j", j);
HttpSession session = req.getSession();
session.setAttribute("j", j);
String optName=req.getParameter("optName");
//调用方法
//返回删除name值
if(StringUtils.isBlank(optName)) {//如果没传optName参数,代表跳转至修改界面
optName="edit";
}
return optName;
}
}
其他的实体类什么的就不贴啦,通用方法在上面也已经贴过了,直接上效果图:
查询显示:
增加:
删除:
注:修改和增加差不多就不过多贴图片了
尾言
自定义mvc框架就到这了,写的比较久,有些地方可能没注意,有不足的地方欢大家指正…