ControllerServlet(Servlet)
创建一个servlet,没有注解,原因是要用户自定义url
-
第一步:接收到request的URI到beanNameurl进行查找(使用 url|类的限定名 的查找)
注意:查找的时候要把url的前面的项目名称去掉,比如("/MyMVCFrameWork") -
如果查到url
- 说明是beanNameMapping这种方式的handlemapping,然后就获得字节码对象,创建对象,
- 判断是否实现了controller接口,如果没有就抛错
注意:controller接口定义了doGet(HttpServletRequest request,HttpServletResponse response)和doPost(HttpServletRequest request,HttpServletResponse response);
- 判断request.getMethod()是get还是post,创建接口引用调用doget和dopost方法,
问题:实现controller接口的意义,为什么第二种不需要实现
答:因为要创建接口调doget和dopost,不能做别的方法,如果没实现直接抛错了,做不了
-
如果没查到,说明是第二种annotationMethodMapping这种handlemapping
- 用第二种方式查找url(url在method里头) 没查到报错404
- 创建字节码对象,反射object对象,创建参数类型数组(大小等于参数数量)
- 拿到方法数组和方法名数组(paraclass构成是"方法名#方法的类型全限定名")
- 通过方法名和参数类型拿到方法getDeclaredMethod()(反射机制)
调用此方法的时候,方法名要传,参数类型传的时候可以直接写数组{“String.class”,“Integer.class”}也可以分开写,因为是可变参方法 “…types”
- 拿到参数的value数组,用类型和方法(包括request, response, paramTypes, paramNames) <==> 拿到调用方法所需的参数值数组
- 调用方法,有可能有的方法不需要传参,所以两种方案
- result=method.invoke(object, null);
- result=method.invoke(object, values);
- 判断是action属性是json还是jsp
- 如果是json,调用JSONObject.fromObject(result);
- 如果报异常,调用JSONArray.fromObject(result);
- 写到out.write里
- 如果是jsp或者其他比如String,相应解析
HandleMapping(final class)---->解析xml(剥洋葱…)
- 得到xml文件路径()
//得到绝对路径--前面有file:/六个字符,要去掉 String path=HandleMapping.class.getClassLoader().getResource("").toString().substring(6); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(new File(path+"config/urlmappings.xml"));//写死的这里
- 得到根节点----调用doc.getRootElement()方法
- 拿到根节点的所有孩子节点 <==> beans–>bean
- 所有的孩子(一个孩子就是表里一行数据)循环提取内容
- 先按照beanNameMapping HashMap<String,String>解析,如果’url’拿不到(getattribute(‘url’)==null就滚到第6点
- beanNameMapping这种表–>存着url 和 类的全限定名
- 拿到’url’的value值和’class’的value值,调用put方法,加到当前hashmap里面
- 按照annotationMethodMapping HashMap<String,MethodInfo>解析
- 这种表是 url | 类的全限定名(class) | 方法名 这种格式
- 拿到类的全限定名,即className('class’的value)<==>class
由于这种类型的url不在bean下,而在method下,所以这一步只能拿到类的全限定名(如com.yanqi.Demo)
- 创建list拿到所有孩子(方法)节点 <==> bean–>method
- 循环解析: 首先拿到’url’, ‘name’, ‘action’
因为该表的类型是跟着方法的url走的,每个方法都有不同的url
- 拿到所有的孩子节点(参数) <==> method–>param
- 建一个数组,循环把参数名(‘name’)和参数类型(如’java.lang.String’)添加进去用’#'隔开
- 创建方法对象(methodInfo),把’url’,‘className’,‘methodName’,‘paramsList’,'action’都set进去
- 调用按照annotationMethodMapping.put(key,value)加到当前hashmap里面
MethodInfo
- 方法对象,用于第二种handlemapping的value值
- 参数: url(调用servlet的时候), methodName(方法名), action(返回方式json解析还是jsp或者基本类型), className(类的全限定名), []params(参数数组)
MyController
- 接口类,实现doGet(HttpServletRequest request,HttpServletResponse response)和doPost(HttpServletRequest request,HttpServletResponse response)
- 意义:为了让第一种handlemapping使用的时候必须调doget()或者dopost(), 如果没有实现该接口,就不能使用调servlet()–这里直接抛错
MyMvcException extends Exception
- 自定义异常类,设置打印异常信息
- 这里主要用于ControllerServlet里验证第一种handlemapping的反射找对象之后验证该类是否实现了controller接口
RequestDataHandler
方法类,提供三个方法供ControllerServlet调用
- Object[] handlerRequest(HttpServletRequest request,HttpServletResponse response,Class paramTypes[] ,String[] paramNames) 把参数合起来转化为对象数组以供controller反射去调用方法
- 创建对象数组,长度为参数数组长度
- 调用checkInterface方法,判断是哪种类型的参数,比如request就是用request.getclass().getinterfaces()去调用checkInterface去判断,和response都要用实现接口去判断,session用request.getsession().getclass()
问题:为啥request这些要用接口判断
答:因为本来就是HttpRequest和response就是接口- 如果是普通类型,就直接判断type[ i]==String.class类似这样就可以了
- 如果是对象,调用toObject方法转为对象
- checkInterface(Class clz, Class interfaces[]) 判断是不是实现clz有没有实现接口,如果实现返回true
- Object toObject(Class clz, HttpServletRequest request) 转对象的,通过反射创建对象,打开权限set属性之后返回对象
但是要注意,这里是拿到所有request.getParameterNames();的参数名来赋值,所以参数名和请求报文里的变量名要一样才能实现成功赋值
两种handlemapping的结构图表
-
第一种handlemapping–beanNameMapping
URL 类的全限定名 test.do com.yanqi.Demo test2.do com.yanqi.Demo test3.do com.yanqi.Demo -
第二种handlemapping–annotationMethodMapping
URL 类的全限定名 方法名 test.do com.yanqi.Demo test() test2.do com.yanqi.Demo test2() test3.do com.yanqi.Demo test3()
两种handlemapping对应的xml配置
-
第一种handlemapping–beanNameMapping
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean url="test1.do" class="my.com.Test"></bean> <bean url="test2.do" class="my.com.Demo"></bean> </beans>
-
第二种handlemapping–annotationMethodMapping
<?xml version="1.0" encoding="UTF-8"?> <beans> <beann class="my.com.Login"> <method url="login.do" name="login"> <param name="sno">java.lang.String</param> <param name="owd">java.lang.String</param> </method> <method url="reg.do" name="reg"> <param name="sno">java.lang.String</param> <param name="pwd">java.lang.String</param> <param name="sname">java.lang.String</param> </method> </beann> </beans>