简单的MVC框架实现

1.MVC(Model View Controller)

MVC是什么?

是一种软件架构思想、其核心思想是,要将数据处理与数据展现分开,按照这种思想,可以将一个软件划分成三种不同类型的模块,分别是模型、视图和控制器。
模型负责数据处理(业务逻辑)、视图负责数据展现(表示逻辑)、控制器负责协调模型和视图(请求要先发送给控制器,由控制器选择对应的模型来处理;模型返回的处理结果也要先发送给控制器,由控制器选择对应的视图来展现)。

如何使用MVC来开发一个web应用?

使用Servlet来充当控制器,使用jsp充当视图,使用java类来充当模型。
三者的关系如下图所示:

简单的MVC框架实现_第1张图片

MVC的优点

a.方便代码的维护。

比如,视图发生变化(修改了视图的代码或者增加了新的视图,不影响模型)。

b.方便测试。

比如,将代码写在java类里面,可以直接测试,不需要部署整个应用。

c.方便分工协作。

2.实现一个简单的MVC框架(smartmvc)

(1)SmartMVC是什么?

是一个简单的MVC框架,实现了一个通用的控制器,开发人员利用该框架,只需要写业务逻辑和表示逻辑。

(2)架构

简单的MVC框架实现_第2张图片

(3)各个组件的关系

简单的MVC框架实现_第3张图片

步骤:

  • step1.新建一个maven工程(smartmvc-exec)。
  • step2.导包。(dom4j)
  • step3.添加login.jsp(/WEB-INF/login.jsp)。
    jsp文件添加在WEB-INF文件夹内,用户不可以字节访问,需通过servlet访问
  • step4.添加@RequestMapping注解(base.common包下)。
//告诉JVM注解的存在时间到运行期
@Retention(value=RetentionPolicy.RUNTIME)
public @interface RequestMapping {
//声明一个属性,注意要用public,而且后面由小括号。用来储存请求路径
	public String value();
}
  • step5.添加LoginController(controller包下)。
public class LoginController {
	@RequestMapping("/toLogin.do")
	public String toLogin() {
		System.out.println("LoginController.toLogin()");
		return "login";
	}
	@RequestMapping("/login.do")
	public String login(HttpServletRequest request) {
		System.out.println("LoginController.login()");
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		if("tom".equals(username)&& "1234".equals(password)) {
			System.out.println("正确,重定向成功页面");
		/*
		 * 如果视图名是以redirect:开头,表示重定向
		 */
			//因为浏览器无法直接访问jsp文件,所以无法直接重定向,这里返回一个请求地址,由下面的方法处理
		return "redirect:toSuccess.do";
		}else {
			request.setAttribute("login_failed", "用户名或密码错误");
			System.out.println("错误,转发登录页面");
			return "login";
		}
	}	
	@RequestMapping("/toSuccess.do")
	public String toSuccess(){
		return "success";//视图名
	}
	
  • step6.添加smartmvc.xml(配置有处理器的类名)。
    这个是用来给servlet使用反射实例化处理器(处理业务逻辑的类)

<beans>
	<bean class="controller.LoginController"/>
	<bean class="..."/>
beans>
  • step7.添加DispatcherServlet(base.web包下)。
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private HandlerMapping handlerMapping;

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("DispatcherServlet.service()");
		request.setCharacterEncoding("utf-8");
		/*
		 * 先获得请求资源路径,然后截取请求资源路径的一部分,生成请求路径(path),然后调用handlerMapping的getHandler方法
		 * 获得Handler对象,利用Handler对象调用处理器的方法
		 */
		String uri = request.getRequestURI();
		System.out.println("uri: "+uri);
		String contextPath = request.getContextPath();
		System.out.println("contextPath: "+ contextPath);
		//截取请求资源路径的一部分(除掉应用名),生成请求路径
		String path = uri.substring(contextPath.length());
		
		System.out.println("path: "+path);
		Handler handler = handlerMapping.getHandler(path);
		System.out.println("handler: "+handler);
		Method m = handler.getMh();
		Object obj = handler.getObj();
		Object rv = null;
		try {
			//获取参数的类型数组。有几个参数,数组就有几个元素
			Class[] types = m.getParameterTypes();
			if(types.length<=0) {
				//0个元素说明是无参方法
				System.out.println("调用无参方法");
				rv = m.invoke(obj);//获得方法的返回值
			}else {
				//有参方法,参数用一个数组来存放,参数的类型自有response和request两种。方法都是从他们里面获取数据
				Object[] params = new Object[types.length]; 
				for(int i=0;i<types.length;i++) {
					if(types[i]==HttpServletRequest.class) {
						params[i] = request;
					}
					if(types[i]==HttpServletResponse.class) {
						params[i]=response;
					}
				}
				System.out.println("调用有参方法");
				rv = m.invoke(obj, params);
			}
			System.out.println("rv: "+rv);
			String viewName = (String) rv;
			System.out.println("viewName: "+viewName);
			if(viewName.startsWith("redirect:")) {//重定向
				String redirectPath = contextPath+"/"+viewName.substring("redirect:".length());
				System.out.println("redirectPath:"+redirectPath);
				response.sendRedirect(redirectPath);
			}else {
				//转发
			/*
			 * 依据视图名,定位到某个jsp
			 * "/WEB-INF/"+视图名+".jsp"
			 */
			String jspPath = "/WEB-INF/"+viewName+".jsp";
			System.out.println("jspPath:"+jspPath);
			request.getRequestDispatcher(jspPath).forward(request, response);
			}
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
	}
	//因为所有的处理器都只需要实例化一次,所以就在初始化的时候实例化一次就可以
	public void init() throws ServletException {
		System.out.println("DispatcherServlet.init()");
		/*
		 * 读取smartmvc配置文件中处理器的配置信息,然后利用java反射将处理器实例化
		 */
		SAXReader reader = new SAXReader();
		InputStream in = getClass().getClassLoader().getResourceAsStream("smartmvc.xml");
		try {
			Document doc = reader.read(in);
			Element root = doc.getRootElement();
			List<Element> elements = root.elements();
			//beans用来存放处理器实例
			List<Object> beans = new ArrayList<Object>();
			for(Element bean:elements) {
				String className = bean.attributeValue("class");
				System.out.println(className);
				//将处理器实例化
				Object obj = Class.forName(className).newInstance();
				beans.add(obj);
			}
			System.out.println("beans: "+beans);
			//创建映射处理器实例
			handlerMapping = new HandlerMapping();
			//调用映射处理器的process方法,该方法利用java反射读取beans元素的@RequestMapping的路径信息,然后建立请求路径与处理器的对应关系
			handlerMapping.process(beans);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
  • step8.添加HandlerMapping(base.common包下)
/**
 * 映射处理器
 * 负责 提供请求路径与处理器的对应关系,比如"/hello.do"应该由HelloController的hello方法来处理
 * @author soft01
 *
 */
public class HandlerMapping {
	private String path;
	/*
	 * handlerMap用于存放请求路径与处理器的对应关系。处理器还需要对应的方法,所以用Handler类来封装
	 */
	private Map<String, Handler> handlerMap = new HashMap<String, Handler>();
	/*
	 * 依据请求路径返回Handler对象。
	 * 注: Handler对象封装了处理器及Method对象
	 */
	public Handler getHandler(String path) {
		return handlerMap.get(path);
	}
	/*
	 * 负责建立请求路径与Handler的对应关系。
	 * 首先遍历beans集合,然后利用java反射读取处理器中的@RequestMapping配置信息的请求路径
	 * 然后,以请求路径作为key,以Handler作为value,Handler封装了处理器及Method对象,
	 * 将这对关系存放到handlerMap里面
	 */
	public void process(List<Object> beans) {
		
		for(Object bean:beans) {
			Class cla = bean.getClass();//class对象可以看作是反射的入口,要做反射相关操作先要拿到class对象
			Method[] ms = cla.getDeclaredMethods();
			for(Method m:ms) {
				//获得加在方法前的@RequestMapping注解
				RequestMapping rm = m.getDeclaredAnnotation(RequestMapping.class);
				if(rm!=null) {
					//读取注解的属性值,即请求路径
					String path = rm.value();
					System.out.println(path);
					//以请求路径为key,以Handler对象为value,将对应关系存放到handlerMap
					handlerMap.put(path, new Handler(m,bean));
				}
				
			}
		}
		System.out.println("handlerMap: "+handlerMap);
	}
step9.添加Handler(base.common包下)。
/**
 * 因为调用方法需要用Method类才行,不能用类名和方法名拼接在一起,这样只是该字符串
 */
import java.lang.reflect.Method;

public class Handler {
	private Method mh;
	private Object obj;
	
	
	public Handler(Method mh, Object obj) {
		super();
		this.mh = mh;
		this.obj = obj;
	}
	public Method getMh() {
		return mh;
	}
	public void setMh(Method mh) {
		this.mh = mh;
	}
	public Object getObj() {
		return obj;
	}
	public void setObj(Object obj) {
		this.obj = obj;
	}
	

你可能感兴趣的:(学习日志,MVC)