实现一个简单的MVC框架——SmartMVC
1.SmartMVC是什么?
是一个类似于SpringMVC的web应用开发框架,其核心是一个通用的控制器。
利用SmartMVC可以开发基于MVC架构的web应用。
2.SmartMVC的架构
3.主要组件及其关系
4.具体实施
该项目主要实现功能:欢迎页面和简单注册登录功能
准备阶段
-
新建一个maven项目—smartMVC,生成web.xml文件,部署到Tomcat上
-
前端.jsp页面,放在webapp/WEB-INF/目录下,用户无法直接访问
-
导入依赖包
<dependencies> <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> </dependencies>
-
自定义注解
package core.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface RequestMapping { public String value(); }
-
处理器类(Controller):
负责业务逻辑的处理。对于比较复杂的业务,也可以调用其它的类来处理。
package controller; import core.annotation.*; public class HelloController { @RequestMapping("/hello.do") public String hello(){ return "hello"; } }
package controller; import javax.servlet.http.HttpServletRequest; import core.annotation.RequestMapping; public class LoginController { @RequestMapping("/toLogin.do") public String toLogin() { return "login"; } @RequestMapping("/login.do") public String login(HttpServletRequest request) { //获得用户名和密码 String username = request.getParameter("username"); System.out.println("username:" + username); String pwd = request.getParameter("pwd"); if("root".equals(username) && "123".equals(pwd)) { //登录成功 return "success"; }else { //登录失败 request.setAttribute("erro_info", "用户名或密码错误"); return "login"; } } }
-
web.xml配置文件,容器启动时,前端控制器实例化
<servlet> <description></description> <display-name>DispatchServlet</display-name> <servlet-name>DispatchServlet</servlet-name> <servlet-class>core.web.DispatchServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatchServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
-
smartmvc.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean class="controller.HelloController"></bean> <bean class="controller.LoginController"></bean> </beans>
-
核心部分——前端控制器(DispatchServlet)
读取smartmvc.xml配置文件当中配置的处理器类名,然后将处理器实例化。
接下来,将处理器实例交给HandlerMapping来处理。
package core.web; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import core.common.Handler; import core.common.HandlerMapping; public class DispatchServlet extends HttpServlet { private static final long serialVersionUID = 1L; private HandlerMapping hmapping; public void init() throws ServletException { SAXReader reader = new SAXReader(); InputStream in = getClass().getClassLoader().getResourceAsStream("smartMVC.xml"); try { Document doc = reader.read(in); Element root = doc.getRootElement(); List<Element> list = root.elements(); List<Object> beans=new ArrayList<>(); for(Element ele: list){ String className = ele.attributeValue("class"); Class<?> c = Class.forName(className); Object obj = c.newInstance(); beans.add(obj); } hmapping=new HandlerMapping(); hmapping.process(beans); } catch (Exception e) { e.printStackTrace(); } } protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); //获得请求资源路径 String uri = request.getRequestURI(); System.out.println(uri); //获得应用名 String contextPath = request.getContextPath(); System.out.println(contextPath); //将请求资源路径中的应用名除掉,生成请求路径 String path = uri.substring(contextPath.length()); System.out.println("path:"+path); //依据请求路径,获得对应的处理器 Handler handler = hmapping.getHandler(path.trim()); System.out.println("handler:"+handler); if(handler==null){ response.sendError(404); return; } Method mt = handler.getMt(); Object obj = handler.getObj(); Object result=null; try { /* * 调用处理器的方法之前,先看 * 处理器的方法是否带参,如果带有 * 参数(当前版本只支持request和response), * 则将相应的参数值传递给处理器对应的方法。 */ Class<?>[] types = mt.getParameterTypes(); if(types.length==0){ result = mt.invoke(obj); }else{ 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; } } result=mt.invoke(obj, params); } System.out.println(result); //获得视图名 String viewName=result.toString(); /* * 处理视图名。 * 默认转发到"/WEB-INF/" + 视图名 * +".jsp"。 */ request.getRequestDispatcher("/WEBINF/"+viewName+".jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); } } }
-
映射处理器类(HandlerMapping):
负责提供请求路径与处理器的对应关系。
package core.common; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import core.annotation.RequestMapping; public class HandlerMapping { /* * maps用于存放请求路径与处理器的对应关系。 * 为了方便利用java反射去调用处理器的方法, * 将处理器和Method对象封装成了Handler对象。 */ private Map<String,Handler> maps=new HashMap<String,Handler>(); /** * 依据请求路径,返回对应的Handler对象。 */ public Handler getHandler(String path){ return maps.get(path); } /** * 负责建立请求路径与处理器的对应关系。 * 比如"/hello.do"由HelloController的hello方法 * 来处理。 */ public void process(List<Object> beans) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { for(Object obj:beans){ //获得class对象 Class c = obj.getClass(); //获得所有方法 Method[] methods = c.getDeclaredMethods(); for(Method mt:methods){ //获得加在方法前的@RequestMapping RequestMapping requestmapping = mt.getAnnotation(RequestMapping.class); //获得@RequestMapping的value属性值 String value = requestmapping.value(); System.out.println(value); //将请求路径与处理器的对应关系保存下来 maps.put(value, new Handler(obj,mt)); } } Set<Entry<String, Handler>> set = maps.entrySet(); for(Entry<String,Handler> e:set){ String key = e.getKey(); Handler value = e.getValue(); System.out.println(key+":"+value); } } }
-
类Handler,封装了处理器实例和相应的Method对象。
package core.common; import java.lang.reflect.Method; public class Handler { private Object obj; private Method mh; public Handler(Object obj, Method mh) { this.obj = obj; this.mh = mh; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } public Method getMh() { return mh; } public void setMh(Method mh) { this.mh = mh; } }