精华内容
下载资源
问答
  • 自定义MVC框架

    2019-01-11 09:25:22
    自己写的MVC框架源码,实现了几乎所有Struts的功能,利用此代码可以很好的了解市面上的MVC框架,包括Spring MVC和Struts。
  • php-mvc-core 用于php的自定义MVC框架
  • 这是基于PHP面向对象的自定义MVC框架高级项目开发视频
  • 自定义MVC框架.md

    2020-06-17 23:37:02
    本文档中的大纲包含J2EE的知识点,有“Java环境搭建”,“集合框架”,“xml”,“反射”,“Jsp标签”,“通用分页”,“自定义mvc框架”,“MySQL”,“DDL”,“DQL”,“DML”,“DCL”,“TCL”,“视图”,...
  • 自定义MVC框架,原文:https://blog.csdn.net/weixin_43075298/article/details/89359169
  • struts2自定义MVC框架

    2020-09-01 09:57:42
    主要为大家详细介绍了struts2如何自定义MVC框架,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 自定义MVC框架.pdf

    2021-09-30 14:02:11
    自定义MVC框架.pdf
  • 自定义mvc框架

    2018-08-30 20:37:50
    mvc.xml配置 <forward name="ByUser" path="/BooksAction.action?methodName:getIndex" redirect="true"/> </config>
  • Java Web自定义MVC框架详解

    万次阅读 多人点赞 2015-01-16 10:56:01
    但是突然发现百度上能搜索到的靠谱的资料并不是很多,有些只是原理没有代码实现,有些有代码实现但是对于初学者来说理解起来还是比较困难,于是决定把自己讲自定义MVC框架的内容放在这里分享给大家,不仅仅是代码,...

    最近给学生讲Java Web,希望他们能够在学完这部分内容后自己实现一个MVC框架。但是突然发现百度上能搜索到的靠谱的资料并不是很多,有些只是原理没有代码实现,有些有代码实现但是对于初学者来说理解起来还是比较困难,于是决定把自己讲自定义MVC框架的内容放在这里分享给大家,不仅仅是代码,也有原理和探讨。内容会比较长,因为我打算用递增的方式讲解如何写一个自定义MVC框架,重点是前端控制器的开发。

    先说一下什么是前端控制器(font controller)。Java Web中的前端控制器是应用的门面,简单的说所有的请求都会经过这个前端控制器,由前端控制器根据请求的内容来决定如何处理并将处理的结果返回给浏览器。这就好比很多公司都有一个前台,那里通常站着几位面貌姣好的美女,你要到这家公司处理任何的业务或者约见任何人都可以跟她们说,她们会根据你要做什么知会相应的部门或个人来处理,这样做的好处是显而易见的,公司内部系统运作可能很复杂,但是这些对于外部的客户来说应该是透明的,通过前台,客户可以获得他们希望该公司为其提供的服务而不需要了解公司的内部实现。这里说的前台就是公司内部系统的一个门面,它简化了客户的操作。前端控制器的理念就是GoF设计模式门面模式(外观模式)在Web项目中的实际应用。SUN公司为Java Web开发定义了两种模型,Model 1和Model 2。Model 2是基于MVC(Model-View-Controller,模型-视图-控制)架构模式的,通常将小服务(Servlet)或过滤器(Filter)作为控制器,其作用是接受用户请求并获得模型数据然后跳转到视图;将JSP页面作为视图,用来显示用户操作的结果;模型当然是POJO(Plain Old Java Object),它是区别于EJB(Enterprise JavaBean)的普通Java对象,不实现任何其他框架的接口也不扮演其他的角色,而是负责承载数据,可以作为VO(Value Object)或DTO(Data Transfer Object)来使用。当然,如果你对这些概念不熟悉,可以用百度或者维基百科查阅一下,想要深入的了解这些内容推荐阅读大师Martin Fowler的《企业应用架构模式》(英文名:Patterns of Enterprise Application Architecture)。

    接下来我们就来编写一个作为处理用户各种请求门面的前端控制器。

     

    package com.lovo.servlet;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("*.do")
    public class FrontController extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	
    	private static final String DEFAULT_PACKAGE_NAME = "com.lovo.action.";// 默认的Action类的包名前缀
    	private static final String DEFAULT_ACTION_NAME = "Action";// 默认的Action类的类名后缀
    
    	@Override
    	protected void service(HttpServletRequest req, HttpServletResponse resp)
    			throws ServletException, IOException {
    		// 获得请求的小服务路径
    		String servletPath = req.getServletPath();
    		// 从servletPath中去掉开头的斜杠和末尾的.do就是要执行的动作(Action)的名字
    		int start = 1;	// 去掉第一个字符斜杠从第二个字符开始
    		int end = servletPath.lastIndexOf(".do");	// 找到请求路径的后缀.do的位置
    		String actionName = end > start ? servletPath.substring(start, end) + DEFAULT_ACTION_NAME : "";
    		String actionClassName = DEFAULT_PACKAGE_NAME + actionName.substring(0, 1).toUpperCase() + actionName.substring(1);
    		// 接下来可以通过反射来创建Action对象并调用
    		System.out.println(actionClassName);
    	}
    }
    

     

    上面的FrontController类中用@WebServlet注解对该小服务做了映射,只要是后缀为.do的请求,都会经过这个小服务,所以它是一个典型的前端控制器(当然,你也可以在web.xml中使用<servlet>和<servlet-mapping>标签对小服务进行映射,使用注解通常是为了提升开发效率,但需要注意的是注解也是一种耦合,配置文件在解耦合上肯定是更好的选择,如果要使用注解,最好是像Spring 3那样可以基于程序配置应用,此外,使用注解配置Servlet需要你的服务器支持Servlet 3规范)。假设使用Tomcat作为服务器(使用默认设置),项目的部署名称为hw,接下来可以浏览器地址栏输入http://localhost:8080/hw/login.do,Tomcat的控制台会输出com.lovo.action.LoginAction。

    到这里我们已经将请求对应到一个处理该请求的Action类的名字,不要着急,我们马上来解释什么是Action,怎么写Action。我们可以使用不同的Action类来处理用户不同的请求,那么如何在前端控制器中根据不同的请求创建出不同的Action对象呢,相信大家都想到了反射,我们刚才已经得到了Action类的完全限定名(带包名的类名),接下来就可以用反射来创建对象,但是稍等,每个Action要执行的处理是不一样的,怎样才能写一个通用的前端控制器呢?答案是多态!我们可以先定义一个Action接口并定义一个抽象方法,不同的Action子类会对该方法进行重写,这样的话用Action的引用引用不同的Action子类对象,再调用子类重写过的方法,那么就可以执行不同的行为。想到这一层,我们可以继续编写我们的前端控制器。

    首先,我们需要定义Action类的接口。

     

    package com.lovo.action;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 处理用户请求的控制器接口
     * @author 骆昊
     *
     */
    public interface Action {
    
    	public ActionResult execute(HttpServletRequest req, HttpServletResponse resp) 
    			throws ServletException, IOException;
    }
    

    接口中的execute方法是处理用户请求的方法,所以它的两个参数分别是HttpServletRequest和HttpServletResponse对象,到时候我们会在前端控制中通过反射创建Action,并调用execute方法,由于不同的Action子类通过重写对execute方法给出了不同的实现版本,因此该方法是一个多态方法。execute方法的返回值是一个ActionResult对象,它的实现代码如下所示。

     

     

    package com.lovo.action;
    
    /**
     * Action执行结果
     * @author 骆昊
     *
     */
    public class ActionResult {
    	private ResultContent resultContent;
    	private ResultType resultType;
    
    	public ActionResult(ResultContent resultContent) {
    		this(resultContent, ResultType.Forward);
    	}
    
    	public ActionResult(ResultContent resultContent, ResultType type) {
    		this.resultContent = resultContent;
    		this.resultType = type;
    	}
    
    	/**
    	 * 获得执行结果的内容
    	 */
    	public ResultContent getResultContent() {
    		return resultContent;
    	}
    	
    	/**
    	 * 获得执行结果的类型
    	 */
    	public ResultType getResultType() {
    		return resultType;
    	}
    
    }

     

    ActionResult类中的ResultContent代表了Action对用户请求进行处理后得到的内容,它可以存储一个字符串表示要跳转或重定向到的资源的URL,它也可以存储一个对象来保存对用户请求进行处理后得到的数据(模型),为了支持Ajax操作,我们可以将此对象处理成JSON格式的字符串。

     

     

    package com.lovo.action;
    
    import com.google.gson.Gson;
    
    /**
     * Action执行结束产生的内容
     * @author 骆昊
     *
     */
    public class ResultContent {
    	private String url;
    	private Object obj;
    	
    	public ResultContent(String url) {
    		this.url = url;
    	}
    	
    	public ResultContent(Object obj) {
    		this.obj = obj;
    	}
    	
    	public String getUrl() {
    		return url;
    	}
    	
    	public String getJson() {
    		return new Gson().toJson(obj);// 这里使用了Google的JSON工具类gson
    	}
    }
    

     

    ActionResult类中的ResultType代表了对用户请求处理后如何向浏览器产生响应,它是一个枚举类型,代码如下所示。

     

    package com.lovo.action;
    
    /**
     * Action执行结果类型
     * @author 骆昊
     *
     */
    public enum ResultType {
    	/**
    	 * 重定向
    	 */
    	Redirect, 
    	/**
    	 * 转发
    	 */
    	Forward, 
    	/**
    	 * 异步请求
    	 */
    	Ajax,
    	/**
    	 * 数据流
    	 */
    	Stream,
    	/**
    	 * 跳转到向下一个控制器
    	 */
    	Chain,
    	/**
    	 * 重定向到下一个控制器
    	 */
    	RedirectChain
    }
     

     

    稍等,我们还需要一个工具类来封装常用的工具方法。

     

    package com.lovo.util;
    
    import java.awt.Color;
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     * 通用工具类
     * @author 骆昊
     *
     */
    public final class CommonUtil {
    	private static final List<String> patterns = new ArrayList<>();
    	private static final List<TypeConverter> converters = new ArrayList<>();
    	
    	static {
    		patterns.add("yyyy-MM-dd");
    		patterns.add("yyyy-MM-dd HH:mm:ss");
    	}
    
    	private CommonUtil() {
    		throw new AssertionError();
    	}
    
    	/**
    	 * 将字符串的首字母大写
    	 */
    	public static String capitalize(String str) {
    		StringBuilder sb = new StringBuilder();
    		if (str != null && str.length() > 0) {
    			sb.append(str.substring(0, 1).toUpperCase());
    			if (str.length() > 1) {
    				sb.append(str.substring(1));
    			}
    			return sb.toString();
    		}
    		return str;
    	}
    	
    	/**
    	 * 生成随机颜色
    	 */
    	public static Color getRandomColor() {
    		int r = (int) (Math.random() * 256);
    		int g = (int) (Math.random() * 256);
    		int b = (int) (Math.random() * 256);
    		return new Color(r, g, b);
    	}
    	
    	/**
    	 * 添加时间日期样式
    	 * @param pattern 时间日期样式
    	 */
    	public static void registerDateTimePattern(String pattern) {
    		patterns.add(pattern);
    	}
    
    	/**
    	* 取消时间日期样式
    	* @param pattern 时间日期样式
    	*/
    	public static void unRegisterDateTimePattern(String pattern) {
    		patterns.remove(pattern);
    	}
    	
    	/**
    	 * 添加类型转换器
    	 * @param converter 类型转换器对象
    	 */
    	public static void registerTypeConverter(TypeConverter converter) {
    		converters.add(converter);
    	}
    
    	/**
    	 * 取消类型转换器
    	 * @param converter 类型转换器对象
    	 */
    	public static void unRegisterTypeConverter(TypeConverter converter) {
    		converters.remove(converter);
    	}
    	
    	/**
    	 * 将字符串转换成时间日期类型
    	 * @param str 时间日期字符串 
    	 */
    	public static Date convertStringToDateTime(String str) {
    		if (str != null) {
    	        for (String pattern : patterns) {
    	            Date date = tryConvertStringToDate(str, pattern);
    	
    	            if (date != null) {
    	                return date;
    	            }
    	        }
    		}
    
            return null;
    	}
    	
    	/**
    	 * 按照指定样式将时间日期转换成字符串
    	 * @param date 时间日期对象
    	 * @param pattern 样式字符串
    	 * @return 时间日期的字符串形式
    	 */
    	public static String convertDateTimeToString(Date date, String pattern) {
    		 return new SimpleDateFormat(pattern).format(date);
    	}
    	
    	private static Date tryConvertStringToDate(String str, String pattern) {
    		 DateFormat dateFormat = new SimpleDateFormat(pattern); 
    		 dateFormat.setLenient(false);	// 不允许将不符合样式的字符串转换成时间日期
    
    		 try {
    			 return dateFormat.parse(str);
    		 } 
    		 catch (ParseException ex) {
    		 }
    		 
    		 return null;
    	}
    	
    	/**
    	 * 将字符串值按指定的类型转换成转换成对象
    	 * @param elemType 类型
    	 * @param value 字符串值
    	 */
    	public static Object changeStringToObject(Class<?> elemType, String value) {
    		Object tempObj = null;
    		
    		if(elemType == byte.class || elemType == Byte.class) {
    			tempObj = Byte.parseByte(value);
    		}
    		else if(elemType == short.class || elemType == Short.class) {
    			tempObj = Short.parseShort(value);
    		}
    		else if(elemType == int.class || elemType == Integer.class) {
    			tempObj = Integer.parseInt(value);
    		}
    		else if(elemType == long.class || elemType == Long.class) {
    			tempObj = Long.parseLong(value);
    		}
    		else if(elemType == double.class || elemType == Double.class) {
    			tempObj = Double.parseDouble(value);
    		}
    		else if(elemType == float.class || elemType == Float.class) {
    			tempObj = Float.parseFloat(value);
    		}
    		else if(elemType == boolean.class || elemType == Boolean.class) {
    			tempObj = Boolean.parseBoolean(value);
    		}
    		else if(elemType == java.util.Date.class) {
    			tempObj = convertStringToDateTime(value);
    		}
    		else if(elemType == java.lang.String.class) {
    			tempObj = value;
    		}
    		else {
    			for(TypeConverter converter : converters) {
    				try {
    					tempObj = converter.convert(elemType, value);
    					if(tempObj != null) {
    						return tempObj;
    					}
    				} 
    				catch (Exception e) {
    				}
    			}
    		}
    		
    		return tempObj;
    	}
    
    	/**
    	 * 获取文件后缀名
    	 * @param filename 文件名
    	 * @return 文件的后缀名以.开头
    	 */
    	public static String getFileSuffix(String filename) {
    		int index = filename.lastIndexOf(".");
    		return index > 0 ? filename.substring(index) : "";
    	}
    	
    }
    

     


    定义好Action接口及其相关类后,我们可以继续改写写前端控制器的代码,如下所示。

     

    package com.lovo.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    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.lovo.action.Action;
    import com.lovo.action.ActionResult;
    import com.lovo.action.ResultContent;
    import com.lovo.action.ResultType;
    
    @WebServlet("*.do")
    public class FrontController extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	
    	private static final String DEFAULT_PACKAGE_NAME = "com.lovo.action.";		// 默认的Action类的包名前缀
    	private static final String DEFAULT_ACTION_NAME = "Action";			// 默认的Action类的类名后缀
    	private static final String DEFAULT_JSP_PATH = "/WEB-INF/jsp";			// 默认的JSP文件的路径
    
    	@Override
    	protected void service(HttpServletRequest req, HttpServletResponse resp)
    			throws ServletException, IOException {
    		String contextPath = req.getContextPath() + "/";
    		// 获得请求的小服务路径
    		String servletPath = req.getServletPath();
    		// 从servletPath中去掉开头的斜杠和末尾的.do就是要执行的动作(Action)的名字
    		int start = 1;	// 去掉第一个字符斜杠从第二个字符开始
    		int end = servletPath.lastIndexOf(".do");	// 找到请求路径的后缀.do的位置
    		String actionName = end > start ? servletPath.substring(start, end) + DEFAULT_ACTION_NAME : "";
    		String actionClassName = DEFAULT_PACKAGE_NAME + actionName.substring(0, 1).toUpperCase() + actionName.substring(1);
    		try {
    			// 通过反射来创建Action对象并调用
    			Action action = (Action) Class.forName(actionClassName).newInstance();
    			// 执行多态方法execute得到ActionResult
    			ActionResult result = action.execute(req, resp);
    			ResultType resultType = result.getResultType();// 结果类型
    			ResultContent resultContent = result.getResultContent();// 结果内容
    			// 根据ResultType决定如何处理
    			switch (resultType) {
    			case Forward: // 跳转
    				req.getRequestDispatcher(
    						DEFAULT_JSP_PATH + resultContent.getUrl()).forward(req,
    						resp);
    				break;
    			case Redirect: // 重定向
    				resp.sendRedirect(resultContent.getUrl());
    				break;
    			case Ajax: // Ajax
    				PrintWriter pw = resp.getWriter();
    				pw.println(resultContent.getJson());
    				pw.close();
    				break;
    			case Chain:
    				req.getRequestDispatcher(contextPath + resultContent.getUrl())
    						.forward(req, resp);
    				break;
    			case RedirectChain:
    				resp.sendRedirect(contextPath + resultContent.getUrl());
    				break;
    			default:
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    			throw new ServletException(e);
    		}
    	}
    }
    

     

    迄今为止,我们还没有编写任何的配置文件,但是大家可能已经注意到前端控制器中的硬代码(hard code)了。我们在前端控制器中设置的几个常量(默认的Action类的包名前缀、默认的Action类的类名后缀以及默认的JSP文件的路径)都算是硬代码,但是我们也可以将其视为一种约定,我们约定好Action类的名字和路径,JSP页面的名字和路径就可以省去很多的配置,甚至可以做到零配置,这种理念并不新鲜,它叫做约定优于配置(CoC,Convenient over Configuration)。当然,对于符合约定的部分我们可以省去配置,对于不合符约定的部分可以用配置文件或者注解加以说明。继续修改我们的前端控制器,代码如下所示。

     

    package com.lovo.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.MultipartConfig;
    import javax.servlet.annotation.WebInitParam;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.lovo.action.Action;
    import com.lovo.action.ActionResult;
    import com.lovo.action.ResultContent;
    import com.lovo.util.CommonUtil;
    
    /**
     * 前端控制器(门面模式[提供用户请求的门面])
     * @author 骆昊
     *
     */
    @WebServlet(urlPatterns = { "*.do" }, loadOnStartup = 0, 
    		initParams = { 
    			@WebInitParam(name = "packagePrefix", value = "com.lovo.action."),
    			@WebInitParam(name = "jspPrefix", value = "/WEB-INF/jsp/"),
    			@WebInitParam(name = "actionSuffix", value = "Action")
    		}
    )
    @MultipartConfig
    public class FrontController extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	
    	private static final String DEFAULT_PACKAGE_NAME = "com.lovo.action.";
    	private static final String DEFAULT_JSP_PATH = "/WEB-INF/content/";
    	private static final String DEFAULT_ACTION_NAME = "Action";
    	
    	private String packagePrefix = null;		// 包名的前缀
    	private String jspPrefix = null;			// JSP页面路径的前缀
    	private String actionSuffix = null;			// Action类名的后缀
    	
    	@Override
    	public void init(ServletConfig config) throws ServletException {
    		String initParam = config.getInitParameter("packagePrefix");
    		packagePrefix = initParam != null ? initParam :  DEFAULT_PACKAGE_NAME;
    		initParam = config.getInitParameter("jspPrefix");
    		jspPrefix = initParam != null ? initParam : DEFAULT_JSP_PATH;
    		initParam = config.getInitParameter("actionSuffix");
    		actionSuffix = initParam != null ? initParam : DEFAULT_ACTION_NAME;
    	}
    
    	@Override
    	protected void service(HttpServletRequest req, HttpServletResponse resp)
    			throws ServletException, IOException {
    		String contextPath = req.getContextPath() + "/";
    		String servletPath = req.getServletPath();
    		
    		try {
    			Action action = (Action) Class.forName(getFullActionName(servletPath)).newInstance();
    			ActionResult actionResult = action.execute(req, resp);
    			ResultContent resultContent = actionResult.getResultContent();
    			switch(actionResult.getResultType()) {
    			case Redirect:
    				resp.sendRedirect(contextPath + resultContent.getUrl());
    				break;
    			case Forward:
    				req.getRequestDispatcher(getFullJspPath(servletPath) + resultContent.getUrl())
    						.forward(req, resp);
    				break;
    			case Ajax:
    				PrintWriter pw = resp.getWriter();
    				pw.println(resultContent.getJson());
    				pw.close();
    				break;
    			case Chain:
    				req.getRequestDispatcher(contextPath + resultContent.getUrl())
    						.forward(req, resp);
    				break;
    			case RedirectChain:
    				resp.sendRedirect(contextPath + resultContent.getUrl());
    				break;
    			default:
    			}
    		} 
    		catch (Exception e) {
    			e.printStackTrace();
    			resp.sendRedirect("error.html");
    		}
    	}
    	
    	// 根据请求的小服务路径获得对应的Action类的名字
    	private String getFullActionName(String servletPath) {
    		int start = servletPath.lastIndexOf("/") + 1;
    		int end = servletPath.lastIndexOf(".do");
    		return packagePrefix + getSubPackage(servletPath) + CommonUtil.capitalize(servletPath.substring(start, end)) + actionSuffix;
    	}
    	
    	// 根据请求的小服务路径获得对应的完整的JSP页面路径
    	private String getFullJspPath(String servletPath) {
    		return jspPrefix + getSubJspPath(servletPath);
    	}
    	
    	// 根据请求的小服务路径获得子级包名
    	private String getSubPackage(String servletPath) {
    		return getSubJspPath(servletPath).replaceAll("\\/", ".");
    	}
    	
    	// 根据请求的小服务路径获得JSP页面的子级路径
    	private String getSubJspPath(String servletPath) {
    		int start = 1;
    		int end = servletPath.lastIndexOf("/");
    		return end > start ? servletPath.substring(start, end > 0 ? end + 1 : 0) : "";
    	}
    	
    }

     

     

    这一次,我们让前端控制器在解析用户请求的小服务路径时,将请求路径和Action类的包以及JSP页面的路径对应起来,也就是说,如果用户请求的小服务路径是/user/order/save.do,那么对应的Action类的完全限定名就是com.lovo.action.user.order.SaveAction,如果需要跳转到ok.jsp页面,那么JSP页面的默认路径是/WEB-INF/jsp/user/order/ok.jsp。这样做才能满足对项目模块进行划分的要求,而不是把所有的Action类都放在一个包中,把所有的JSP页面都放在一个路径下。

     

    然而,前端控制器的任务到这里还远远没有完成,如果每个Action都要写若干的req.getParameter(String)从请求中获得请求参数再组装对象而后调用业务逻辑层的代码,这样Action实现类中就会有很多重复的样板代码,代码有很多种坏味道,重复是最坏的一种!解决这一问题的方案仍然是反射,通过反射我们可以将Action需要的参数注入到Action类中。需要注意的是,反射虽然可以帮助我们写出通用性很强的代码,但是反射的开销也是不可忽视的,我们的自定义MVC框架还有很多可以优化的地方,不过先放放,先解决请求参数的注入问题。

    先封装一个反射的工具类,代码如下所示。

     

    package com.lovo.util;
    
    public interface TypeConverter {
    
    	public Object convert(Class<?> elemType, String value) throws Exception;
    }

     

     

     

    package com.lovo.util;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 反射工具类
     * @author 骆昊
     *
     */
    public class ReflectionUtil {
    
    	private ReflectionUtil() {
    		throw new AssertionError();
    	}
    	
    	/**
    	 * 根据字段名查找字段的类型
    	 * @param target 目标对象
    	 * @param fieldName 字段名
    	 * @return 字段的类型
    	 */
    	public static Class<?> getFieldType(Object target, String fieldName) {
    		Class<?> clazz = target.getClass();
    		String[] fs = fieldName.split("\\.");
    		
    		try {
    			for(int i = 0; i < fs.length - 1; i++) {
    				Field f = clazz.getDeclaredField(fs[i]);
    				target = f.getType().newInstance();
    				clazz = target.getClass();
    			}
    			return clazz.getDeclaredField(fs[fs.length - 1]).getType();
    		}
    		catch(Exception e) {
    		        // throw new RuntimeException(e);
    		}
    		return null;
    	}
    	
    	/**
    	 * 获取对象所有字段的名字
    	 * @param obj 目标对象
    	 * @return 字段名字的数组
    	 */
    	public static String[] getFieldNames(Object obj) {
    		Class<?> clazz = obj.getClass();
    		Field[] fields = clazz.getDeclaredFields();
    		List<String> fieldNames = new ArrayList<>();
    		for(int i = 0; i < fields.length; i++) {
    			if((fields[i].getModifiers() & Modifier.STATIC) == 0) {
    				fieldNames.add(fields[i].getName());
    			}
    		}
    		return fieldNames.toArray(new String[fieldNames.size()]);
    	}
    
    	/**
    	 * 通过反射取对象指定字段(属性)的值
    	 * @param target 目标对象
    	 * @param fieldName 字段的名字
    	 * @throws 如果取不到对象指定字段的值则抛出异常
    	 * @return 字段的值
    	 */
    	public static Object getValue(Object target, String fieldName) {
    		Class<?> clazz = target.getClass();
    		String[] fs = fieldName.split("\\.");
    		
    		try {
    			for(int i = 0; i < fs.length - 1; i++) {
    				Field f = clazz.getDeclaredField(fs[i]);
    				f.setAccessible(true);
    				target = f.get(target);
    				clazz = target.getClass();
    			}
    		
    			Field f = clazz.getDeclaredField(fs[fs.length - 1]);
    			f.setAccessible(true);
    			return f.get(target);
    		}
    		catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    	}
    	
    	/**
    	 * 通过反射给对象的指定字段赋值
    	 * @param target 目标对象
    	 * @param fieldName 字段的名称
    	 * @param value 值
    	 */
    	public static void setValue(Object target, String fieldName, Object value) {
    		Class<?> clazz = target.getClass();
    		String[] fs = fieldName.split("\\.");
    		try {
    			for(int i = 0; i < fs.length - 1; i++) {
    				Field f = clazz.getDeclaredField(fs[i]);
    				f.setAccessible(true);
    				Object val = f.get(target);
    				if(val == null) {
    					Constructor<?> c = f.getType().getDeclaredConstructor();
    					c.setAccessible(true);
    					val = c.newInstance();
    					f.set(target, val);
    				}
    				target = val;
    				clazz = target.getClass();
    			}
    		
    			Field f = clazz.getDeclaredField(fs[fs.length - 1]);
    			f.setAccessible(true);
    			f.set(target, value);
    		}
    		catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    	}
    	
    }
    

     

    这个工具类中封装了四个方法,通过这个工具类可以给对象的指定字段赋值,也可以获取对象指定字段的值和类型,对于对象的某个字段又是一个对象的情况,上面的工具类也能够提供很好的处理,例如person对象关联了car对象,car对象关联了producer对象,producer对象有name属性,可以用ReflectionUtil.get(person, "car.producer.name")来获取name属性的值。有了这个工具类,我们可以继续改写前端控制器了,代码如下所示。

     

    package com.lovo.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.lang.reflect.Array;
    import java.util.Enumeration;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.MultipartConfig;
    import javax.servlet.annotation.WebInitParam;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.lovo.action.Action;
    import com.lovo.action.ActionResult;
    import com.lovo.action.ResultContent;
    import com.lovo.util.CommonUtil;
    import com.lovo.util.ReflectionUtil;
    
    /**
     * 前端控制器(门面模式[提供用户请求的门面])
     * @author 骆昊
     *
     */
    @WebServlet(urlPatterns = { "*.do" }, loadOnStartup = 0, 
    		initParams = { 
    			@WebInitParam(name = "packagePrefix", value = "com.lovo.action."),
    			@WebInitParam(name = "jspPrefix", value = "/WEB-INF/jsp/"),
    			@WebInitParam(name = "actionSuffix", value = "Action")
    		}
    )
    @MultipartConfig
    public class FrontController extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	
    	private static final String DEFAULT_PACKAGE_NAME = "com.lovo.action.";
    	private static final String DEFAULT_JSP_PATH = "/WEB-INF/content/";
    	private static final String DEFAULT_ACTION_NAME = "Action";
    	
    	private String packagePrefix = null;		// 包名的前缀
    	private String jspPrefix = null;			// JSP页面路径的前缀
    	private String actionSuffix = null;			// Action类名的后缀
    	
    	@Override
    	public void init(ServletConfig config) throws ServletException {
    		String initParam = config.getInitParameter("packagePrefix");
    		packagePrefix = initParam != null ? initParam :  DEFAULT_PACKAGE_NAME;
    		initParam = config.getInitParameter("jspPrefix");
    		jspPrefix = initParam != null ? initParam : DEFAULT_JSP_PATH;
    		initParam = config.getInitParameter("actionSuffix");
    		actionSuffix = initParam != null ? initParam : DEFAULT_ACTION_NAME;
    	}
    
    	@Override
    	protected void service(HttpServletRequest req, HttpServletResponse resp)
    			throws ServletException, IOException {
    		String contextPath = req.getContextPath() + "/";
    		String servletPath = req.getServletPath();
    		try {
    			Action action = (Action) Class.forName(getFullActionName(servletPath)).newInstance();
    			injectProperties(action, req);// 向Action对象中注入请求参数
    			ActionResult actionResult = action.execute(req, resp);
    			ResultContent resultContent = actionResult.getResultContent();
    			switch (actionResult.getResultType()) {
    			case Redirect:
    				resp.sendRedirect(contextPath + resultContent.getUrl());
    				break;
    			case Forward:
    				req.getRequestDispatcher(
    						getFullJspPath(servletPath) + resultContent.getUrl())
    						.forward(req, resp);
    				break;
    			case Ajax:
    				PrintWriter pw = resp.getWriter();
    				pw.println(resultContent.getJson());
    				pw.close();
    				break;
    			case Chain:
    				req.getRequestDispatcher(contextPath + resultContent.getUrl())
    						.forward(req, resp);
    				break;
    			case RedirectChain:
    				resp.sendRedirect(contextPath + resultContent.getUrl());
    				break;
    			default:
    			}
    		}
    		catch (Exception e) {
    			e.printStackTrace();
    			resp.sendRedirect("error.html");
    		}
    	}
    	
    	// 根据请求的小服务路径获得对应的Action类的名字
    	private String getFullActionName(String servletPath) {
    		int start = servletPath.lastIndexOf("/") + 1;
    		int end = servletPath.lastIndexOf(".do");
    		return packagePrefix + getSubPackage(servletPath) + CommonUtil.capitalize(servletPath.substring(start, end)) + actionSuffix;
    	}
    	
    	// 根据请求的小服务路径获得对应的完整的JSP页面路径
    	private String getFullJspPath(String servletPath) {
    		return jspPrefix + getSubJspPath(servletPath);
    	}
    	
    	// 根据请求的小服务路径获得子级包名
    	private String getSubPackage(String servletPath) {
    		return getSubJspPath(servletPath).replaceAll("\\/", ".");
    	}
    	
    	// 根据请求的小服务路径获得JSP页面的子级路径
    	private String getSubJspPath(String servletPath) {
    		int start = 1;
    		int end = servletPath.lastIndexOf("/");
    		return end > start ? servletPath.substring(start, end > 0 ? end + 1 : 0) : "";
    	}
    
    	// 向Action对象中注入属性
    	private void injectProperties(Action action, HttpServletRequest req) throws Exception {
    		Enumeration<String> paramNamesEnum =  req.getParameterNames();
    		while(paramNamesEnum.hasMoreElements()) {
    			String paramName = paramNamesEnum.nextElement();
    			Class<?> fieldType = ReflectionUtil.getFieldType(action, paramName.replaceAll("\\[|\\]", ""));
    			if(fieldType != null) {
    				Object paramValue = null;
    				if(fieldType.isArray()) {	// 如果属性是数组类型
    					Class<?> elemType = fieldType.getComponentType();	// 获得数组元素类型
    					String[] values = req.getParameterValues(paramName);
    					paramValue = Array.newInstance(elemType, values.length);	// 通过反射创建数组对象
    					for(int i = 0; i < values.length; i++) {
    						Object tempObj = CommonUtil.changeStringToObject(elemType, values[i]);
    						Array.set(paramValue, i, tempObj);
    					}
    				}
    				else {	// 非数组类型的属性
    					paramValue = CommonUtil.changeStringToObject(fieldType, req.getParameter(paramName));
    				}
    				ReflectionUtil.setValue(action, paramName.replaceAll("\\[|\\]", ""), paramValue);
    			}
    		}
    	}
    }

     

     

     

    到这里,我们的前端控制器还不能够支持文件上传。Java Web应用的文件上传在Servlet 3.0规范以前一直是个让人闹心的东西,需要自己编写代码在Servlet中通过解析输入流来找到上传文件的数据,虽然有第三方工具(如commons-fileupload)经封装了这些操作,但是一个Web规范中居然没有文件上传的API难道不是很搞笑吗?好在Servlet 3.0中有了@MultiConfig注解可以为Servlet提供文件上传的支持,而且通过请求对象的getPart或getParts方法可以获得上传的数据,这样处理文件上传就相当方便了。

    我们先定义一个接口来让Action支持文件上传,凡是要处理文件上传的Action类都要实现这个接口,然后我们通过接口注入的方式,将上传文件的数据以及上传文件的文件名注入到Action类中,这样Action类中就可以直接处理上传的文件了。

    支持文件上传的接口代码如下所示。

     

    package com.lovo.action;
    
    import javax.servlet.http.Part;
    
    /**
     * 支持文件上传的接口
     * @author 骆昊
     *
     */
    public interface Uploadable {
    	
    	/**
    	 * 设置文件名
    	 * @param filenames 文件名的数组
    	 */
    	public void setFilenames(String[] filenames);
    	
    	/**
    	 * 设置上传的附件
    	 * @param parts 附件的数组
    	 */
    	public void setParts(Part[] parts);
    	
    }
    

     

    修改后的前端控制器

     

    package com.lovo.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.lang.reflect.Array;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.List;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.MultipartConfig;
    import javax.servlet.annotation.WebInitParam;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.Part;
    
    import com.lovo.action.Action;
    import com.lovo.action.ActionResult;
    import com.lovo.action.ResultContent;
    import com.lovo.action.ResultType;
    import com.lovo.action.Uploadable;
    import com.lovo.util.CommonUtil;
    import com.lovo.util.ReflectionUtil;
    
    /**
     * 前端控制器(门面模式[提供用户请求的门面])
     * @author 骆昊
     *
     */
    @WebServlet(urlPatterns = { "*.do" }, loadOnStartup = 0, 
    		initParams = { 
    			@WebInitParam(name = "packagePrefix", value = "com.lovo.action."),
    			@WebInitParam(name = "jspPrefix", value = "/WEB-INF/jsp/"),
    			@WebInitParam(name = "actionSuffix", value = "Action")
    		}
    )
    @MultipartConfig
    public class FrontController extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	
    	private static final String DEFAULT_PACKAGE_NAME = "com.lovo.action.";
    	private static final String DEFAULT_JSP_PATH = "/WEB-INF/content/";
    	private static final String DEFAULT_ACTION_NAME = "Action";
    	
    	private String packagePrefix = null;		// 包名的前缀
    	private String jspPrefix = null;			// JSP页面路径的前缀
    	private String actionSuffix = null;			// Action类名的后缀
    	
    	@Override
    	public void init(ServletConfig config) throws ServletException {
    		String initParam = config.getInitParameter("packagePrefix");
    		packagePrefix = initParam != null ? initParam :  DEFAULT_PACKAGE_NAME;
    		initParam = config.getInitParameter("jspPrefix");
    		jspPrefix = initParam != null ? initParam : DEFAULT_JSP_PATH;
    		initParam = config.getInitParameter("actionSuffix");
    		actionSuffix = initParam != null ? initParam : DEFAULT_ACTION_NAME;
    	}
    
    	@Override
    	protected void service(HttpServletRequest req, HttpServletResponse resp)
    			throws ServletException, IOException {
    		String contextPath = req.getContextPath() + "/";
    		String servletPath = req.getServletPath();
    		
    		try {
    			Action action = (Action) Class.forName(getFullActionName(servletPath)).newInstance();
    			try {
    				injectProperties(action, req);
    			} catch (Exception e) {
    			}
    			if(action instanceof Uploadable) {	// 通过接口向实现了接口的类注入属性(接口注入)
    				List<Part> fileparts = new ArrayList<>();
    				List<String> filenames = new ArrayList<>();
    				for(Part part : req.getParts()) {
    					String cd = part.getHeader("Content-Disposition");
    					if(cd.indexOf("filename") >= 0) {
    						fileparts.add(part);
    						filenames.add(cd.substring(cd.lastIndexOf("=") + 1).replaceAll("\\\"", ""));
    					}
    				}
    				((Uploadable) action).setParts(fileparts.toArray(new Part[fileparts.size()]));
    				((Uploadable) action).setFilenames(filenames.toArray(new String[filenames.size()]));
    			}
    			ActionResult actionResult = action.execute(req, resp);
    			if(actionResult != null) {
    				ResultContent resultContent = actionResult.getResultContent();
    				ResultType resultType = actionResult.getResultType();
    				switch(resultType) {
    				case Redirect:
    					resp.sendRedirect(contextPath + resultContent.getUrl());
    					break;
    				case Forward:
    					req.getRequestDispatcher(getFullJspPath(servletPath) + resultContent.getUrl()).forward(req, resp);
    					break;
    				case Ajax:
    					PrintWriter pw = resp.getWriter();
    					pw.println(resultContent.getJson());
    					pw.close();
    					break;
    				case Chain:
    					req.getRequestDispatcher(contextPath + resultContent.getUrl()).forward(req, resp);
    					break;
    				case RedirectChain:
    					resp.sendRedirect(contextPath + resultContent.getUrl());
    					break;
    				default:
    				}
    			}
    		} 
    		catch (Exception e) {
    			e.printStackTrace();
    			resp.sendRedirect("error.html");
    		}
    	}
    	
    	// 根据请求的小服务路径获得对应的Action类的名字
    	private String getFullActionName(String servletPath) {
    		int start = servletPath.lastIndexOf("/") + 1;
    		int end = servletPath.lastIndexOf(".do");
    		return packagePrefix + getSubPackage(servletPath) + CommonUtil.capitalize(servletPath.substring(start, end)) + actionSuffix;
    	}
    	
    	// 根据请求的小服务路径获得对应的完整的JSP页面路径
    	private String getFullJspPath(String servletPath) {
    		return jspPrefix + getSubJspPath(servletPath);
    	}
    	
    	// 根据请求的小服务路径获得子级包名
    	private String getSubPackage(String servletPath) {
    		return getSubJspPath(servletPath).replaceAll("\\/", ".");
    	}
    	
    	// 根据请求的小服务路径获得JSP页面的子级路径
    	private String getSubJspPath(String servletPath) {
    		int start = 1;
    		int end = servletPath.lastIndexOf("/");
    		return end > start ? servletPath.substring(start, end > 0 ? end + 1 : 0) : "";
    	}
    
    	// 向Action对象中注入属性
    	private void injectProperties(Action action, HttpServletRequest req) throws Exception {
    		Enumeration<String> paramNamesEnum =  req.getParameterNames();
    		while(paramNamesEnum.hasMoreElements()) {
    			String paramName = paramNamesEnum.nextElement();
    			Class<?> fieldType = ReflectionUtil.getFieldType(action, paramName.replaceAll("\\[|\\]", ""));
    			if(fieldType != null) {
    				Object paramValue = null;
    				if(fieldType.isArray()) {	// 如果属性是数组类型
    					Class<?> elemType = fieldType.getComponentType();	// 获得数组元素类型
    					String[] values = req.getParameterValues(paramName);
    					paramValue = Array.newInstance(elemType, values.length);	// 通过反射创建数组对象
    					for(int i = 0; i < values.length; i++) {
    						Object tempObj = CommonUtil.changeStringToObject(elemType, values[i]);
    						Array.set(paramValue, i, tempObj);
    					}
    				}
    				else {	// 非数组类型的属性
    					paramValue = CommonUtil.changeStringToObject(fieldType, req.getParameter(paramName));
    				}
    				ReflectionUtil.setValue(action, paramName.replaceAll("\\[|\\]", ""), paramValue);
    			}
    		}
    	}
    }
    

     

    到这里,我们的前端控制器已经基本可用了,接下来用我们自定义的MVC框架做一个小应用“班级学生管理系统”。由于要进行数据库操作,我们可以对操作数据库的JDBC代码进行一个简单的封装并引入DAO(数据访问对象)模式。DAO(Data Access Object)顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作。在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中。用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储。DAO模式实际上包含了两个模式,一是Data Accessor(数据访问器),二是Data Object(数据对象),前者要解决如何访问数据的问题,而后者要解决的是如何用对象封装数据。

    数据库资源管理器的代码如下所示。

     

    package com.lovo.util;
    
    import java.sql.Connection;
    import java.sql.Driver;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    /**
     * 数据库资源管理器
     * @author 骆昊
     *
     */
    public class DbResourceManager {
    	// 最好的做法是将配置保存到配置文件中(可以用properteis文件或XML文件)
    	private static final String JDBC_DRV = "com.mysql.jdbc.Driver";
    	private static final String JDBC_URL = "jdbc:mysql://localhost:3306/hw";
    	private static final String JDBC_UID = "root";
    	private static final String JDBC_PWD = "123456";
    	
    	private static Driver driver = null;
    	private static Properties info = new Properties();
    	
    	private DbResourceManager() {
    		throw new AssertionError();
    	}
    
    	static {	
    		try {
    			loadDriver();	// 通过静态代码块加载数据库驱动
    			info.setProperty("user", JDBC_UID);
    			info.setProperty("password", JDBC_PWD);
    		}
    		catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    	}
    	
    	public static void setDriver(Driver _driver) {
    		driver = _driver;
    	}
    	
    	// 加载驱动程序
    	private static void loadDriver() throws Exception {
    		driver = (Driver) Class.forName(JDBC_DRV).newInstance();
    		DriverManager.registerDriver(driver);
    	}
    	
    	/**
    	 * 打开连接
    	 * @return 连接对象
    	 * @throws Exception 无法加载驱动或无法建立连接时将抛出异常
    	 */
    	public static Connection getConnection() throws Exception {
    		if(driver == null) {
    			loadDriver();
    		}
    		return driver.connect(JDBC_URL, info);
    	}
    	
    	/**
    	 * 关闭游标
    	 */
    	public static void close(ResultSet rs) {
    		try {
    			if(rs != null && !rs.isClosed()) {
    				rs.close();
    			}
    		}
    		catch (SQLException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 关闭语句
    	 */
    	public static void close(Statement stmt) throws SQLException {
    		try {
    			if(stmt != null && !stmt.isClosed()) {
    				stmt.close();
    			}
    		}
    		catch (SQLException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 关闭连接
    	 */
    	public static void close(Connection con) {
    		try {
    			if(con != null && !con.isClosed()) {
    				con.close();
    			}
    		}
    		catch (SQLException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 注销驱动
    	 * @throws SQLException
    	 */
    	public static void unloadDriver() throws SQLException {
    		if(driver != null) {
    			DriverManager.deregisterDriver(driver);
    			driver = null;
    		}
    	}
    	
    }
    


    数据库会话的代码如下所示,封装了执行查询和执行增删改的方法以减少重复代码。

     

    package com.lovo.util;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.io.Serializable;
    
    import com.lovo.exception.DbSessionException;
    
    /**
     * 数据库会话(尚未提供批处理操作)
     * @author 骆昊
     *
     */
    public class DbSession {
    	private Connection con = null;
    	private PreparedStatement stmt = null;
    	private ResultSet rs = null;
    	
    	/**
    	 * 开启数据库会话
    	 */
    	public void open() {
    		if(con == null) {
    			try {
    				con = DbResourceManager.getConnection();
    			}
    			catch (Exception e) {
    				throw new DbSessionException("创建会话失败", e);
    			}
    		}
    	}
    	
    	/**
    	 * 获得与数据库会话绑定的连接
    	 */
    	public Connection getConnection() {
    		return con;
    	}
    	
    	/**
    	 * 关闭数据库会话
    	 */
    	public void close() {
    		try {
    			DbResourceManager.close(rs);
    			rs = null;
    			DbResourceManager.close(stmt);
    			stmt = null;
    			DbResourceManager.close(con);
    			con = null;
    		}
    		catch (SQLException e) {
    			throw new DbSessionException("关闭会话失败", e);
    		}
    	}
    	
    	/**
    	 * 开启事务
    	 * @throws 无法开启事务时将抛出异常
    	 */
    	public void beginTx() {
    		try {
    			if(con != null && !con.isClosed()) {
    				con.setAutoCommit(false);
    			}
    		}
    		catch (SQLException e) {
    			throw new RuntimeException("开启事务失败", e);
    		}
    	}
    	
    	/**
    	 * 提交事务
    	 * @throws 无法提交事务时将抛出异常
    	 */
    	public void commitTx() {
    		try {
    			if(con != null && !con.isClosed()) {
    				con.commit();
    			}
    		}
    		catch (SQLException e) {
    			throw new DbSessionException("提交事务失败", e);
    		}
    	}
    	
    	/**
    	 * 回滚事务
    	 * @throws 无法回滚事务时将抛出异常
    	 */
    	public void rollbackTx() {
    		try {
    			if(con != null && !con.isClosed()) {
    				con.rollback();
    			}
    		}
    		catch (SQLException e) {
    			throw new DbSessionException("回滚事务失败", e);
    		}
    	}
    	
    	/**
    	 * 执行更新语句
    	 * @param sql SQL语句
    	 * @param params 替换SQL语句中占位符的参数
    	 * @return 多少行受影响
    	 */
    	public DbResult executeUpdate(String sql, Object... params) {
    		try {
    			boolean isInsert = sql.trim().startsWith("insert");
    			if(isInsert) {
    				stmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
    			}
    			else {
    				stmt = con.prepareStatement(sql);
    			}
    			for(int i = 0; i < params.length; i++) {
    				stmt.setObject(i + 1, params[i]);
    			}
    			int affectedRows = stmt.executeUpdate();
    			Serializable generatedKey = null;
    			if(isInsert) {
    				rs = stmt.getGeneratedKeys();
    				generatedKey = rs.next()? (Serializable) rs.getObject(1) : generatedKey;
    			}
    			return new DbResult(affectedRows, generatedKey);
    		}
    		catch (SQLException e) {
    			throw new DbSessionException(e);
    		}
    	}
    	
    	/**
    	 * 执行查询语句
    	 * @param sql SQL语句
    	 * @param params 替换SQL语句中占位符的参数
    	 * @return 结果集(游标)
    	 */
    	public ResultSet executeQuery(String sql, Object... params) {
    		try {
    			stmt = con.prepareStatement(sql);
    			for(int i = 0; i < params.length; i++) {
    				stmt.setObject(i + 1, params[i]);
    			}
    			rs = stmt.executeQuery();
    		}
    		catch (SQLException e) {
    			throw new DbSessionException(e);
    		}
    		
    		return rs;
    	}
    	
    }

     

     

    package com.lovo.util;
    
    import java.io.Serializable;
    
    /**
     * 数据库操作的结果
     * @author Hao
     *
     */
    public class DbResult {
    	private int affectedRows;		// 受影响的行数
    	private Serializable generatedKey;	// 生成的主键
    
    	public DbResult(int affectedRows, Serializable generatedKey) {
    		this.affectedRows = affectedRows;
    		this.generatedKey = generatedKey;
    	}
    
    	public int getAffectedRows() {
    		return affectedRows;
    	}
    
    	public Serializable getGeneratedKey() {
    		return generatedKey;
    	}
    
    }

     

    数据库会话工厂的代码如下所示,使用ThreadLocal将数据库会话和线程绑定。

     

    package com.lovo.util;
    
    
    /**
     * 数据库会话工厂
     * @author 骆昊
     *
     */
    public class DbSessionFactory {
    	private static final ThreadLocal<DbSession> threadLocal = new ThreadLocal<DbSession>();
    	
    	private DbSessionFactory() {
    		throw new AssertionError();
    	}
    	
    	/**
    	 * 打开会话
    	 * @return DbSession对象
    	 */
    	public static DbSession openSession() {
    		DbSession session = threadLocal.get();
    		
    		if(session == null) {
    			session = new DbSession();
    			threadLocal.set(session);
    		}
    		
    		session.open();
    		
    		return session;
    	}
    	
    	/**
    	 * 关闭会话
    	 */
    	public static void closeSession() {
    		DbSession session = threadLocal.get();
    		threadLocal.set(null);
    		
    		if(session != null) {
    			session.close();
    		}
    	}
    	
    }
    

    如果使用基于事务脚本模式的分层开发,可以在业务逻辑层设置事务的边界,但是这会导致所有的业务逻辑方法中都要处理事务,为此可以使用代理模式为业务逻辑对象生成代理,如果业务逻辑层有设计接口,那么可以使用Java中的动态代理来完成业务逻辑代理对象的创建,代码如下所示。

     

     

    package com.lovo.biz;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import com.lovo.exception.DbSessionException;
    import com.lovo.util.DbSession;
    import com.lovo.util.DbSessionFactory;
    
    /**
     * 业务逻辑代理对象(对非get开头的方法都启用事务)
     * @author 骆昊
     *
     */
    public class ServiceProxy implements InvocationHandler {
    	private Object target;
    	
    	public ServiceProxy(Object target) {
    		this.target = target;
    	}
    	
    	public static Object getProxyInstance(Object target) {
    		Class<?> clazz = target.getClass();
    		
    		return Proxy.newProxyInstance(clazz.getClassLoader(), 
    				clazz.getInterfaces(), new ServiceProxy(target));
    	}
    	
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		Object retValue = null;
    		DbSession session = DbSessionFactory.openSession();
    		boolean isTxNeeded = !method.getName().startsWith("get");
    		try {
    			if(isTxNeeded) session.beginTx();
    			retValue = method.invoke(target, args);
    			if(isTxNeeded) session.commitTx();
    		}
    		catch(DbSessionException ex) {
    			ex.printStackTrace();
    			if(isTxNeeded) session.rollbackTx();
    		}
    		finally {
    			DbSessionFactory.closeSession();
    		}
    		return retValue;
    	}
    
    }
    

     

    可以使用工厂类来创建业务逻辑对象,其实DAO实现类对象的创建也应该交给工厂来完成,当然,对于那些熟练使用Spring框架的Java开发者来说,这些东西Spring都帮你做好了,你只需要做出一些配置即可,Spring的理念是“不重复发明轮子”。我们上面的很多代码都是在重复的发明轮子,但是作为一个案例,这个例子却充分运用了多态、反射、接口回调、接口注入、代理模式、工厂模式、单例模式、ThreadLocal等诸多知识点。如果你已经对Java有了一定程度的了解和认识,想验证自己的水平,真的可以尝试自己写一个MVC框架。

    业务逻辑对象的工厂类,仍然是采用约定优于配置的方式,代码如下所示。

     

    package com.lovo.biz;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import com.lovo.util.CommonUtil;
    
    /**
     * 创建业务逻辑代理对象的工厂 (登记式单例模式)
     * @author 骆昊
     *
     */
    public class ServiceFactory {
    	private static final String DEFAULT_IMPL_PACKAGE_NAME = "impl";
    	
    	private static Map<Class<?>, Object> map = new HashMap<>();
    
    	/**
    	 * 工厂方法
    	 * @param type 业务逻辑对象的类型
    	 * @return 业务逻辑对象的代理对象
    	 */
    	public static synchronized Object factory(Class<?> type) {
    		if(map.containsKey(type)) {
    			return map.get(type);
    		}
    		else {
    			try {
    				Object serviceObj = Class.forName(
    						type.getPackage().getName() + "." + DEFAULT_IMPL_PACKAGE_NAME + "." 
    						+ type.getSimpleName() + CommonUtil.capitalize(DEFAULT_IMPL_PACKAGE_NAME)).newInstance();
    				map.put(type, ServiceProxy.getProxyInstance(serviceObj));
    				return serviceObj;
    			} catch (Exception e) {
    				throw new RuntimeException(e);
    			}
    		}
    	}
    }
    


    项目的其他部分,我就不在这里赘述了,运行效果如下图所示。

    查看和创建班级页面。

    点击班级名称查看学生信息。

    点击下一页可以查看下一页的学生信息。

    点击修改按钮编辑学生信息。

    点击删除按钮删除班级或学生信息(删除班级时如果班级中有学生则无法删除)。

     

    展开全文
  • MyMVC框架说明 * 本框架起源于课堂教学实践,目的在于...*框架对表单使用用户自定义类进行了封装,用户需要强制类型转换为具体类型拆出数据。 *整体上MyMVC 博文链接:https://sun-pursuer.iteye.com/blog/223289
  • 自定义MVC框架(一)自定义MVC框架MVC简介MVC结构MVC开发自定义MVC工作原理简单计算器案例 自定义MVC框架 MVC简介 什么是MVC ? MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的...

    自定义MVC框架简单入门、开发

    MVC简介

    什么是MVC ?

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,它是一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码.

    Model1 jsp+jdbc

    Model2 ->MVC

    MVC核心思想:各司其职

    MVC结构

    M
    实体域模型(名词)
    过程域模型(动词)

    V
    jsp/ios/android

    C
    servlet/action

    web做浏览器请求分发
    service调用dao处理项目业务的
    dao操作数据库

    注意:

    • 不能跨层调用
    • 只能出现由上而下的调用

    MVC开发

    现今开发
    实体类、dao、web
    	AddBookServlet
    	DelBookServlet
    	。。。。。。
    	
    web.xml
    	AddBookServlet
    	DelBookServlet
    	。。。。。。
    	
    	
    ....>
    BookServlet
    web.xml
    	BookServlet
    	String methodName = req.getParamater("methodName");
    	String methodName = req.getParamater("bname");
    	String methodName = req.getParamater("pice");
    	String methodName = req.getParamater("type");
    	........
    	Book b = new Book();
    	b.set()
    	......
    	
    	if("add".equals(methodName)){
    		add(req,resp);
    	}
    	
    	重定向、转发
    	req.getResquestDispather("xxx.jsp").forward(req,resp);
    	resp.sendRedirect();
    	
    jsp--->methodName=add/del/....
    
    -->
    

    前一种方式有一种方式有什么不好呢?

    • 1、BookServlet中的if语句判断非常多

    • 2、省去jsp传递到后台封装成对象的过程

    • 3、省去结果集的处理

      控制器:

    中央控制器 子控制器


    自定义MVC工作原理

    自定义MVC工作原理图:

    注意:图中的ActionServlet也可是DispatcherServlet
    在这里插入图片描述
    控制器:

    主控制动态调用子控制器,完成具体的业务逻辑

    主控制器:查看是否有对应的子控制器来处理用户请求,如果就调用子控制器来处理请求,没有就报错,就处理不了请求

    子控制器:就是处理用户请求用的


    简单计算器案例

    首先定义主控制器,代码如下:

    package com.dj.framework;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.dj.web.AddCalAction;
    import com.dj.web.DelCalAction;
    import com.dj.web.DivideCalAction;
    import com.dj.web.MultiplyCalAction;
    
    /**
     * 主控制器
     * @author 86182
     */
    public class DispatcherServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	
    	private Map<String, Action> actionMap = new HashMap<>();//键值对
    
    	public void init() {
    		actionMap.put("/addCal", new AddCalAction());//加
    		actionMap.put("/delCal", new DelCalAction());//减
    		actionMap.put("/multiplyCal", new MultiplyCalAction());//乘
    		actionMap.put("/divideCal", new DivideCalAction());//除
    	}
    	
    	@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 {
    		init();
    		String url = req.getRequestURI();//路径
    		url = url.substring(url.lastIndexOf("/"),url.lastIndexOf("."));
    		
    		Action action = actionMap.get(url);
    		action.execut(req, resp);	
    	}
    }
    

    其次定义子控制器,

    package com.dj.framework;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 子控制器
     * 专门用来处理业务逻辑
     * @author 86182
     */
    public interface Action {
    	
    	void execut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
    	
    }
    

    然后,完成业务逻辑处理界面

    
    加:
    
    public class AddCalAction implements Action{
    	@Override
    	public void execut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		String num1 = req.getParameter("num1");
    		String num2 = req.getParameter("num2");
    		req.setAttribute("res", Integer.valueOf(num1) + Integer.valueOf(num2));
    		req.getRequestDispatcher("calRes.jsp").forward(req, resp);
    	}
    }
    
    
    减:
    
    public class DelCalAction implements Action{
    	@Override
    	public void execut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		String num1 = req.getParameter("num1");
    		String num2 = req.getParameter("num2");
    		req.setAttribute("res", Integer.valueOf(num1) -   Integer.valueOf(num2));                            
    		req.getRequestDispatcher("calRes.jsp").forward(req, resp);
    	}
    }
    
    
    乘:
    
    public class MultiplyCalAction implements Action{
    	@Override
    	public void execut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		String num1 = req.getParameter("num1");
    		String num2 = req.getParameter("num2");
    		req.setAttribute("res", Integer.valueOf(num1) * Integer.valueOf(num2));
    		req.getRequestDispatcher("calRes.jsp").forward(req, resp);
    	}
    }
    
    
    除:
    
    public class DivideCalAction implements Action{
    	@Override
    	public void execut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		String num1 = req.getParameter("num1");
    		String num2 = req.getParameter("num2");
    		req.setAttribute("res", Integer.valueOf(num1) / Integer.valueOf(num2));
    		req.getRequestDispatcher("calRes.jsp").forward(req, resp);
    	}
    }
    

    当然,少不了要配置web.xml
    在这里插入图片描述
    除此之外,在jsp界面中还有一个很重要的函数。
    在这里插入图片描述
    页面展示效果如下:
    在这里插入图片描述
    依次点击 “+”、“-”、“*”、“/”,依次得到以下结果:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    诸位可以自己尝试做简单的计算器,还是挺有意思的呢 (●’◡’●) (/≧▽≦)/

    展开全文
  • 木制品 简单的 PHP MVC 框架
  • Java Web自定义MVC框架项目源代码

    千次下载 热门讨论 2015-04-13 16:03:16
    Java Web自定义MVC框架完整的项目文件,包括源代码和创建数据库的SQL语句,详细的讲解请参考http://blog.csdn.net/jackfrued/article/details/42774459
  • 用于php的自定义MVC框架。 工作正在进行中。
  • 自定义MVC框架之详述过程(二)前言自定义MVC的流程案例讲述总结 前言 由于自定义MVC的使用不熟练,江今儿个又来说说这自定义MVC,之前也讲过关于自定义MVC的,指路:自定义MVC框架(一) 自定义MVC的流程 案例讲述...

    自定义MVC框架之详述过程(二)

    前言

    由于自定义MVC的使用不熟练,江今儿个又来说说这自定义MVC,之前也讲过关于自定义MVC的,指路:自定义MVC框架(一)

    自定义MVC的流程

    自定义MVC流程

    案例讲述

    MySQL+自定义MVC+bootstrap实现的
    在这里插入图片描述
    其实效果与之前的一样,就显示数据,增删改查看详情熬!
    步骤按上方流程图讲述,首先是通过请求到达中央控制器,
    1.获取请求路径
    2.截取出请求路径
    3.判断请求路径有没有找到 没有找到就抛出异常
    4.通过path实例化处理网络请求URL的类
    5.动态封装参数 判断此类有没有继承父类modelDroiven(这一点忘记在上面的流程图讲啦!)
    6.动态调用方法,调用的execute中通过反射使用对应的方法,因为将req当做参数传递了过去所以可以获取到方法名
    7.通过解析xml文件获取到path路径 再通过isRedirect方法判断是否需要重定向或者转发
    8.最后显示结果

    中央控制器:

    package com.zengjing.framework;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    
    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;
    
    @WebServlet(name="dispatcherServlet",urlPatterns="*.action")
    public class DispatcherServlet extends HttpServlet{
    
    	private static final long serialVersionUID = 1L;
    	private ConfigModel configModel;
    	
    	@Override
    	public void init() throws ServletException {
    		try {
    			configModel = ConfigModelFactory.build();
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			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 {
    	//1.获取请求路径
    		String uri=req.getRequestURI();
    		req.setCharacterEncoding("utf-8");
    		//2.截取出请求路径
    		String path = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));//book
    		//System.out.println(uri);
    		//System.out.println(path);
    		ActionModel actionModel = configModel.pop(path);
    		//3.判断请求路径有没有找到 没有找到就抛出异常
    		if(actionModel==null) {
    			throw new RuntimeException(path+"action 标签没有配置");
    		}
    		
    		//4.实例化处理网络请求URL的类
    		try {
    			Action action=(Action) Class.forName(actionModel.getType()).newInstance();
    			//5.动态封装参数 判断此类有没有继承父类modelDroiven
    			if(action instanceof ModelDriven) {
    				ModelDriven md=(ModelDriven)action;
    				//将前端jsp参数传递到后端的所有值封装到业务模型类中
    				BeanUtils.populate(md.getModel(), req.getParameterMap());
    			}
    			//6.动态调用方法
    			String code = action.execute(req, resp);
    			ForwardModel forwardModel = actionModel.pop(code);
    			//7.通过解析xml文件获取到path路径 再通过isRedirect方法判断是否需要重定向或者转发
    			String jspPath=forwardModel.getPath();
    			if(forwardModel.isRedirect()) {
    				resp.sendRedirect(req.getServletContext()+jspPath);
    			}else {
    				req.getRequestDispatcher(jspPath).forward(req, resp);
    			}
    			
    		} catch (InstantiationException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalAccessException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (ClassNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (InvocationTargetException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (NoSuchMethodException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (SecurityException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalArgumentException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		
    		
    	}
    }
    
    
    

    通过path实例化处理网络请求URL的工具类:

    package com.zengjing.action;
    
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.List;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import com.zengjing.dao.RecruitmentDao;
    import com.zengjing.entity.Recruitment;
    import com.zengjing.framework.ActionSupport;
    import com.zengjing.framework.ModelDriven;
    import com.zengjing.util.PageBean;
    
    
    public class RecruitmentServlet extends ActionSupport implements ModelDriven<Recruitment>{
    
    	private static final long serialVersionUID = 1L;
    	
    	private Recruitment r=new Recruitment();
    
    	public RecruitmentDao rd=new RecruitmentDao();
    	
    	public String list(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    		Recruitment re=new Recruitment();
    		req.setCharacterEncoding("utf-8");
    		String name=req.getParameter("name");
    		re.setCompany(name);
    		
    		PageBean pageBean=new PageBean();
    		pageBean.setRequest(req);
    
    		req.setAttribute("pageBean", pageBean);
    		try {
    			List<Recruitment> ls= rd.list(re, pageBean);
    			req.setAttribute("recruitment", ls);
    			req.setAttribute("name", name);
    			req.getRequestDispatcher("/index.jsp").forward(req, resp);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return "list";
    	}
    
    	public String add(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    		rd.add(r);
    		return "toList";
    	}
    	
    	public String del(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    		rd.del(r);
    		return "toList";
    	}
    	
    	public String edit(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    		rd.edit(r);
    		return "toList";
    	}
    	
    	public String toEdit(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    		Recruitment re=this.rd.list(r, null).get(0);
    		req.setAttribute("r", re);
    		return "toEdit";
    	}
    	
    	public String one(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    		Recruitment re=this.rd.list(r, null).get(0);
    		req.setAttribute("Recruitment", re);
    		return "toOne";
    	}
    	
    	@Override
    	public Recruitment getModel() {
    		// TODO Auto-generated method stub
    		return r;
    	}
    	
    }
    
    

    用来动态调用方法的类:

    package com.zengjing.framework;
    
    import java.lang.reflect.Method;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class ActionSupport implements Action{
    
    	@Override
    	public String execute(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    		String methodName=req.getParameter("methodName");
    		Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
    		m.setAccessible(true);
    		return (String)m.invoke(this,req, resp);
    	}
    
    }
    
    

    写完以上的自定义mvc框架之后,咱们调用dao方法也就简化了许多,也简明易懂:

    
    public class RecruitmentDao extends BaseDao<Recruitment>{
    
    	
    	public List<Recruitment> list(Recruitment r,PageBean pageBean) throws Exception{
    		String name=r.getCompany();
    		String id=r.getId();
    		String sql="select * from t_solr_job where true ";
    		if(name!=null) {
    			sql+=" and company like '%"+name+"%'";
    		}
    		if(id!=null) {
    			sql+=" and id='"+id+"'";
    		}
    		return super.executeQuery(sql, r.getClass(), pageBean);
    	}
    	
    	public int add(Recruitment r) throws Exception{
    		String sql="insert into t_solr_job values(?,?,?,?,?,?,?,now(),?,?,?)";
    		return super.executeUpdate(sql, r,new String[] {"id","job","company","address","salary","url","limit","desc","jobHandle","addressHandle"} );
    		
    	}
    	
    	public int del(Recruitment r) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, SQLException {
    		String sql="delete from t_solr_job where id=?";
    		return super.executeUpdate(sql, r, new String[] {"id"});
    	}
    	
    	public int edit(Recruitment r) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, SQLException{
    		String sql="update t_solr_job set `job`=?,`company`=?,`address`=?,`salary`=?,`url`=?,`limit`=?,`desc`=?,`jobHandle`=?,`addressHandle`=? where `id`=?";
    		return super.executeUpdate(sql, r,new String[] {"job","company","address","salary","url","limit","desc","jobHandle","addressHandle","id"} );
    	}
    }
    	
    
    

    增加&模糊查询:
    在这里插入图片描述
    查看详情,里面数据不能改变需要点击修改进入修改界面才可以改:
    在这里插入图片描述
    修改,注意光标,是可以进行编辑(修改)的意思:
    在这里插入图片描述

    总结

    关键性的代码我都搁上边啦,有啥问题欢迎指教哦摆摆!

    展开全文
  • 基于PHP面向对象的自定义MVC框架高级项目开发12天视频video基于PHP面向对象的自定义MVC框架高级项目开发12天视频video教程大小: 发布时间:2016-02-26 下载次数:次本套基于PHP面向对象的自定义MVC框架高级项目...

    php教程

    当前位置:主页 > php教程 > 基于PHP面向对象的自定义MVC框架高级项目开发12天视频video

    基于PHP面向对象的自定义MVC框架高级项目开发12天视频video

    教程大小:   发布时间:2016-02-26   下载次数:次

    本套基于PHP面向对象的自定义MVC框架高级项目开发12天视频video共12天,是一套项目实战的php视频教程,希望您提供本套教程的学习,能够掌握php的设计方法和流程,屌丝建站教程自学网祝您在学习的道路好好学习。不断进步!

    教程目录如下:

    1面向对象编程思想介绍.wmv

    2基本概念:类和对象.wmv

    3属性和方法,对象的创建和使用.wmv

    4对象的传值方式.wmv

    5实例属性实例方法.wmv

    6静态属性静态方法.wmv

    7this和self关键字.wmv

    8构造方法.wmv

    9析构.wmv

    9覆盖的基本要求.wmv

    8覆盖-基本概念.wmv

    7parent关键字.wmv

    6构造方法和析构方法的继承特性.wmv

    5访问控制修饰符.wmv

    4继承-完整概念演示.wmv

    3继承-基本概念.wmv

    2day13作业.wmv

    1day1复习.wmv

    13mysql数据库工具类.wmv

    12单例模式.wmv

    11设计模式-工厂模式.wmv

    10最终类,最终方法.wmv

    9__isset方法, __unset方法.wmv

    8__get方法.wmv

    7__set方法.wmv

    6重载概念.wmv

    5抽象类和抽象方法的关系.wmv

    4抽象类和抽象方法.wmv

    3mysqldb工具类.wmv

    2day2作业.wmv

    1day2复习.wmv

    12mysqldb工具类的继续扩展.wmv

    11接口interface.wmv

    10方法重载.wmv

    9类型约束.wmv

    8标准内置类以及转换为对象的处理.wmv

    7对象遍历.wmv

    6对象的克隆及单例的改进.wmv

    5自定义自动加载.wmv

    4常规自动加载.wmv

    3day3作业.wmv

    2mysqldb工具类.wmv

    1day3复习.wmv

    15面向对象3大思想.wmv

    14其他零碎.wmv

    13__tostring,__invoke演示.wmv

    12mysqldb工具类的进一步优化.wmv

    11对象的序列化和反序列化.wmv

    10序列化与反序列化技术.wmv

    9控制器中调用模型类获取数据,删除用户举例.wmv

    8控制器调用模型层的典型实现.wmv

    7模型层的作用与典型代码模式.wmv

    6mvc思想框架图.wmv

    5mvc典型演示(带数据库).wmv

    4mvc简单演示.wmv

    3模板技术.wmv

    2显示与逻辑相分离思想.wmv

    1项目开发流程.wmv

    13整个模型层的关系结构.wmv

    12模型工厂单例化.wmv

    11基础模型类.wmv

    10商品模块的实现.wmv

    9前端控制器(请求分发器).wmv

    8视图层的典型实现.wmv

    7基础控制器类的实现.wmv

    6修改用户数据.wmv

    5控制器的提升.wmv

    4控制器的作用及细节描述.wmv

    3添加用户(提交数据).wmv

    2添加用户(表单界面).wmv

    1day5复习.wmv

    10目录结构设定.wmv

    9ecshop安装.wmv

    8禁止某些目录或文件的访问.wmv

    7基础常量的设定.wmv

    6自动加载的实现.wmv

    5后台的简单实现(再做了一个版本).wmv

    4后台的简单实现.wmv

    3平台的划分.wmv

    2新功能:商品详情.wmv

    1day7复习.wmv

    13pdo结果集对象.wmv

    12pdo的错误处理.wmv

    11pdo的基本概念和使用.wmv

    10仿ecshop登录.wmv

    9session概念和基本原理.wmv

    8cookie应用:记住用户名.wmv

    7cookie其他细节及总结.wmv

    6cookie细节:过期时间.wmv

    5cookie初步.wmv

    4登录分析,会话技术的引入.wmv

    3异常处理.wmv

    2pdo的预处理语法.wmv

    1day8复习.wmv

    10session初步.wmv

    9文件上传类在mvc项目中的应用.wmv

    8文件上传类.wmv

    7文件上传原理演示.wmv

    6商品上传和列表显示.wmv

    5商品模块分析与添加界面的实现.wmv

    4mysql注入及防范.wmv

    3退出功能的实现.wmv

    2实现mvc的仿ecshop后台框架.wmv

    1day9复习.wmv

    2016年屌丝建站教程自学网重磅出击,实力教程打架整理中,带你轻轻松松走向人生巅峰,漂亮的不像实力派!赶紧学习吧!祝普天下的屌丝男女新的一年容光焕发,财源广进!

    网盘提取密码:r686

    a1c3929c6a57cb24e787a46be0c3d49e.png

    http://pan.baidu.com/s/1i4jWJpb

    本文版权归原作者所有,转载请注明原文来源出处,屌丝建站教程自学网感谢您的支持!

    展开全文
  • PHP 自定义MVC框架-----码神帅

    千次阅读 2018-05-23 11:51:58
    1.新建根目录2.入口文件3....lt;?php /**框架核心类*运行框架*自动加载类*/namespace Framework;use Exception;class Framework{ public... //运行框架 static public function run(){ $route = new route(); $con =...
  • 本程序是基于MyMVC Framework开发web程序实现的演示 博文链接:https://sun-pursuer.iteye.com/blog/223295
  • 自定义mvc框架

    2012-09-21 17:12:56
    自定义mvc框架,了解mvc开发流程,进一步深入了解mvc框架的作用,方便开发工作
  • SkataFramework-PHP 我自己的自定义MVC框架基于与Laravel和Symfony等流行框架相同的原理。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 97,781
精华内容 39,112
关键字:

自定义mvc框架