先看看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访问成功,页面跳转。
只是实现了一个简单的流程,还有很多细节没有实现。
由于公司限制复制内容大小,限制网络等原因,代码格式不好,以后完善后给出下载链接。