流行词:顶层设计 MVC
参考代码:https://gitee.com/li-lixiang/lean-spring-2.0.git
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{
//1、通过从request中拿到URL,去匹配一个HandlerMapping
GPHandlerMapping handler = getHandler(req);
if(handler == null){
processDispatchResult(req,resp,new GPModelAndView("404"));
return;
}
//2、准备调用前的参数
GPHandlerAdapter ha = getHandlerAdapter(handler);
//3、真正的调用方法,返回ModelAndView存储了要穿页面上值,和页面模板的名称
GPModelAndView mv = ha.handle(req,resp,handler);
//这一步才是真正的输出
processDispatchResult(req, resp, mv);
}
初始化ApplicationContext在前面blog中已经说明实现原理和主要逻辑
context = new GPApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
private void initHandlerMappings(GPApplicationContext context) {
String [] beanNames = context.getBeanDefinitionNames();
try {
for (String beanName : beanNames) {
Object controller = context.getBean(beanName);
Class> clazz = controller.getClass();
if(!clazz.isAnnotationPresent(GPController.class)){
continue;
}
String baseUrl = "";
//获取Controller的url配置
if(clazz.isAnnotationPresent(GPRequestMapping.class)){
GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
baseUrl = requestMapping.value();
}
//获取Method的url配置
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//没有加RequestMapping注解的直接忽略
if(!method.isAnnotationPresent(GPRequestMapping.class)){ continue; }
//映射URL
GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
this.handlerMappings.add(new GPHandlerMapping(pattern,controller,method));
log.info("Mapped " + regex + "," + method);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
private void initHandlerAdapters(GPApplicationContext context) {
//把一个requet请求变成一个handler,参数都是字符串的,自动配到handler中的形参
//可想而知,他要拿到HandlerMapping才能干活
//就意味着,有几个HandlerMapping就有几个HandlerAdapter
for (GPHandlerMapping handlerMapping : this.handlerMappings) {
this.handlerAdapters.put(handlerMapping,new GPHandlerAdapter());
}
}
主要实现方法:
boolean supports(Object handler);
以及方法:
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
supports方法获取该适配器是否支持。
handle方法进行主要逻辑处理,将request转换成某个方法进行调用后,返回ModelAndView。
GPModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
GPHandlerMapping handlerMapping = (GPHandlerMapping)handler;
//把方法的形参列表和request的参数列表所在顺序进行一一对应
Map paramIndexMapping = new HashMap();
//提取方法中加了注解的参数
//把方法上的注解拿到,得到的是一个二维数组
//因为一个参数可以有多个注解,而一个方法又有多个参数
Annotation[] [] pa = handlerMapping.getMethod().getParameterAnnotations();
for (int i = 0; i < pa.length ; i ++) {
for(Annotation a : pa[i]){
if(a instanceof GPRequestParam){
String paramName = ((GPRequestParam) a).value();
if(!"".equals(paramName.trim())){
paramIndexMapping.put(paramName, i);
}
}
}
}
//提取方法中的request和response参数
Class> [] paramsTypes = handlerMapping.getMethod().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);
}
}
//获得方法的形参列表
Map params = request.getParameterMap();
//实参列表
Object [] paramValues = new Object[paramsTypes.length];
for (Map.Entry parm : params.entrySet()) {
String value = Arrays.toString(parm.getValue()).replaceAll("\\[|\\]","")
.replaceAll("\\s",",");
if(!paramIndexMapping.containsKey(parm.getKey())){continue;}
int index = paramIndexMapping.get(parm.getKey());
paramValues[index] = caseStringValue(value,paramsTypes[index]);
}
if(paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
int reqIndex = paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex] = request;
}
if(paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
int respIndex = paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[respIndex] = response;
}
Object result = handlerMapping.getMethod().invoke(handlerMapping.getController(),paramValues);
if(result == null || result instanceof Void){ return null; }
boolean isModelAndView = handlerMapping.getMethod().getReturnType() == GPModelAndView.class;
if(isModelAndView){
return (GPModelAndView) result;
}
return null;
}
该方法将ModelAndView处理成最后response。主要通过View.render方法渲染成前端能识别的字符串。
private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, GPModelAndView mv) throws Exception{
//把给我的ModleAndView变成一个HTML、OuputStream、json、freemark、veolcity
//ContextType
if(null == mv){return;}
//如果ModelAndView不为null,怎么办?
if(this.viewResolvers.isEmpty()){return;}
for (GPViewResolver viewResolver : this.viewResolvers) {
GPView view = viewResolver.resolveViewName(mv.getViewName(),null);
view.render(mv.getModel(),req,resp);
return;
}
}
至此,MVC整个已实现。
1、启动时,报错80端口被绑定。修改pom文件中启动的端口为81,启动成功。
2、发现返回中文直接乱码
3、发现action直接out到响应中,做一下处理,将out里面返回html格式