实现V3 版本
在V2 版本中,基本功能以及完全实现,但代码的优雅程度还不如人意。譬如HandlerMapping 还不能像SpringMVC
一样支持正则,url 参数还不支持强制类型转换,在反射调用前还需要重新获取beanName,在V3 版本中,下面我们
继续优化。
首先,改造HandlerMapping,在真实的Spring 源码中,HandlerMapping 其实是一个List 而非Map。List 中的元
素是一个自定义的类型。现在我们来仿真写一段代码,先定义一个内部类Handler 类:
package com.hezhiqin.hand;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.hezhiqin.mvcframework.annotation.HZQRequestParam;
public class HandlerMapping {
public Object controller;//保存方法对应的实例
public Method method;//保存映射的方法
public Pattern pattern;
public Map<String,Integer> paramIndexMapping;//参数顺序
/**
*构造一个Handler基本的参数
* @param controller
* @param method
*/
public HandlerMapping(Object controller, Method method, Pattern pattern) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
paramIndexMapping = new HashMap<String,Integer>();
putParamIndexMapping(method);
}
private void putParamIndexMapping(Method method) {
// TODO Auto-generated method stub
//提取方法中加了注解的参数
Annotation [] [] pa = method.getParameterAnnotations();
for (int i = 0; i < pa.length; i++) {
for (Annotation a : pa[i]) {
if(a instanceof HZQRequestParam){
String paramName = ((HZQRequestParam) a).value();
if(!"".equals(paramName.trim())){
paramIndexMapping.put(paramName, i);
}
}
}
}
//提取方法中的 request 和 response 参数
Class<?> [] paramsTypes = method.getParameterTypes();
for (int i = 0; i < paramsTypes.length ; i ++) {
Class<?> type = paramsTypes[i];
if(type == HttpServletRequest.class ||
type == HttpServletResponse.class){
paramIndexMapping.put(type.getName(),i);
}
}
}
public Class<?>[] getParamTypes() {
Class<?> [] paramsTypes = method.getParameterTypes();
return paramsTypes;
}
}
然后,优化HandlerMapping 的结构,代码如下:
//保存 url 和 Method 的对应关系
// private Map<String,Method> handlerMapping = new HashMap<String,Method>();
private List<HandlerMapping> handlerMapping = new ArrayList<HandlerMapping>();
修改initHandlerMapping()方法:
private void initHandlerMapping() {
if(ioc.isEmpty()){ return; }
for (Entry<String, Object> entry : ioc.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(HZQController.class)){ continue; }
String url = "";
if(clazz.isAnnotationPresent(HZQRequestMapping.class)){
HZQRequestMapping requestMapping = clazz.getAnnotation(HZQRequestMapping.class);
url = requestMapping.value();
}
//获取 Method 的 url 配置
Method [] methods = clazz.getMethods();
for (Method method : methods) {
//没有加 RequestMapping 注解的直接忽略
if(!method.isAnnotationPresent(HZQRequestMapping.class)){ continue; }
//映射 URL
HZQRequestMapping requestMapping = method.getAnnotation(HZQRequestMapping.class);
String regex = ("/" + url + requestMapping.value()).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
handlerMapping.add(new HandlerMapping(entry.getValue(),method,pattern));
System.out.println("mapping " + regex + "," + method);
}
}
}
修改doDispatch()方法:
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
HandlerMapping handler = getHandler(req);
if (handler==null) {
resp.getWriter().write("404 Not Found!!!");
return;
}
//获得方法的形参列表
Class<?> [] paramTypes = handler.getParamTypes();
Object [] paramValues = new Object[paramTypes.length];
Map<String,String[]> params = req.getParameterMap();
for (Map.Entry<String, String[]> parm : params.entrySet()) {
String value = Arrays.toString(parm.getValue()).replaceAll("\\[|\\]","")
.replaceAll("\\s",",");
if(!handler.paramIndexMapping.containsKey(parm.getKey())){continue;}
int index = handler.paramIndexMapping.get(parm.getKey());
paramValues[index] = convert(paramTypes[index],value);
}
if(handler.paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex] = req;
}
if(handler.paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
int respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[respIndex] = resp;
}
Object returnValue = handler.method.invoke(handler.controller,paramValues);
if(returnValue == null || returnValue instanceof Void){ return; }
resp.getWriter().write(returnValue.toString());
}
private HandlerMapping getHandler(HttpServletRequest req) {
if(handlerMapping.isEmpty()){ return null; }
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
System.out.println("我草拟大爷"+url);
for (HandlerMapping handler : handlerMapping) {
Matcher matcher = handler.pattern.matcher(url);
System.out.println(matcher.matches());
//如果没有匹配上继续下一个匹配
if(!matcher.matches()){ continue; }
return handler;
}
return null;
}
private Object convert(Class<?> type,String value){
if(Integer.class == type){
return Integer.valueOf(value);
}
return value;
}
在以上代码中,增加了两个方法,一个是getHandler()方法,主要负责处理url 的正则匹配;一个是
convert()方法,主要负责url 参数的强制类型转换。
至此,手写Mini 版SpringMVC 框架就已全部完成。