JAVA框架——SpringMVC总结(一)(SpringMVC执行过程,视图解析器,请求和接受问题,接受参数问题,返回结果问题)

本文介绍了SpringMVC的基本概念、处理流程、配置步骤、参数接收和视图解析,包括DispatcherServlet的初始化、Controller的创建与映射、ModelAndView的使用和Ajax响应处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、SpringMVC简介

SpringMVC:是基于spring的一个框架, 实际上就是spring的一个模块, 专门是做web开发的。可以理解为servlet的一个升级

  • web开发底层是servlet , 框架是在servlet基础上面加入一些功能,做web开发方便。

  • SpringMVC能够创建对象, 放入到SpringMVC容器, SpringMVC容器中放的是控制器对象。

  • 我们使用@Contorller创建控制器对象, 把对象放入到SpringMVC容器中, 把创建的对象作为控制器使用,这个控制器对象能接收用户的请求, 显示处理结果。

二、SpringMVC处理流程

在这里插入图片描述

SpringMVC中有一个对象是Servlet : DispatherServlet(中央调度器)

  • DispatherServlet: 负责创建springmvc容器对象,读取xml配置文件,创建文件中的Controller对象

  • DispatherServlet负责接口用户的请求,分派给自定义的Controller对象

三、SpringMVC快速入门案例

第一步:web.xml中配置前端控制器

注册SpringMVC核心对象DispatcherServlet
    需要在tomcat服务器启动后,创建DispatcherServlet对象实例
    为什么要创建DispatcherServlet对象实例呢?
    因为DispatcherServlet在他的创建过程中,会同时创建springmvc容器对象
    读取springmvc的配置文件,把这个配置文件中的对象都创建好,当用户发起请求时就可以直接使用对象了

    Servlet的初始化会指定init()方法,DispatcherServlet在init()中{
        //创建容器,读取配置文件
        WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
        //把容器对象放到ServletContext中
        getServletContext.setAttribute(key,ctx)
    }
<?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">

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--自定义springmvc读取的配置文件的位置-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <!--指定自定义文件的位置-->
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--在tomcat启动就会创建Servlet对象-->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

</web-app>
    使用框架的时候,url-pattern可以使用两种值
        1.使用扩展名方式,语法: *.xxxx,xxxx是自定义的扩展名,常用方式 
        *.do  | *.action  | *.mvc等
        2.使用斜杠"/"

第二步:配置applicationContext.xml中配置注解扫描器

<!--声明组件扫描器-->
    <context:component-scan base-package="com.aiit.controller"/>

第三步:书写控制器类

 @RequestMapping:请求映射,作用是把一个请求地址和一个方法绑定在一起
                一个请求指定一个方法处理
     属性:1.value是一个String,标识请求的uri地址的
               value的值必须是唯一的,不能重复。在使用时,推荐以“/”开头
     位置:1.在方法的上面,常用的
          2.在类的上面

       说明:使用RequestMapping修饰的方法叫做处理器方法或者控制器方法
	      使用@RequestMapping修饰的方法可以处理请求的,类似Sevlet种的doGet,doPost
	
         返回值:ModelAndView
	      Model:数据,请求处理完成后,要显示给用户的数据
	      View:视图,比如jsp等
/*
* @Controller创建处理器对象,对象放在Springmvc容器中
* 位置在类上
* 和spirng中的@Service ,@Component
*
* 能处理请求的都是控制器(处理器):MyController能处理请求,叫做后端控制器
* */
@Controller
public class MyController {
    //处理用户提交的请求,springmvc中式使用方法来处理的
    //方法式自定义的,可以有多种返回值,多种参数,方法名称自定义
    @RequestMapping( value = "/some.do")
    public ModelAndView doSome(){   //doGet——调用service请求处理
        //处理some.do请求,相当于service调用处理完成了
        ModelAndView modelAndView = new ModelAndView();
        //添加数据,框架在请求的最后把数据放到request作用域
        //request.setAttribute
        modelAndView.addObject("msg","欢迎使用springmvc做web开发");
        modelAndView.addObject("fun","指定doSome方法");
        //指定视图。指定视图的完整路径
        //框架对视图指定的forward操作
        //request.getRequestDispatcher("/show.jsp").forward(request,response);
        modelAndView.setViewName("/show.jsp");
        //返回modelAndView
        return modelAndView;
    }

}

第四步:书写两个jsp页面

<body>
    <h1>第一个springmvc项目</h1>
    <a href="some.do">发起some.do的请求</a>
</body>

<body>
    <h1>show.jsp</h1>
    <h6>msg数据:${msg}</h6>
    <h3>fun数据:${fun}</h3>
</body>

效果如下:
在这里插入图片描述

四、SpringMVC执行过程源码分析

1.tomcat启动,创建容器的过程

  • 通过<load-on-start>标签指定的1,创建DisaptcherServlet对象, DisaptcherServlet它的父类是继承HttpServlet的, 它是一个serlvet, 在被创建时,会执行init()方法。

  • 在init()方法中

//创建容器,读取配置文件
    WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
    //把容器对象放入到ServletContext中
    getServletContext().setAttribute(key, ctx);
  • 上面创建容器作用: 创建@controller注解所在的类的对象, 创建MyController对象,

  • 这个对象放入到 springmvc的容器中, 容器是map , 类似 map.put(“myController”,MyController对象)

2.请求的处理过程

执行servlet的service()

protected void service(HttpServletRequest request, HttpServletResponse response)

protected void doService(HttpServletRequest request, HttpServletResponse response)

  DispatcherServlet.doDispatch(request, response){

      调用MyController的.doSome()方法
  }

doDispatch:springmvc中DispatcherServlet的核心方法, 所有的请求都在这个方法中完成的

五、视图解析器

当我们不想用户通过url去访问我门一些网站的时候,我们可以将一些页面放在WEB-INF目录下。但当我们重复使用一个WEB-INF,转发路径就需要重复去写。

applicationContext.xml文件

 <!--声明springmvc框架中的视图解析器,帮助开发人员人员设置设置视图文件的路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!--后缀:视图文件的扩展名-->
        <property name="suffix" value=".jsp"/>
    </bean>

控制器类

ModelAndView modelAndView = new ModelAndView();
        //添加数据,框架在请求的最后把数据放到request作用域
        //request.setAttribute
        modelAndView.addObject("msg","欢迎使用springmvc做web开发");
        modelAndView.addObject("fun","指定doSome方法");  
       // modelAndView.setViewName("WEB-INF/view/show.jsp")
        //当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
        modelAndView.setViewName("show");

六、请求方式问题&Http接受问题

  • @RequestMapping:注解属性

  • 属性:value:所有请求地址的公共部分,叫做模块名称

  • 位置:放在类的上面

主要放在一个类上面,做模块化处理的,比如我们在一个类上面上面定义用户user,那么该类均用当作用户处理的请求方式

  • @RequestMapping:请求映射

  • 属性:method:表示请求方式,RequestMethod.GET

  • 表示get请求方式,RequestMethod.GET

  • 表示post请求方式,RequestMethod.POST

  • 如果不是get方式,请求写的是post, 那么就会报405的错误

主要放在方法上面,这样可以指定一个方法的请求方式

  • 注意doSome方法,里面传入的参数就是HttpServlet,HttpResponse等
  • 在方法中,使用传入参数问题获获取。

主要运用参数传入的办法将HttpServlet,HttpResponse,HttpSession

@Controller
@RequestMapping("/user")
public class MyController {
    //指定some.do,使用get请求方式
    @RequestMapping( value = "/some.do" ,method = RequestMethod.GET)
    public ModelAndView doSome(HttpServletResponse response, HttpServletRequest request, HttpSession session){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","欢迎使用springmvc做web开发"+request.getParameter("name"));
        modelAndView.addObject("fun","指定doSome方法");
        modelAndView.setViewName("show");

        //返回modelAndView
        return modelAndView;
    }
    //如果不指定请求方式,就没有限制
    @RequestMapping( value = "/other.do" ,method = RequestMethod.POST)
    public ModelAndView doOther(){
        ModelAndView modelAndView = new ModelAndView();
        //request.setAttribute
        modelAndView.addObject("msg","欢迎使用springmvc做web开发");
        modelAndView.addObject("fun","指定doSome方法");
        //当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
        modelAndView.setViewName("other");

        //返回modelAndView
        return modelAndView;
    }
}

七、接受参数问题

1、逐个接受请求参数

  • 要求:处理器方法的形参名和请求中参数名必须保持一致

  • 同名的请求参数赋值给同名的形参

  • 直接通过参数问题来进行接受参数,注意参数

控制器类

public class MyController {
    /*
    *  框架接受请求参数
    *       1.使用request对象接受请求参数
    *           String strName = request.getParameter("name");
    *           String strAge = request.getParameter("age");
    *       2.springmvc框架通过DispatcherServlet调用MyController的doSome()方法
    *           调用方法时,按名称对应,把接受的参数赋值给形参
    *           doSome(strName,Integer.valueOf(strAge))
    *           框架会提供类型转换功能,能把String转换为int,long...
    *
    *       如果值对不上会出现400的问题。形参接受不正确
    * */
    @RequestMapping( value = "/receiveProperty.do" )
    public ModelAndView doSome(String name,int age){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("myname",name);
        modelAndView.addObject("myage",age);
        modelAndView.setViewName("show");
        return modelAndView;
    }

}

jsp页面

<form action="receiveProperty.do" method="post">
        姓名:<input type="text" name="name"><br/>
        年龄:<input type="text" name="age"><br/>
        <input type="submit" value="提交参数">
    </form>

2、SpringMVC解决乱码问题

我们使用post请求方式,去传递参数的时候,尝尝会出现乱码问题,SpringMVC中提供了乱码的方式,我们需要在web.xml文件中进行配置过滤器即可。

 <!--注册声明过滤器,解决post请求乱码问题-->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!--设置项目中使用的字符编码-->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <!--强制请求对象(HttpServletRequest)使用encoding编码的值-->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <!--强制应答对象(HttpServletResponse)使用encoding编码的值-->
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

3.使用@RequestParam注解

当我们前端的表单与我们后端的接受参数的数据两个不相同时,我们可以使用@RequestParam去匹配相应的数据

表单

<h1>请求参数名和处理器方法的形参名不一样</h1>
    <form action="receiveParam.do" method="post">
        姓名:<input type="text" name="rename"><br/>
        年龄:<input type="text" name="rage"><br/>
        <input type="submit" value="提交参数">
    </form>

控制器类

 @RequestMapping( value = "/receiveParam.do" )
    public ModelAndView receiveParam(@RequestParam(value = "rename",required = false) String name,
                                     @RequestParam(value = "rage") int age){
        System.out.println(name +"  " +age);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("myname",name);
        modelAndView.addObject("myage",age);
        modelAndView.setViewName("show");
        return modelAndView;
    }
   请求中参数名和处理器方法的形参名不一样
   @RequestParam:解决请求中参数名形参名不一样的问题
       属性:1.value请求中的参数名称
           	 2. required是一个boolean。默认值是true
                   true:表示请求中必须包含此参数
       位置:在处理器方法的形参定义的前面
       如果我们设置成false,那么用户在不传入参数的情况下也能访问页面。

4.对象接受参数

首先我们需要 一个vo对象

  • 注意此处省去getter/setter方法

  • 注意此处属性名和参数名需要保持一致

public class Student {
    //要求:属性名和请求中参数名需要保持一致
    private String name;
    private Integer age;
}

表单页面

<h1>使用java对象接受请求参数</h1>
    <form action="receiveObject.do" method="post">
        姓名:<input type="text" name="name"><br/>
        年龄:<input type="text" name="age"><br/>
        <input type="submit" value="提交参数">
    </form>

控制器类

/*
    *   处理器方法形参是java对象,这个对象的属性名和请求中参数名一样的
    *   框架会创建形参的java对象,给属性赋值。请求中的参数是name,框架会调用setName()
    * */
    @RequestMapping( value = "/receiveObject.do" )
    public ModelAndView receiveObject(Student student){
        System.out.println(student.getName() +"  " +student.getAge());
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("myname",student.getName());
        modelAndView.addObject("myage",student.getAge());
        modelAndView.addObject("student",student);
        modelAndView.setViewName("show");
        return modelAndView;
    }

八、ModelAndView的返回值问题

1.返回ModelAndView

若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象。

在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何
资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。

2.返回 String(View视图)

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址。

也就是说我们想要不传入数据,仅仅是一个视图的转发或者重定向,那么我们的返回值就是一个String即可

表单

 <h1>处理器方法返回String表示视图名称</h1>
    <form action="returnString-view.do" method="post">
        姓名:<input type="text" name="name"><br/>
        年龄:<input type="text" name="age"><br/>
        <input type="submit" value="提交参数">
    </form>

控制器类

 /*
    * 处理器方法返回值String——表示逻辑视图名称,需要配置视图解析器
    * */
    @RequestMapping( value = "/returnString-view.do" )
    public String  doReturnView(HttpServletRequest request, String name,int age){
        System.out.println("doReturnView,name"+name+"     age"+age);
        request.setAttribute("myname",name);
        request.setAttribute("myage",age);
        //show:逻辑视图名称,项目中配置了视图解析器
        //框架对视图执行了forword
        return "show";
    }

当然我们我们想要传入数据,可以在参数中传入Request等方法

3.返回值是void

对于处理器方法返回 void 的应用场景,AJAX 响应.

若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void

前端页面

<head>
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-3.4.1.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn").click(function () {
               $.ajax({
                   url:"return-ajax.do",
                   data:{
                       name:"zhangsan",
                       age:20
                   },
                   type:"post",
                   dataType:"json",
                   success: function (resp) {
                       //resp从服务器段返回时json格式的字符串
                       //jquery会把字符串转为json对象,赋值给resp形参
                        alert(resp.name+"   "+resp.age)
                   }
               })
            })
        });
    </script>
</head>
<body>

    <button id="btn">发起ajax请求</button>
</body>

后端处理

 //处理器返回void响应ajax请求
    @RequestMapping( value = "/return-ajax.do" )
    public void  doReturnVoidAjax(HttpServletResponse response,String name,int age) throws IOException {
        System.out.println("doReturnVoidAjax"+name+"     age"+age);
        //处理ajax,使用json做数据的格式
        //Service已经调用完毕,使用Student表示处理结果
        Student student = new Student();
        student.setName(name);
        student.setAge(age);
        //把结果的对象转为json格式
        String json="";
        if (student != null) {
            ObjectMapper om = new ObjectMapper();
            json = om.writeValueAsString(student);
            System.out.println("student转换的json"+json);
        }

        //输出数据,响应ajax请求
        response.setContentType("text/html; charset=UTF-8");
        response.getWriter().write(json);

    }

我们在把结果对象转为json格式以及输出数据,响应ajax请求的时候会有代码冗余
由此引出下一个返回方式

4.返回对象Object

处理器方法也可以返回 Object 对象。这个 Object 可以是 Integer,String,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。

主要实现步骤

  • 加入处理json的工具库的依赖,springmvc默认使用jackson。
  • 在springmvc配置文件之间加入<mvc:annotation-driven>注解驱动
  • 在处理器方法上面加入@ResponseBody注解。

工作原理

1、<mvc:annotation-driven>注解驱动

  • 该注解驱动实现的功能是 完成java对象到json,xml,text,二进制等数据格式的转换
    HttpMessageConveter接口:消息转换器。
  • <mvc:annotation-driven>在加入到springmvc配置文件后, 会自动创建HttpMessageConverter接口 的7个实现类对象,包括 MappingJackson2HttpMessageConverter (使用jackson工具库中ObjectMapper实现java对象转为json字符串)
HttpMessageConverter 接口实现类作用
ByteArrayHttpMessageConverter负责读取二进制格式的数据和写出二进制格式的数据
StringHttpMessageConverter负责读取字符串格式的数据和写出字符串格式的数据
ResourceHttpMessageConverter负责读取资源文件和写出资源文件数据
SourceHttpMessageConverter能够读 / 写 来 自 HTTP 的 请 求 与 响 应 的javax.xml.transform.Source ,支持 DOMSource,SAXSource, 和 StreamSource 的 XML格式
AllEncompassingFormHttpMessageConverter负责处理表单(form)数据
Jaxb2RootElementHttpMessageConverter使用 JAXB 负责读取和写入 xml 标签格式的数据
MappingJackson2HttpMessageConverter负责读取和写入 json 格式的数据。利用Jackson 的 ObjectMapper 读写 json 数据,操作Object 类型数据,可读取 application/json,响应媒体类型为 application/json

功能:定义了java转为json,xml等数据格式的方法。这个接口有很多的实现类
这些实现类完成java对象到json,java对象到xml等的转换

下面的两个方法是控制器类把结果输出给浏览器时使用的:

  • boolean canWrite(Class<?> var1, @Nullable MediaType var2);

  • void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)

      canWrite作用检查处理器方法的返回值,能不能转为var2表示的数据格式。
     检查student(lisi,20)能不能转为var2表示的数据格式。如果检查能转为json,canWrite返回true
     MediaType:表示数格式的, 例如json, xml等等
     
      write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转为json字符串。
    

2、@ResponseBody注解

放在处理器方法的上面, 通过HttpServletResponse输出数据,响应ajax请求的。

表单

<head>
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-3.4.1.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn").click(function () {
               $.ajax({
                   //url:"return-ajax.do",
                   url:"returnStudentJson.do",
                   data:{
                       name:"zhangsan",
                       age:20
                   },
                   type:"post",
                   dataType:"json",
                   success: function (resp) {
                       //resp从服务器段返回时json格式的字符串
                       //jquery会把字符串转为json对象,赋值给resp形参
                        alert(resp.name+"   "+resp.age)
                   }
               })
            })
        });
    </script>
</head>
<body>
    <button id="btn">发起ajax请求</button>
</body>

控制器类

 /*
    * 处理器方法返回一个Student,通过框架转换为json,响应ajax请求
    *  @ResponseBody
    *       作用:把处理器的方法返回对象转为json后,通过httpServletResponse输出给浏览器
    *       位置:方法的定义上面,和其他注解没有顺序关系
    * */
    @RequestMapping(value = "/returnStudentJson.do")
    @ResponseBody
    public Student doStudentJson(HttpServletResponse response,String name,Integer age){
        //调用Service方法,获取请求结果数据,Student对象表示结果数据
        Student student = new Student();
        student.setName("宋进锋");
        student.setAge(22);
        return student;
    }

1.框架会把返回Student类型,调用框架中的ArrayList<HttpMessageConverter>中每个类型onwrite()方法,检查那个HttpMessageConverter接口的实现类能处理Student类型的数据

2.框架会调用实现类的write(),MappingJackson2HttpMessageConverter的write()方法
把Xxx的Student对象转为json,调用Jackson的ObjectMapper实现转为json

3.框架会调用@ResponseBody把步骤二的结果数据输出到浏览器,ajax请求处理完成

5.返回多个对象

控制器类

/*
    *  处理器方法返回List<Student>
    * */
    @RequestMapping(value = "/returnStudentJsonArray.do")
    @ResponseBody
    public List<Student> doStudentJsonObjectArray(String name,Integer age){
        List<Student> list = new ArrayList<>();
        Student student = new Student();
        student.setName("宋进锋");
        student.setAge(22);
        list.add(student);
        Student student1 = new Student();
        student1.setName("虞俊文");
        student1.setAge(20);
        list.add(student1);
        return list;
    }

前端页面

<script type="text/javascript" src="js/jquery-3.4.1.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn").click(function () {
               $.ajax({
                   //url:"return-ajax.do",
                   url:"returnStudentJson.do",
                   data:{
                       name:"zhangsan",
                       age:20
                   },
                   type:"post",
                   dataType:"json",
                   success: function (resp) {
                       //resp从服务器段返回时json格式的字符串
                       //jquery会把字符串转为json对象,赋值给resp形参
                        //alert(resp.name+"   "+resp.age)
                       $.each(resp,function (i,n) {
                            alert(n.name+"   "+n.age)
                       })
                   }
               })
            })
        });
    </script>
    <button id="btn">发起ajax请求</button>
</body>

6.返回String(Model数据)

控制器

*
     *  处理器方法返回的是String,String表示数据,不是视图
     * 区分返回值String是数据还是视图,只要看有没有加@ResponseBody注解
     * 如果有@ResponseBody注解,返回String就是数据,反之就是视图
     *
     * 默认使用的编码,会导致乱码,
     * 解决方案就是:给RequestMapping增加一个属性produces,使用这个属性来指定,新的ContextType
     * */
    @RequestMapping(value = "/returnStringData.do" ,produces = "text/plain;charset=utf-8")
    @ResponseBody
    public String doStringData(String name,Integer age){

        return "返回数据";
    }

前端页面

<script type="text/javascript">
        $(function () {
            $("#btn").click(function () {
               $.ajax({
                   //url:"return-ajax.do",
                   url:"/returnStringData.do",
                   data:{
                       name:"zhangsan",
                       age:20
                   },
                   type:"post",
                   dataType:"text",
                   success: function (resp) {
                      alert("返回的是文本数据:"+resp)
                   }
               })
            })
        });
    </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值