• 03-springMVC(上)


    基于 Spring4.x

     (ps:红色的字是表示学到的位置)

    在SpringMVC中,MVC三部分的作用如下:

    执行流程

    具体到执行流程上,SpringMVC主要依赖了HandlerMapping 处理器映射器、HandlerAdapter 处理器适配器以及 ViewReslover 视图解析器三个组件。

    SpringMVC执行流程示意图如下:

     

    二,用springmvc做helloworld测试,效果是网页里点击a标签之后控制台输出helloworld,并转到成功页面。
    第一步:导包。

     

    web
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <!--配置 DispatcherServlet-->
        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--配置 DispatcherServlet的一个初始化参数:配置springMVC 配置文件的位置和名称-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext.xml</param-value>
            </init-param>
            <!--也可以不通过contextConfigLocation来配文件,有默认初始化。
                公式为:/WEB-INF/<servlet-name>-servlet.xml
                本例中为:/WEB-INF/dispatcher-servlet.xml
                如果使用默认方法,那么写对配置文件的名字就可以不需要配置上面的初始化代码了。
            -->
        <!--值大于0时,在启动时就加载这个servlet,值是整数,1的优先级最高-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>dispatcher</servlet-name>
            <!--index.jsp发送的请求被拦截,再自动匹配@RequestMapping注解-->
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    配置 DispatcherServlet
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <!--配置自动扫描的包-->
        <context:component-scan base-package="hello1">
        </context:component-scan>
        <!--配置视图解析器:如何把handler方法返回值(本例中就是"success")解析为实际的物理视图-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/views/">
            </property>
            <property name="suffix" value=".jsp">
            </property>
        </bean>
    </beans>
    applicationContext.xml
    package hello1;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * Created by Zhuxiang on 2020/5/27.
     */
    @Controller
    public class HelloWorld {
        /**
         * 1.使用@RequestMapping注解来映射请求的url
         * 2.返回值会通过视图解析器为实际的物理视图,对于InternalResourceViewResolver视图解析器,
         *   会做如下的解析:通过prefix + returnVal +后缀得到物理视图,然后做转发操作。
         *   本例中会转发到 /web-int/views/success.jsp
         */
        @RequestMapping("/hello1")
        public String helloWorld(){
            System.out.println("helloWorld!");
            return "success";
        }
    }
    HelloWorld
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
      <a href="hello1">hello</a>
      </body>
    </html>
    index.jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    success page
    </body>
    </html>
    success.jsp

    按顺序看一遍,然后理一遍思路。index.jsp里一个a标签发出请求,由servlet拦截,通过DispatcherServlet连接到applicationContext.xml里。配置好注解和视图解析器,(HelloWorld类)再通过@RequestMapping注解,映射请求的url,通过视图解析器转发到相应的jsp里。

     ----------------------------------------------------

     

    package hello1;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * Created by Zhuxiang on 2020/5/27.
     */
    @RequestMapping("springmvc")
    @Controller
    public class SpringMVCTest {
        public static final String SUCCESS="success";
    
        /**
         * 1.@RequestMapping除了修饰方法还可以修饰类。
         * 2:
         * 1)类定义处:提供初步的请求映射信息。相对于 WEB 应用的根目录
         * 2) 方法处:提供进一步的细分映射信息。相对于类定义处的 URL。若
         * 类定义处未标注 @RequestMapping,则方法处标记的 URL 相对于
         * WEB 应用的根目录
         * @return
         */
        @RequestMapping("/testRequestMapping")
        public String testRequestMapping(){
            System.out.println("testRequestMapping");
            return  SUCCESS;
        }
    }
    SpringMVCTest
    index.jsp里写 <a href="springmvc/testRequestMapping">testRequestMapping</a>
    点击进入成功页面。
    还是这个类,继续test。
    method
        /**
         * 使用method属性来指定请求方式(重要)
         * @return
         */
        @RequestMapping(value = "testMethod",method = RequestMethod.POST)
        public String testMethod(){
            System.out.println();
            return SUCCESS;
        }
    SpringMVCTest
    index.jsp里写上 
    <form action="springmvc/testMethod" method="post">
    <input type="submit" value="submit">
    </form>
     params和headers了解一下
     /**
         * 了解就行用的不多:可以使用params 和 headers 来精确映射请求,params和 headers支持简单的表达式。
         * @return
         */
        @RequestMapping(value = "testParamsAndHeaders",params = {"username,age!=10"},headers = {"Accept-Language=zh-CN,zh;q=0.9"})
        public String testParamsAndHeaders(){
            System.out.println("testParamsAndHeaders");
            return SUCCESS;
        }
    SpringMVCTest

    index.jsp里写上 

    <%-- 用params 和 headers--%>
    <a href="springmvc/testParamsAndHeaders?username=小明&age=11">testParamsAndHeaders</a>
    <br/>

     

        /**
         * 使用@PathVariable 可以来映射URl中的占位符到目标方法的参数中。(重要)
         * @return
         */
        @RequestMapping("/testPathVariable/{id}")
        public String testPathVariable(@PathVariable("id") Integer id){
            System.out.println("testPathVariable"+id);
            return SUCCESS;
        }
        /**
         * 使用通配符,*这个地方可以放无限多字符(不常用,了解)
         * @return
         */
        @RequestMapping("/testAntPath/*/abc")
        public String testAntPath(){
            System.out.println("/testAntPath/*/abc");
            return SUCCESS;
        }
        /**
         * 了解就行用的不多:可以使用params 和 headers 来精确映射请求,params和 headers支持简单的表达式。
         * @return
         */
        @RequestMapping(value = "testParamsAndHeaders",params = {"username,age!=10"},headers = {"Accept-Language=zh-CN,zh;q=0.9"})
        public String testParamsAndHeaders(){
            System.out.println("testParamsAndHeaders");
            return SUCCESS;
        }
    SpringMVCTest
    <%--  使用@PathVariable占位符--%>
      <a href="springmvc/testPathVariable/1">testPathVariable</a>
    <%--  通配符--%>
      <a href="springmvc/testAntPath/1234abcd/abc">testAntPath</a>
    <%--  用params 和 headers--%>
      <a href="springmvc/testParamsAndHeaders?username=小明&age=11">testParamsAndHeaders</a>
      <br/>
    
      <form action="springmvc/testMethod" method="post">
        <input type="submit" value="submit">
      </form>
    index.jsp

    rest风格了解一下。

     

     

        <!--配置org.springframework.web.filter.HiddenHttpMethodFilter:可以把 post 请求转为 delete 或 put 请求-->
        <filter>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    web里加上
     /**
         * rest风格的url.(了解一下)
         * 以crud为例:            比较
         * 增:/order post
         * 改:/order/1 put        update?id=1
         * 查:/order/1 get        get?id=1
         * 删:/order/1 delete     delete?id=1
         * 如何发送 put 请求和 delete 请求呢?
         * 1.需要配置HiddenHttpMethodFilter
         * 2.需要发送post请求
         * 3.需要在发送post请求时携带一个name="_method"的隐藏域,值为 delete 或 put
         *
         * 如何得到id呢?用@PathVariable注解
         *
         * @param id
         * @return
         */
        @ResponseBody
        @RequestMapping(value = "/testRest/{id}",method = RequestMethod.PUT)
        public String testRest4(@PathVariable("id") Integer id){
            System.out.println("testRest PUT " +id);
            return SUCCESS;
        }
        @ResponseBody
        @RequestMapping(value = "/testRest/{id}",method = RequestMethod.DELETE)
        public String testRest3(@PathVariable("id") Integer id){
            System.out.println("testRest DELETE " +id);
            return SUCCESS;
        }
        @RequestMapping(value = "/testRest",method = RequestMethod.POST)
        public String testRest2(){
            System.out.println("testRest POST ");
            return SUCCESS;
        }
        @RequestMapping(value = "/testRest/{id}",method = RequestMethod.GET)
        public String testRest1(@PathVariable("id") Integer id){
            System.out.println("testRest GET " +id);
            return SUCCESS;
        }
    SpringMVCTest
    <form action="springmvc/testRest/1" method="post">
        <input type="hidden" name="_method" value="PUT">
        <input type="submit" value="testRest PUT">
      </form>
      <br/>
      <form action="springmvc/testRest/1" method="post">
        <input type="hidden" name="_method" value="DELETE">
        <input type="submit" value="testRest DELETE">
      </form>
      <br/>
      <form action="springmvc/testRest" method="post">
        <input type="submit" value="testRest POST">
      </form>
      <br/>
      <a href="springmvc/TestRest/1"></a>
      <br/>
    index.jsp

     ------------------------------------------------------

     

    @RequestParam(重点看)

        /**
         * 了解就行
         * @CookieValue作用是映射一个cookie值。
         * 属性同RequestParam
         */
        @RequestMapping("/testCookieValue")
        public String testCookieValue(@CookieValue(value = "JSESSIONID") String session){
            System.out.println("testCookieValue "+session);
            return SUCCESS;
        }
        /**
         * 了解就行
         * @RequestHeader作用是映射请求头信息
         * 用法同RequestParam
         */
        @RequestMapping("/testRequestHeader")
        public String testRequestHeader(@RequestHeader(value = "Accept-Language") String al){
            System.out.println("testRequestHeader "+al);
            return SUCCESS;
        }
        /**
         *@RequestParam来映射请求参数。(重要)
         * value 值即请求参数的参数名
         * required 该参数是否必须,默认是true
         * defaultValue 请求参数的默认值
         */
        @RequestMapping(value = "/testRequestParam")
        public String testRequestParam(@RequestParam(value = "username") String un,@RequestParam(value = "age",required = false) Integer age){
            System.out.println("testRequestParam  username="+un+" age="+age);
            return SUCCESS;
        }
    SpringMVCTest
      <a href="springmvc/testCookieValue">testCookieValue</a>
      <a href="springmvc/testRequestHeader">testRequestHeader</a>
      <a href="springmvc/testRequestParam?username=小明&age=13">testRequestParam</a>
    index.jsp

     Spring MVC 会按请求参数名和 POJO 属性名进行自动匹 配,自动为该对象填充属性值。支持级联属性。(重要)

      <form action="springmvc/testPOJO" method="get">
        username:<input type="text" name="username">
        password:<input type="password" name="password">
        city:<input type="text" name="address.city">
        province:<input type="text" name="address.province">
        <input type="submit" value="submit">
      </form>
    index.jsp
        @RequestMapping("/testPOJO")
        public String testPOJO(User user){
            System.out.println(user);
            return SUCCESS;
        }
    SpringMVCTest

     -----------------------------------------------------------------------------------

    MVC 的 Handler 方法可以接受 哪些 Servlet API 类型的参数

    • HttpServletRequest

    • HttpServletResponse

    • HttpSession

    • java.security.Principal

    • Locale

    • InputStream

    • OutputStream

    • Reader

    • Writer

    在上面的SpringMVCTest这个handle里加上下面代码(以HttpServletRequest,HttpServletResponse为例)

        @RequestMapping("/testServletAPI")
        public String testServletAPI(HttpServletRequest request, HttpServletResponse response){
            System.out.println(request+" "+response);
            return SUCCESS;
        }
    jsp页面里加上
    <a href="springmvc/testServletAPI">testServletAPI</a>
    --------------------------------------

     下面是四种方法:

     

    第一种方法:ModelAndView

    SpringMVCTest这个handle里加上下面代码

        /**
         * 返回值可以是 ModelAndView 类型。
         * 其中可以包含视图和模型信息。
         * springMVC 会把 ModelAndView 的 model中数据放入到 request 域对象中。可以查看源码。
         * @return
         */
        @RequestMapping("/testModelAndView")
        public ModelAndView testModelAndView(){
            String viewName="success";
            ModelAndView mav = new ModelAndView(viewName);
            //添加模型数据到 ModelAndView 中。
            mav.addObject("time",new Date());
            return mav;
        }

    index.jsp页面里加上:
      <a href="springmvc/testModelAndView">testModelAndView</a>
    success.jsp加上:  
      time: ${requestScope.time}
    -------------------------------

    第二种方法: Map 和 Model
    SpringMVCTest这个handle里加上下面代码
        /**常用,重点
         * 目标方法可以添加 Map 类型(实际上也可以是 model 类型 或 ModelMap 类型(了解一下))的参数
         * @param map
         * @return
         */
        @RequestMapping("/testMap")
        public String testMap(Map<String,Object> map){
            //实际传入的是 org.springframework.validation.support.BindingAwareModelMap
            System.out.println(map.getClass().getName());
            map.put("names", Arrays.asList("Tom","Jerry","Mike"));
            return SUCCESS;
        }
    index.jsp页面里加上:
      <a href="springmvc/testMap">testMap</a>
    success.jsp加上:
       names :${requestScope.names}
     
    @SessionAttributes 注解
    /**
     * @SessionAttributes注解
     * 除了可以通过属性名指定需要放到会话中的属性外,(如:value = {"user"})
     * 还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。(如:types = {String.class})
     * 所以,下面的结果就是,叫 user的类和 值是string类型的类都会到 Session域里。
     *
     * 注意:该注解只能放在类的上面,而不能修饰方法。
     */
    @SessionAttributes(value = {"user"},types = {String.class})
    @RequestMapping("springmvc")
    @Controller
    public class SpringMVCTest {
        public static final String SUCCESS="success";
    
        @RequestMapping("/testSessionAttributes")
        public String testSessionAttributes(Map<String,Object> map){
            User user = new User("xiaoming","123");
            map.put("user",user);
            map.put("spring","atguigu");
            return SUCCESS;
        }
     
    success.jsp加上:
      request user :${requestScope.user}
      session user :${sessionScope.user}
      </br>
      string:${sessionScope.spring}
      </br>
    ---------------------------------------------
    第四种方法:
    @ModelAttribute 注解
    一般使用场景:

     -----------------------------------------------------
    解决上面的问题。
        /**
         * 由@ModelAttribute 标记的方法,会在每个目标方法执行之前被springmvc调用!
         * 
         */
        @ModelAttribute
        public void getUser(@RequestParam(value = "id",required = false) Integer id,
                            Map<String,Object> map){
            if(id!=null){
                User tom = new User("Tom", "123");
                System.out.println("从数据库中获取一个对象:"+tom);
                map.put("user",tom);
            }
        }
    
        /**
         * 运行流程:
         * 1.执行 @ModelAttribute 注解修饰的方法:从数据库中取出对象,把对象放入到了map中,键为:user
         * 2.SpringMVC 从 Map中取出 User对象,并把表单的请求参数赋给该 User对象的对应属性。
         * 3.SpringMVC 把上述对象传入目标方法的参数。
         * 
         * 注意:在@ModelAttribute修饰的方法中,放入到 map 时的键需要和
         * 目标方法入参类型的第一个字母小写的字符串一致。
         */
        @RequestMapping("/testModelAttribute")
        public String testModelAttribute(User user){
            System.out.println("修改 " +user);
            return SUCCESS;
        }
      <!--
          模型修改操作:
          1.原始数据为:1,Tom,123
          2.密码不能被修改。
          3.表单回显,模拟操作直接在表单填写对应的属性值。
      -->
      <form action="springmvc/testModelAttribute" method="post">
        <input type="hidden" name="id" value="1">
        username:<input type="text" name="username" value="Tom">
        <input type="submit" value="submit">
      </form>

    结果:
        从数据库中获取一个对象:User{username='Tom', password='123'}
        修改 User{username='Tom', password='123'}

    ---------------------------------------------------------------

     搞一下源码,主要看一下思路。(了解一下)

      //看了源码之后对上面内容的补充
      /**
         * 1.(重要!)由@ModelAttribute 标记的方法,会在每个目标方法执行之前被springmvc调用!
         * 2. (了解)@ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
         * 1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
         * 2). SpringMVC 会以 value 为 key, POJO 类型的对象为 value, 存入到 request 中.
         */
    
        //看了源码之后对上面内容的补充
        /**
         * 运行流程:(使用为目的)
         * 1.执行 @ModelAttribute 注解修饰的方法:从数据库中取出对象,把对象放入到了map中,键为:user
         * 2.SpringMVC 从 Map中取出 User对象,并把表单的请求参数赋给该 User对象的对应属性。
         * 3.SpringMVC 把上述对象传入目标方法的参数。
         *
         * 注意:在@ModelAttribute修饰的方法中,放入到 map 时的键需要和
         * 目标方法入参类型的第一个字母小写的字符串一致。
         *
         * SpringMVC 确定目标方法 POJO 类型入参的过程(结论)
         * 1. 确定一个 key:
         * 1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
         * 2). 若使用了  @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值.
         * 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
         * 1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到.
         * 3(需要多看看). 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰,
         * 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所
         * 对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常.
         * 4. 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则
         * 会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数
         * 5. SpringMVC 会把 key 和 POJO 类型的对象(value)保存到 implicitModel 中, 进而会保存到 request 中.
         *
         *
         *
         *
         *看了源码之后对上面内容的补充
         *
         * 源代码分析的流程(看懂源码)
         * 1. 调用 @ModelAttribute 注解修饰的方法. 实际上把 @ModelAttribute 方法中 Map 中的数据放在了 implicitModel 中.
         * 2. 解析请求处理器的目标参数, 实际上该目标参数来自于 WebDataBinder 对象的 target 属性
         * 1). 创建 WebDataBinder 对象:
         * ①. 确定 objectName 属性: 若传入的 attrName 属性值为 "", 则 objectName 为类名第一个字母小写.
         * *注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为 @ModelAttribute
         * 的 value 属性值
         *
         * ②. 确定 target 属性:
         *     > 在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok
         *     > *若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中
         * 获取 attrName 所对应的属性值. 若 session 中没有对应的属性值, 则抛出了异常.
         *     > 若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key
         * 和 attrName 相匹配, 则通过反射创建了 POJO 对象
         *
         * 2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性.
         * 3). *SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel.
         * 近而传到 request 域对象中.
         * 4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参.
         */

    具体debug。

    debug小帮助:

    1:Step Over ,进入下一步,如果是方法,那就直接跳过(F8)

    2:Step Into,进入下一步,如果是方法,就进入方法内部,但是不会进入jdk封装的方法。(F7)

    3:Force Step Into:强制进入下一步,不管是什么方法,即使是jdk封装的方法,也会进入。(Alt+Shift+F7)

    4:Step Out:跳转到下一个断点,没有一直运行到最后。(Shift+F8)

    5: Run to Cursor:运行到光标处 (Alt+F9)

    一,上面getUser方法里map放入键和值之后springMVC源码是如何把值传入testModelAttribute方法里?

    (思路:“放入从数据库中获取的值” 这一步打上断点,“获得这个被修改后的值” 这一步打上断点。)

                 ||  经历了什么?

                 ||  把值给到。

     

     二,debug开始,①经过了众多的类与方法到②,找到其中对值进行储存的类和方法。

    (思路:“找到储存方法”打上断点,“后面一行代码,用于判断前一行是否储存了值” 打上断点)

     在③中,我们发现其中一个属性叫implicitModel(隐式的model)为空。

     

    在④中,我们发现这个属性有值。

    三,③的方法需要两个参数,重点是args,看看它储存了什么,又是如何储存的。

     点开这个方法,找到储存值的步骤。如果方法太多不方便找,就在User类的setUsername方法里打上断点。然后点前面几步就可以找到了。

    args还是null

     

     args已经有值了,表单提交修改的值和从数据库里得到没被修改的值都有。

    四,⑦看binder是如何创建的,getTarget方法又是什么?

     找到返回binder的类:

     再找创建方法:

     

    进入,看源码。

     

     发现⑧的参数里bindObject就是上图的target,name就是上图的objectName

    找到 name 和 bindObject 是如何赋值的。

    name:通过下面的方法得到类的类名,第一个字母小写。

    解释说明一下:

    如果上面得到的name=implicitModel里的,那么把属性值给bindObject,

    否则,从session域里找键为name的,如果有,把它的值给bindObject,

    如果没有,抛出异常。

    dobind方法里的binder属性就是储存经过表单提交修改后的值,最后加入到args里了。

    最后,由这个方法把 target 和 objectName 给implicitModel

     

    ---------------------------------------------------------------

     

     

     

     

    下面的了解一下。

     

     下面的了解一下。

     

     <!--在实际开发中通常都需配置 mvc:annotation-driven标签。因为加上上面标签之后,以前的@requestMapping就都404了-->

    <mvc:annotation-driven></mvc:annotation-driven>

    ----------------------------重点掌握下面的-----------------------------

     

     SpringMVCTest类里加上。

    @RequestMapping("/testRedirect")

    public String testRedirect(){

    System.out.println(testRedirect);

    return "redirect:index.jsp";

    }

    index.jsp加上

    <a href="testRedirect">testRedirect</a>

    效果:点了a标签,重定向到index.jsp

  • 相关阅读:
    Selenium:下拉框处理(Select模块)
    Selenium:三种等待方式详解
    Fiddler断点调试
    Fiddler修改请求参数
    Jenkins邮件配置
    转-web自动化测试,定位不到元素的原因及解决方案
    Allure--自动化测试报告生成
    jenkins--Jenkins+Git+coding+maven 实现自动化测试持续集成
    8种定位方式以及等待详解
    java String、StringBuilder
  • 原文地址:https://www.cnblogs.com/zhuxiang1029/p/12970344.html
Copyright © 2020-2023  润新知