• 原来浏览器发送请求到服务器
      服务器接收请求
      参考web.xml 或 @WebServlet 找到请求与controller之间的映射关系
      现在浏览器发送请求到服务器
      服务器接收请求
      将请求交给web框架
      框架根据一些请求映射关系,找到与之匹配的controller实现映调用。
      正因为服务器现在将请求交给了web框架
      所以才能在框架中完成一些常用功能的封装
      1. 多个请求,可以访问同一个controller对象的不同方法 (请求映射)。
      2. 框架负责接收参数(普通参数,文件参数)
      3. 框架负责传递参数 (类型转换)
      【具体业务实现 - 业务程序实现】
      4. 框架负责响应
      直接响应
      间接响应
      转发(携带数据)
      重定向

      编码实现
      1. DispatcherServlet
      按照上面的分析
      有了web框架以后
      浏览器给服务器的请求,服务器会交给框架
      反之也就是说框架可以接收服务器的请求
      从语法而言,可以接收服务器请求的组件:Servlet , Filter
      所以框架需要一个请求入口
      补充:
      /
      一进来就交给框架
      / 是先看看有没有处理他的,没有再交给框架
    1. 请求映射的配置
      现在请求都交给了框架
      所以需要框架实现请求映射
      哪个请求对应哪个controller的方法需要业务程序员按照框架的要求配置
      暂时有2种规范
      * 第1种,在xml文件中配置映射关系
      在web.xml中配置DispatcherServlet时
      通过设置servlet的参数指定框架的配置文件名称(默认要求配置文件必须放在src目录下)
      1. <!--web.xml-->
      2. <servlet>
      3. <servlet-name>mvc</servlet-name>
      4. <servlet-class>org.duyi.web7.DispatcherServlet</servlet-class>
      5. <init-param>
      6. <param-name>classpath</param-name> <!-- 用来指定配置文件名字的从参数固定的 -->
      7. <param-value>mvc.xml</param-value> <!-- 配置文件名子可以自定义 -->
      8. </init-param>
      9. </servlet>

    补充:
    context-param和init-param区别
    web.xml里面可以定义两种参数:
    (1)application范围内的参数,存放在servletcontext中,在web.xml中配置如下:

    context/param
    avalible during application

    (2)servlet范围内的参数,只能在servlet的init()方法中取得,在web.xml中配置如下:

    MainServlet
    com.wes.controller.MainServlet

    param1
    avalible in servlet init()

    0

    详情见:https://www.cnblogs.com/tiancai/p/9305381.html

    1. * 人为规定xml中的映射关系配置如下:
    1. <!--mvc.xml-->
    2. <?xml version="1.0" ?>
    3. <mvc>
    4. <!--
    5. 浏览器发送的/test1请求,由TestController类的t1方法处理
    6. -->
    7. <mapping path="/test1" class="com.controller.TestController" method="t1"></mapping>
    8. <mapping path="/test11" class="com.controller.TestController" method="t1">
    9. <type>int</type>
    10. </mapping>
    11. <mapping path="/test2" class="com.controller.TestController" method="t2">
    12. <!--引用数据类型需要指明全路径-->
    13. <type>java.lang.String</type>
    14. <type>com.domain.Car</type>
    15. </mapping>
    16. <mapping path="/test3" class="com.controller.TestController" method="t3" ></mapping>
    17. </mvc>

    * 第2种,在注解中配置映射关系
    框架自定义*@RequestMapping
    约定优于配置

    1. package org.duyi.web7.annotation;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. /**
    7. * 配置请求映射关系
    8. * 指定哪个请求对应哪个controller的方法
    9. * 作用在哪个方法上,就表示映射哪个方法
    10. * value属性用来设置请求路径
    11. */
    12. @Target(ElementType.METHOD)
    13. @Retention(RetentionPolicy.RUNTIME) //设置存活时间,在运行时用
    14. public @interface RequestMapping {
    15. public String value();
    16. }
    1. * 该注解作用controller的方法上,表示请求与方法的映射关系<br /> @RequestMapping("/test2")<br /> public void t2(String s , Car car){}<br /> * 之前可以从xml文件中获得请求映射关系<br /> * 现在需要从注解中获得映射关系<br /> * 要想找到注解,就需要找到方法,需要找到controller类(反射)<br /> * 为了更方便的找到controller类<br /> 1. web.xml文件中,指定寻找controller的基本包位置
    1. <-- 参数,在web.xml里 -->
    2. <init-param>
    3. <param-name>controller-scan</param-name>
    4. <param-value>com.controller,com.controller2</param-value>
    5. </init-param>
    1. 2. 框架自定义@Controller <br />目的:降低扫描类的工作量<br /> 该注解作用在controller类上<br /> 具有该注解的controller类,才会通过反射寻找RequestMapping
    1. package org.duyi.web7.annotation;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. /**
    7. * 作用在controller类上,表示这个类中有方法可以实现请求,可以扫描
    8. */
    9. @Target(ElementType.TYPE)
    10. @Retention(RetentionPolicy.RUNTIME)
    11. public @interface Controller {
    12. }
    1. <--web.xml-->
    2. <?xml version="1.0" encoding="UTF-8"?>
    3. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    5. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    6. version="4.0">
    7. <servlet>
    8. <servlet-name>mvc</servlet-name>
    9. <servlet-class>org.duyi.web7.DispatcherServlet</servlet-class>
    10. <init-param>
    11. <param-name>classpath</param-name>
    12. <param-value>mvc.xml</param-value>
    13. </init-param>
    14. <init-param>
    15. <param-name>controller-scan</param-name>
    16. <param-value>com.controller,com.controller2</param-value>
    17. </init-param>
    18. <load-on-startup>1</load-on-startup>
    19. </servlet>
    20. <servlet-mapping>
    21. <servlet-name>mvc</servlet-name>
    22. <url-pattern>/</url-pattern>
    23. </servlet-mapping>
    24. </web-app>
    1. 初始化映射关系
      有了框架以后
      所有请求就会交给框架
      框架会根据映射关系,最终调用controller的指定方法
      映射关系可以使用xml配置,也可以使用注解配置
      每次请求映射时都需要读取一次配置信息么?
      当然不需要
      只需在服务器启动时读取一次,并存储缓存即可
      static{}
      servlet.init
      在servlet(单例)创建的时候,创建的初始化方法(只创建一次)
      配置信息来自于两个部分
      配置了5个部分 ```java package org.duyi.web7;

    import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader;

    import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map;

    public class DispatcherServlet extends HttpServlet {

    1. /*
    2. 缓存请求映射信息
    3. key = "/test1"
    4. value = mappingInfo{目标对象,目标方法}
    5. */
    6. //需要一个缓存
    7. Map<String,MappingInfo> mappingInfoMap = new HashMap<>();
    8. /**
    9. * 读取各种配置信息
    10. * (目前只有请求映射信息)
    11. * @throws ServletException
    12. */
    13. @Override
    14. public void init() throws ServletException {
    15. //目前配置信息来自于两个部分,而且两个部分都可能存在
    16. //读配置文件
    17. String xmlPath = super.getInitParameter("classpath");
    18. if(xmlPath != null && !"".equals(xmlPath)){
    19. //指定了配置文件,需要读取配置文件
    20. readXml(xmlPath);
    21. }
    22. //读注解
    23. // 包路径 一个controller扫描
    24. String packagePath = super.getInitParameter("controller-scan") ;
    25. if(packagePath != null && !"".equals(packagePath)){
    26. //指定了包路径,需要读取包下类中的注解信息
    27. }
    28. }
    29. //读取xml配置文件
    30. public void readXml(String path){
    31. //path="mvc.xml"
    32. //需要得到的路径是:
    33. // D:\ideaworkspace2\web-demo\out\artifacts\web_demo_war_exploded\WEB-INF\classes\mvc.xml
    34. path = Thread.currentThread().getContextClassLoader()
    35. .getResource(path).getPath();
    36. // 可以直接写这个路径,但是这个路径不是固定的
    37. //其实找的不是src目录下的,而是classes下的
    38. //new File(path) 的相对路径通常是在web_demo_war_exploded目录下
    39. // 找mvc.xml,是找不到的,而且名字也可能会改,所以不建议用这个
    40. try {
    41. // 读的是字节
    42. InputStream is = new FileInputStream(path) ;
    43. //使用dom,sax读取xml内容
    44. // 解析只有sax和dom,原生解析方式
    45. //sax读写器
    46. SAXReader reader = new SAXReader() ;
    47. //读到一个文档
    48. Document document = reader.read(is) ;
    49. //解析mapping标签
    50. parseMappingElement(document);
    51. //解析其他标签
    52. } catch (FileNotFoundException e) {
    53. e.printStackTrace();
    54. } catch (DocumentException e) {
    55. e.printStackTrace();
    56. } catch (IllegalAccessException e) {
    57. e.printStackTrace();
    58. } catch (InstantiationException e) {
    59. e.printStackTrace();
    60. } catch (ClassNotFoundException e) {
    61. e.printStackTrace();
    62. } catch (NoSuchMethodException e) {
    63. e.printStackTrace();
    64. }
    65. }
    66. //读取xml中<mapping>请求映射信息
    67. private void parseMappingElement(Document document) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException {
    68. //按照标签的嵌套规则,找到需要的所有mapping标记
    69. // mapping标记 一堆标签(mvc.xml)
    70. List<Element> mappingElements = document.selectNodes("mvc/mapping") ;
    71. for(Element mappingElement : mappingElements ){
    72. /*
    73. 获得标签指定的属性值
    74. 标签中记载着请求与响应对应关系
    75. 此时(初始化)获得这个对应关系,不是次使用
    76. 后面请求的时候用
    77. 所以需要存储起来,组成对象
    78. */
    79. String path = mappingElement.attributeValue("path") ;
    80. String classname = mappingElement.attributeValue("class") ; // com.controller.TestController
    81. String methodname = mappingElement.attributeValue("method") ; // t1
    82. //在请求映射的时候,需要的是类对象、方法,而不仅仅是一个名字
    83. // 所以根据反射创建目标对象
    84. Class clazz = Class.forName(classname) ;
    85. Object controller = clazz.newInstance() ;
    86. //通过反射,根据方法名获得方法对象 t1() , t2(int , String)
    87. //获得mapping标签的子标签<type> 存储目标方法对应的参数列表
    88. List<Element> typeElements = mappingElement.selectNodes("type") ;
    89. //创建一个与配置文件中<type>数量相同的Class数组,准备装载目标方法对应的参数列表类型
    90. // 将其变成一个可变数组传进去 其长度与类型长度一致
    91. Class[] types = new Class[typeElements.size()] ;
    92. int i = 0 ;
    93. for(Element typeElement : typeElements){
    94. //<type> 获得标签的文本内容 "int"
    95. String typeStr = typeElement.getText() ;
    96. //"int" -> int.class 将字符串变成int.class
    97. //Class.forName("int"); --> 不行
    98. // 索引位置 找小弟判断
    99. types[i++] = castStringToClass(typeStr) ;
    100. }
    101. //通过class类型就是寻找方法所对应的类型列表
    102. // 而这个就是对应的方法 可变参数列表
    103. // 而这个方法可能存在重载,所以除了方法名字外,还需要类型
    104. Method method = clazz.getMethod(methodname,types ) ;
    105. // 组成对象
    106. MappingInfo info = new MappingInfo(path,controller,method) ;
    107. //将一个mappingInfo标签的信息就存成一个具体的对象
    108. mappingInfoMap.put(path,info) ;
    109. }
    110. }
    111. /*
    112. * 将字符串表示的类型转换成对应的Class类型
    113. * "int" -> int.class
    114. * "java.lang.String" -> String.class -> Class.forName("java.lang.String")
    115. */
    116. private Class castStringToClass(String typeStr) throws ClassNotFoundException {
    117. if("int".equals(typeStr)){
    118. return int.class;
    119. }
    120. if("long".equals(typeStr)){
    121. return long.class;
    122. }
    123. if("double".equals(typeStr)){
    124. return double.class;
    125. }
    126. //除去8种基本类型,以外的都是引用类型,写的都是全名,反射生成Class , java.lang.Integer包装类
    127. //Class c = Class.forName("java.lang.String");
    128. //Class c2 = String.class ; 类型.属性
    129. //Class c3 = 对象.getClass方法
    130. return Class.forName(typeStr) ;//最终产生的是class对象的方法
    131. }
    132. //读取注解
    133. public void readAnnotation(){
    134. }
    135. @Override
    136. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    137. //请求映射,根据请求调用controller的具体方法
    138. //获得请求 /test1
    139. //根据请求找到对应的mappinginfo 映射信息
    140. //获得参数
    141. //处理参数
    142. //调用mappinginfo中的对象的方法,传递参数
    143. }

    }

    1. ```java
    2. <--mvc.xml-->
    3. <?xml version="1.0" ?>
    4. <mvc>
    5. <!--
    6. 浏览器发送的/test1请求,由TestController类的t1方法处理
    7. -->
    8. <--无参-->
    9. <mapping path="/test1" class="com.controller.TestController" method="t1"></mapping>
    10. <--有参-->
    11. <mapping path="/test11" class="com.controller.TestController" method="t1">
    12. <type>int</type>
    13. <type>java.lang.String</type>
    14. </mapping>
    15. <!--在web.xml中配置也可-->
    16. <controller-scan package="com.controller,com.controller2"></controller-scan>
    17. </mvc>
    1. package org.duyi.web7;
    2. import java.lang.reflect.Method;
    3. /**
    4. * 存储请求映射关系
    5. */
    6. public class MappingInfo {
    7. private String path ;// /test1 请求命令
    8. private Object controller ; // TestController 目标对象
    9. private Method method ;// t1() 目标方法 方法对象,自然包括对象(如果有参数,就有)
    10. public MappingInfo() {
    11. }
    12. public MappingInfo(String path, Object controller, Method method) {
    13. this.path = path;
    14. this.controller = controller;
    15. this.method = method;
    16. }
    17. public String getPath() {
    18. return path;
    19. }
    20. public void setPath(String path) {
    21. this.path = path;
    22. }
    23. public Object getController() {
    24. return controller;
    25. }
    26. public void setController(Object controller) {
    27. this.controller = controller;
    28. }
    29. public Method getMethod() {
    30. return method;
    31. }
    32. public void setMethod(Method method) {
    33. this.method = method;
    34. }
    35. }

    补充:
    将集合转成字符串(JSON)
    fastjson阿里巴巴,gson谷歌,json-lib,jackson — StringMVC底层使用的方式

    1. 配置文件
      * 目前只有mapping请求映射信息
      2. 注解