自己开发一个简单的mvc框架(类似spring mvc)
先看看controller,这里写了一个UserController
package com.tanlei.controller; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import tmvc.framework.annotation.Controller; import tmvc.framework.annotation.RequestMapping; import tmvc.framework.annotation.RequestParameter; @Controller(url = "/user") public class UserController { @RequestMapping(url ="/login.html", method="POST") public String login(@RequestParameter(value="userName") String userName,@RequestParameter(value="request") HttpServletRequest request, @RequestParameter(value="password") String password) { Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> returnMap = new HashMap<String, Object>(); map.put("a", "aaaaa"); System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); request.setAttribute("userName", "tanlei"); return "index.jsp"; } }
这里定义了三个注解:@Controller和@RequestMapping和@RequestParameter
package tmvc.framework.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface RequestParameter { String value() default ""; }
package tmvc.framework.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequestMapping { String url() default ""; //描述路径,例如: /article/1,则这里写成 url = /1 String method() default "GET"; //用于描述请求方式 }
package tmvc.framework.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Controller { String url() default ""; }
核心流程:
服务器启动,执行ControllerHandlerListener,扫描Contrtoller所在包:com.tanlei.controller,生成MethodHandlerMap并放入ServletContext中:
package tmvc.framework.web; import java.util.Map; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import tmvc.framework.util.ControllerHandler; public class ControllerHandleListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } @Override public void contextInitialized(ServletContextEvent servletContextEvent) { try { String controllerPackageName = servletContextEvent.getServletContext().getInitParameter("controllerPackageName"); Map<String, Map<String, Object>> methodHandlerMap = ControllerHandler.handle(controllerPackageName); servletContextEvent.getServletContext().setAttribute("methodHandlerMap", ControllerHandler.handle(controllerPackageName)); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
这里的核心是methodHandlerMap这个数据结构。
来看看methodHandlerMap生成方法:
package tmvc.framework.util; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import tmvc.framework.annotation.Controller; import tmvc.framework.annotation.RequestMapping; import tmvc.framework.annotation.RequestParameter; /** * 控制层处理器 反射controller层的包 得到url与controller对应的map,例如 对ShowArticleController处理 * handlerMap.put("/article/1", map<String, Object>); map.put("1"(二级url), * methodName); map.put("1"(二级url), parameterList<Map<"parameterName",方法参数名>,Map<"parameterType",方法参数类型>>); * * @author Administrator * */
public class ControllerHandler { public static String[] getMethodParameterNamesByAnnotation(Method method) { Annotation[][] parameterAnnotations = method.getParameterAnnotations(); if (parameterAnnotations == null || parameterAnnotations.length == 0) { return null; } String[] parameterNames = new String[parameterAnnotations.length]; int i = 0; for (Annotation[] parameterAnnotation : parameterAnnotations) { for (Annotation annotation : parameterAnnotation) { if (annotation instanceof RequestParameter) { RequestParameter param = (RequestParameter) annotation; parameterNames[i++] = param.value(); } } } return parameterNames; }
public static Map<String, Map<String, Object>> handle(String controllerPackageName) throws ClassNotFoundException { // 控制层的数据封装 Map<String, Map<String, Object>> handlerMap = new HashMap<String, Map<String, Object>>(); // 根据包名反射控制层所有controller Set<String> clazzs = ClassUtil.getClassName(controllerPackageName, false); for (String clazz : clazzs) { Class<?> c = Class.forName(clazz); // 得到头部注解@Controller Controller ct = c.getAnnotation(Controller.class); String url = null; String headerUrl = ct.url();
// 得到方法上的注解@RequestMapping,方法名和方法内的参数(有序) for (Method method : c.getDeclaredMethods()) { Map<String, Object> methodMap = new HashMap<String, Object>(); RequestMapping r = method.getAnnotation(RequestMapping.class); String methodUrl = r.url(); url = headerUrl + methodUrl; Class<?> paramTypes[] = method.getParameterTypes(); String[] paramNames = getMethodParameterNamesByAnnotation(method); List<Map<String , Object>> params = new ArrayList<Map<String, Object>>(); for (int i = 0; i < paramTypes.length; i++) { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("paramName", paramNames[i]); paramMap.put("paramType", paramTypes[i]); params.add(paramMap); } methodMap.put("url", clazz); methodMap.put("params", params); methodMap.put("methodName", method.getName()); handlerMap.put(url, methodMap); } } return handlerMap; } }
ControllerHandler这个类负责扫描Controller所在包的所有类,生成类的反射信息封装在map中。
以UserController为例:
Map<String, Map<String, Object>>的内层map负责 innermap.put("url", com.tanlei.controller.UserController), innermap.put("params", params),
params为一个List<Map<String , Object>> params = new ArrayList<Map<String, Object>>();类型。里面存放的是方法的参数名和类型的信息
例如UserController的login方法参数信息。
innermap.put("methodName", method.getName());
然后外层outermap.put(url, methodMap); url为: /user/login.html,即方法controller和requestMaping的映射信息。
核心dispatcherServlet类负责转发请求给controller和返回到页面:
package tmvc.framework.web; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.Map; 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 tmvc.framework.util.ClassUtil; import tmvc.framework.util.UriUtil; /** * Servlet implementation class DispatcherServlet */ @WebServlet("/DispatcherServlet") public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void init() throws ServletException { }
public void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException, RuntimeException { //反射得到对应的controller Map<String, Map<String, Object>> methodHandlerMap = (Map<String, Map<String, Object>>) this.getServletContext().getAttribute("methodHandlerMap"); String uri = httpServletRequest.getRequestURI().toString(); Map<String, Object> methodMap = methodHandlerMap.get(UriUtil.getControllerUri(httpServletRequest,uri)); String queryStr = httpServletRequest.getQueryString(); System.out.println(uri+ ":" + httpServletRequest.getContextPath().length()); List<Map<String , Object>> params = (List<Map<String, Object>>) methodMap.get("params"); //反射方法得到的参数信息的list Object[] paramObjs = new Object[params.size()];//method的参数值的集合
Class[] paramTypes = new Class[params.size()];//method的参数类型的集合 int index = 0; //List<Map<String , String>> urlParams = RequestParameterHandler.generateParameterListMap(httpServletRequest); //url里面传来的参数的list for(int i = 0; i < params.size(); i++) { Map<String, Object> paramMap = params.get(i); //反射得到的参数map paramTypes[i] = (Class) paramMap.get("paramType"); String paramName = (String) paramMap.get("paramName"); if(paramTypes[i].isInstance(httpServletRequest)) { paramObjs[i] = httpServletRequest; } else { paramObjs[i] = httpServletRequest.getParameter(paramName); } }
try { Class<?> clazz = Class.forName((String)methodMap.get("url")); Method method = clazz.getDeclaredMethod((String)methodMap.get("methodName"), paramTypes); Object obj = clazz.newInstance(); Object ob = method.invoke(obj, paramObjs); System.out.println(ob.toString()); httpServletRequest.getRequestDispatcher("/" + ob.toString()).forward(httpServletRequest, httpServletResponse); return; } catch (ClassNotFoundException | NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }
urlutil:
package tmvc.framework.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; public class UriUtil { public static String getControllerUri(HttpServletRequest request, String uri) { if(uri.contains("?")) { uri = uri.substring(request.getContextPath().length(), uri.indexOf("?")); } else { uri = uri.substring(request.getContextPath().length(), uri.length()); } return uri; } }
classutil核心方法:
public static Set<String> getClassName(String packageName, boolean isRecursion) { Set<String> classNames = null; ClassLoader loader = Thread.currentThread().getContextClassLoader(); String packagePath = packageName.replace(".", "/"); URL url = loader.getResource(packagePath); if (url != null) { String protocol = url.getProtocol(); if (protocol.equals("file")) { classNames = getClassNameFromDir(url.getPath(), packageName, isRecursion); } else if (protocol.equals("jar")) { JarFile jarFile = null;
try{ jarFile = ((JarURLConnection) url.openConnection()).getJarFile(); } catch(Exception e){ e.printStackTrace(); } if(jarFile != null){ getClassNameFromJar(jarFile.entries(), packageName, isRecursion); } } } else { /*从所有的jar包中查找包名*/ classNames = getClassNameFromJars(((URLClassLoader)loader).getURLs(), packageName, isRecursion); } return classNames; }
web.xml的配置:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>test</display-name> <context-param> <param-name>controllerPackageName</param-name> <param-value>com.tanlei.controller</param-value> </context-param> <listener> <listener-class>tmvc.framework.web.ControllerHandleListener</listener-class> </listener>
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>tmvc.framework.web.DispatcherServlet</servlet-class> <init-param> <param-name>controllerPackageName</param-name> <param-value>com.tanlei.controller</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>
访问:http://localhost:8080/user/login.html?userName=a&password=b访问成功,页面跳转。
只是实现了一个简单的流程,还有很多细节没有实现。
由于公司限制复制内容大小,限制网络等原因,代码格式不好,以后完善后给出下载链接。
- 上一篇: 正向代理,反向代理和透明代理的原理和区别!
- 下一篇: java正则表达式捕获组