JavaWeb—Servlet

13 篇文章 2 订阅

目录

一、Servlet简介

1.1 快速入门

二、执行流程

三、生命周期

四、方法介绍

五、体系结构

六、urlPattern配置

6.1 urlPattern配置规则

6.2 小结

七、XML配置


一、Servlet简介

Servlet是一种可以与用户进行交互的技术,它能够处理用户提交的HTTP请求并做出响应。Servlet程序可以完成JavaWeb应用程序中处理请求并发送响应的过程。通过Servlet技术,可以收集来自页面表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。Servlet是基于Java的、与平台无关的服务器端组件。

  •  Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术
  • 使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容
  • Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由Web服务器运行Servlet
  • 什么是servlet?

简单来说servlet是运行在服务器上的java程序,servlet由servlet容器管理,servlet容器也叫servlet引擎,是servlet的运行环境,给发送的请求和相应之上提供网络服务

  • servlet的作用

        通俗来讲servlet专门用来接收客户端的请求,专门接收客户端的请求数据,然后调用底层service处理数据并生成结果

        浏览器http请求------> tomcat服务器-------> 到达servlet-----> 执行doget,dopost方法------> 返回数据

<1>客户端发送请求到服务器端
<2>服务器将请求信息发送至Servlet
<3>Servlet生成响应内容并将其传给服务器
<4>服务器将响应返回给客户端

  • servlet里的三大作用域
   request(请求):它的作用范围是一次请求和响应,是三个作用域中最小的。
   session(会话):它的作用比request要大一点,一次会话过程中,它的作用域就一直存在,(默认是30分钟)
   servletcontext:它作用范围最大,作用于整个服务器中。(Application)

概念:运行在服务器端的小程序

  • Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则
  • 将来我们定义了一个类,实现Servlet接口,复写方法

1.1 快速入门

需求分析: 编写一个Servlet类,并使用IDEA中Tomcat插件进行部署,最终通过浏览器访问所编写的Servlet程序

具体的实现步骤为:

     1. 创建Web项目web-demo,导入Servlet依赖坐标

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <!--
      此处为什么需要添加该标签?
      provided指的是在编译和测试过程中有效,最后生成的war包时不会加入
       因为Tomcat的lib目录中已经有servlet-api这个jar包,如果在生成war包的时候生效就会和Tomcat中的jar包冲突,导致报错
    -->
    <scope>provided</scope>
</dependency>

     2. 创建:定义一个类,实现Servlet接口,并重写接口中所有方法,并在service方法中输入一句话

package com.learn.web;

import javax.jws.WebService;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 */
@WebServlet("/demo1")
public class ServletDemo1 implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Servlet,Hello world~");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

     3. 配置:在类上使用@WebServlet注解,配置该Servlet的访问路径

@WebServlet("/demo1")

     4. 访问:启动Tomcat,浏览器中输入URL地址访问该Servlet

http://localhost:8080/web-demo/demo1

     5. 浏览器访问后,在控制台会打印servlet hello world~ 说明servlet程序已经成功运行。

二、执行流程

  • 浏览器发出http://localhost:8080/web-demo/demo1请求,从请求中可以解析出三部分内容,分别是localhost:8080web-demodemo1

        ○根据localhost:8080可以找到要访问的Tomcat Web服务器

        ○根据web-demo可以找到部署在Tomcat服务器上的web-demo项目

        ○根据demo1可以找到要访问的是项目中的哪个Servlet类,根据@WebServlet后面的值进行匹配

  • 找到ServletDemo1这个类后,TomcatWeb服务器就会为ServletDemo1这个类创建一个对象,然后调用对象中的service方法

        ○ServletDemo1实现了Servlet接口,所以类中必然会重写service方法供Tomcat Web服务器进行调用

        ○service方法中有ServletRequest和ServletResponse两个参数,ServletRequest封装的是请求数据,ServletResponse封装的是响应数据,后期我们可以通过这两个参数实现前后端的数据交互

Servlet执行以下主要任务:

  1. 读取客户端(浏览器)发送的显式的数据。这包括页面上的HTML表单,或者也可以是来自Applet或自定义的HTTP客户端程序的表单。
  2. 读取客户端(浏览器)发送的隐式的HTTP请求数据。这包括Cookies、媒体类型和浏览器能理解的压缩格式等
  3. 处理数据并生成结果。这个过程可能需要访问数据库,执行RMI或CORBA调用,调用Web服务,或者直接计算得出对应的响应
  4. 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML或XML)、二进制文件(GIF图像)、Excel等
  5. 发送隐式的HTTP响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如HTML),设置Cookies和缓存参数,以及其他类似的任务

小结:

        1. Servlet由谁创建?Servlet方法由谁调用?

答:Servlet由Web服务器创建,Servlet方法由Web服务器调用

        2. 服务器怎么知道Servlet中一定有service方法?

答:因为我们自定义的Servlet,必须实现Servlet接口并复写其方法,而Servlet接口中有service方法

2.1 执行原理

  1. 当服务器接收到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容
  3. 如果有,则在找到对应的<servlet-class>全类名
  4. tomcat会将字节码文件加载进内存,并且创建其对象
  5. 调用其方法(暂且理解为调用service()方法)

三、生命周期

生命周期: 对象的生命周期指一个对象从被创建到被销毁的整个过程。

Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:

      1. 加载和实例化==:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象

        默认情况,Servlet会在第一次访问被容器创建,但是如果创建Servlet比较耗时的话,那么第一个访问的人等待的时间就比较长,用户的体验就比较差,那么我们能不能把Servlet的创建放到服务器启动的时候来创建,具体如何来配置?

@WebServlet(urlPatterns = "/demo1",loadOnStartup = 1)
loadOnstartup的取值有两类情况
    (1)负整数:第一次访问时创建Servlet对象
    (2)0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高

      2. 初始化:在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只==调用一次==

      3. 请求处理:每次请求Servlet时,Servlet容器都会调用Servlet的service()方法对请求进行处理

      4. 服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收

package com.learn.web;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 */
@WebServlet(urlPatterns="/demo2",loadOnStartup = 1)
public class ServletDemo2 implements Servlet {

    /*
        初始化方法
        1. 调用时机:默认情况下,Servlet被第一次访问时(第一次访问时会创建Servlet对象,这个对象会调用),调用
           * loadOnStartup:
        2. 调用次数:1次
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init....");
    }

    /*
        提供服务方法
        1. 调用时机:每一次Servlet对象对访问时,就会调用
        2. 调用次数:多次
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Servlet,Hello world~");
    }

    /*
        销毁方法
        1. 调用时机:内存释放或服务器关闭时,Servlet对象会被销毁,会调用该方法
        2. 调用次数:1次
     */
    @Override
    public void destroy() {
        System.out.println("destroy.....");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public String getServletInfo() {
        return null;
    }


}

四、方法介绍

  • 初始化方法,在Servlet被创建时执行,只执行一次

void init(ServletConfig config) 

  • 提供服务方法, 每次Servlet被访问,都会调用该方法

void service(ServletRequest req, ServletResponse res)

  • 销毁方法,当Servlet被销毁时,调用该方法。在内存释放或服务器关闭时销毁Servlet

void destroy() 

  • 获取Servlet信息

String getServletInfo() 
//该方法用来返回Servlet的相关信息,没有什么太大的用处,一般返回一个空字符串即可
public String getServletInfo() {
    return " ";
}

  • 获取ServletConfig(Servlet配置)对象

ServletConfig getServletConfig()

        ServletConfig对象,在init方法的参数中有,而Tomcat Web服务器在创建Servlet对象的时候会调用init方法,必定会传入一个ServletConfig对象,我们只需要将服务器传过来的ServletConfig进行返回即可。

Servlet中的生命周期方法:

1. 被创建:执行init方法,只执行一次

  • Servlet什么时候被创建

                * 默认情况下,第一次被访问时Servlet被创建

                * 可以配置执行Servlet的创建时机

                        在<servlet>标签下配置

                                ①第一次被访问时,创建     <load-on-startup>的值为负数

                                ②在服务器启动时,创建     <load-on-startup>的值为0或正整数

  • Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Serlvet是单例的

                * 多个用户同时访问时,可能存在线程安全问题

                * 解决:尽量不要在Serlvet中定义成员变量。即使定义了成员变量,也不要修改值

2. 提供服务:执行service方法,执行多次

  • 每次访问Servlet时,Service方法都会被调用一次

3. 被销毁:执行destroy方法,只执行一次

  • Servlet被销毁时执行。服务器关闭时,Servlet被销毁
  • 只有服务器正常关闭时,才会执行destroy方法
package com.learn.web;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 * Servlet生命周期方法
 */
@WebServlet(urlPatterns="/demo3",loadOnStartup = 1)
public class ServletDemo3 implements Servlet {
    private ServletConfig config;
    /*
        初始化方法
        1. 调用时机:默认情况下,Servlet被第一次访问时(第一次访问时会创建Servlet对象,这个对象会调用),调用
           * loadOnStartup:
        2. 调用次数:1次
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.config = config;
        System.out.println("init....");
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

    /*
        提供服务方法
        1. 调用时机:每一次Servlet对象对访问时,就会调用
        2. 调用次数:多次
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Servlet,Hello world~");
    }

    /*
        销毁方法
        1. 调用时机:内存释放或服务器关闭时,Servlet对象会被销毁,会调用该方法
        2. 调用次数:1次
     */
    @Override
    public void destroy() {
        System.out.println("destroy.....");
    }



    @Override
    public String getServletInfo() {
        return null;
    }

}

五、体系结构

  • GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象

        ◇ 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可

  • HttpServlet:对http协议的一种封装,简化操作

        ◇ 定义类继承HttpServlet

        ◇ 复写doGet/doPost方法

 因为我们将来开发B/S架构的web项目,都是针对HTTP协议,所以我们自定义Servlet,会通过继承HttpServlet

@WebServlet("/demo4")
public class ServletDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //TODO GET 请求方式处理逻辑
        System.out.println("get...");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //TODO Post 请求方式处理逻辑
        System.out.println("post...");
    }
}
  • 要想发送一个GET请求,请求该Servlet,只需要通过浏览器发送http://localhost:8080/web-demo/demo4,就能看到doGet方法被执行了

  • 要想发送一个POST请求,请求该Servlet,单单通过浏览器是无法实现的,这个时候就需要编写一个form表单来发送请求,在webapp下创建一个a.html页面,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/web-demo/demo4" method="post">
        <input name="username"/><input type="submit"/>
    </form>
</body>
</html>

Servlet的简化编写就介绍完了,接着需要思考两个问题:

  1. HttpServlet中为什么要根据请求方式的不同,调用不同的方法?

  2. 如何调用?

针对问题一,我们需要回顾之前的知识点==前端发送GET和POST请求的时候,参数的位置不一致,GET请求参数在请求行中,POST请求参数在请求体中==,为了能处理不同的请求方式,我们得在service方法中进行判断,然后写不同的业务处理,这样能实现,但是每个Servlet类中都将有相似的代码,针对这个问题,优化的策略如下

package com.learn.web;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 */
@WebServlet("/demo5")
public class ServletDemo5 extends MyHttpServlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //根据请求方式的不同,进行分别的处理
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        //1.获取请求方式
        String method = request.getMethod();
        //2.判断
        if ("GET".equals(method)){
            //get方式的处理逻辑
            doget(servletRequest,servletResponse);
        }else if ("POST".equals(method)){
            //post方式的处理逻辑
            dopost(servletRequest,servletResponse);
        }
    }

    protected void dopost(ServletRequest servletRequest, ServletResponse servletResponse) {
    }

    protected void doget(ServletRequest servletRequest, ServletResponse servletResponse) {
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

对Servlet接口进行继承封装,来简化代码开发。

package com.learn.web;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 */
public class MyHttpServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //根据请求方式的不同,进行分别的处理
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        //1.获取请求方式
        String method = request.getMethod();
        //2.判断
        if ("GET".equals(method)){
            //get方式的处理逻辑
            doget(servletRequest,servletResponse);
        }else if ("POST".equals(method)){
            //post方式的处理逻辑
            dopost(servletRequest,servletResponse);
        }
    }

    protected void dopost(ServletRequest servletRequest, ServletResponse servletResponse) {
    }

    protected void doget(ServletRequest servletRequest, ServletResponse servletResponse) {
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

有了MyHttpServlet这个类,以后再编写Servlet类的时候,只需要继承MyHttpServlet,重写父类中的doGet和doPost方法,就可以用来处理GET和POST请求的业务逻辑。接下来,可以把ServletDemo5代码进行改造

@WebServlet("/demo5")
public class ServletDemo5 extends MyHttpServlet {

    @Override
    protected void doGet(ServletRequest req, ServletResponse res) {
        System.out.println("get...");
    }

    @Override
    protected void doPost(ServletRequest req, ServletResponse res) {
        System.out.println("post...");
    }
}

将来页面发送的是GET请求,则会进入到doGet方法中进行执行,如果是POST请求,则进入到doPost方法。这样代码在编写的时候就相对来说更加简单快捷。

类似MyHttpServlet这样的类Servlet中已经为我们提供好了,就是HttpServlet,翻开源码,可以搜索service()方法,会发现HttpServlet做的事更多,不仅可以处理GET和POST还可以处理其他五种请求方式。

六、urlPattern配置

Servlet类编写好后,要想被访问到,就需要配置其访问路径(urlPattern)

urlpartten:Servlet访问路径

  • 一个Servlet可以定义多个访问路径:@WebServlet({ "/d4" , "/dd4" , "/ddd4" })
  • 路径定义规则

        1. /xxx

        2. /xxx/xxx:多层路径,目录结构

        3. *.do 

  • 一个Servlet,可以配置多个urlPattern

package com.learn.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 * urlPattern:一个Servlet可以配置多个路径
 */
@WebServlet(urlPatterns = {"/demo7","/demo8"})
public class ServletDemo7 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo7 get。。。");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

在浏览器上输入http://localhost:8080/web-demo/demo7,http://localhost:8080/web-demo/demo8这两个地址都能访问到ServletDemo7的doGet方法。

6.1 urlPattern配置规则

  • 精确匹配

package com.learn.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 * 精确匹配
 */
@WebServlet(urlPatterns = "/user/select")
public class ServletDemo8 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo8 get。。。");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

访问路径http://localhost:8080/web-demo/user/select

  •  目录匹配

package com.learn.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 * 目录匹配
 */
@WebServlet(urlPatterns = "/user/*")
public class ServletDemo9 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo9 get。。。");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

 访问路径http://localhost:8080/web-demo/user/任意

  • 扩展名匹配

package com.learn.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 * 扩展名匹配
 */
@WebServlet(urlPatterns = "*.do")
public class ServletDemo10 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo10 get。。。");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

  • 任意匹配

package com.learn.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 * 任意匹配:/ *
 */
@WebServlet(urlPatterns = "/")
public class ServletDemo11 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo11 get。。。");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

 访问路径http://localhost:8080/demo-web/任意

6.2 小结

  • urlPattern总共有四种匹配方式,分别是精确匹配、目录匹配、扩展名匹配、任意匹配
  • 五种配置的优先级为 精确匹配 > 目录匹配 > 扩展名匹配 > /* > /

七、XML配置

        前面对应Servlet的配置,我们都使用的是@WebServlet,这个是Servlet从3.0版本后开始支持注解配置,3.0版本前只支持XML配置文件的配置方法。

对于XML的配置步骤有两步:

  • 编写Servlet类
package com.learn.web;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 咕咕猫
 * @version 1.0
 *
 */
public class ServletDemo12 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo12 get。。。");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}
  • 在web.xml中配置该Servlet

    <?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>
            <servlet-name>demo12</servlet-name>
            <servlet-class>com.learn.web.ServletDemo12</servlet-class>
        </servlet>
    
        <!--Servlet访问路径-->
        <servlet-mapping>
            <servlet-name>demo12</servlet-name>
            <url-pattern>/demo12</url-pattern>
        </servlet-mapping>
    </web-app>

这种配置方式和注解比起来,麻烦很多,所以建议使用注解来开发。但是要认识上面这种配置方式,因为并不是所有的项目都是基于注解开发的。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值