自定义mvc(二)

自定义mvc框架(二 )

前言

之前的博文把自定义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框架就到这了,写的比较久,有些地方可能没注意,有不足的地方欢大家指正…

猜你喜欢

转载自blog.csdn.net/qq_45510899/article/details/106608616