四、Servlet

Servlet

  • 网络应用体系结构

    • C/S:client-server 基于客户端软件 —> 服务器的模式

      • 针对不同的应用下载对应的客户端软件
      • 优点:图形显示效果较好,用户体验好
      • 缺点:
        • 必须下载指定的客户端软件才能使用
        • 一旦服务器端升级更新,所有的客户端软件也必须升级
    • B/S:browser-server 基于客户端浏览器 —> 服务器的模式

      • 优点:服务器端升级,不影响客户端的使用

      • 缺点:图形显示效果差,特别是3D效果等

基于B/S结构的网络应用(Web应用)
  • request 请求

  • response 响应

  • 静态的Web应用

    • 服务器端:保存的是静态的HTML网页资源,当用户请求的时候服务器把指定的网页资源HTML响应到客户端浏览器运行
    • 缺点:
      • 随着用户需求的不断增长和变化,服务器端的HTML会越来越多,导致网站的维护成本越来越高
      • HTML不支持对数据库的访问
  • 动态的Web应用

    • 可以根据不同用户的需求和条件,响应不同的结果到一个网页上
    • 服务器端:存储程序员编写好的Java程序,用来处理客户端的请求,并动态的响应结果
Web服务器
  • 指一台安装了服务器软件的高性能计算机
  • 服务器最主要的功能是可以通过网络对外提供服务
  • 主流的Web服务器软件
    • apache tomcat:是apache软件基金组织开发的一款开源的、免费的Web服务器软件,性能优良,受广大互联网公司欢迎,Tomcat底层是采用 Java语言实现
    • Jetty:开源的servlet容器,为基于Java的Web容器
    • resin:CAUCHO公司对JavaWeb开发提供了良好的支持
    • WebLogic:Oracle公司出品的一个JavaWeb服务器
    • WebSphere:IBM推出的一款JavaWeb应用服务器
    • Jboss等
  • tomcat目录结构
    • .jar java application resource
    • bin: 存放的是tomcat启动和关闭的命令执行文件
    • conf:存放的是tomcat相关参数配置文件
    • webapps: 目录里面的每一个子目录都代表了一个网站系统
    • lib: 目录存放了tomcat软件的全局功能jar包
    • temp: 目录存放临时文件
    • logs: 目录存放tomcat运行时产生的日志
    • work: 目录存放 jsp运行后产生的一些文件
手动部署一个应用到tomcat服务器
  • 一个标准Web应用(网站)目录结构

    • myweb
      • |-WEB-INF
        • |-web.xml Web应用部署描述符
        • |-classes 存放 java程序编译后的 .class文件
        • |-lib 存放当前项目需要使用的依赖 .jar包,如ojdbc6.jar
      • |-与WEB-INF同级位置存放HTML、CSS、image、javascript等其他资源
  • tomcat服务器核心的作用

    • 接收并解析来自客户端的请求
    • 处理请求
      • 静态网页:找到内部指定项目下的html网页,响应给客户端浏览器
      • 动态网页:tomcat会调用服务器中的一段 Java程序处理请求
        • 使用 Java程序在服务器端处理请求,支持对数据库的访问操作,动态展示结果
    • 响应结果
Servlet
  • 主要作用是编写动态的 JavaWeb应用

  • Servlet是 JavaEE规范规范体系中的一种,是用于编写运行与服务端的一段 Java程序,处理来自客户端浏览器的基于HTTP协议的请求

  • 销毁:void destroy()

    • 当前Servlet销毁的时候调用一次
  • ServletConfig getServletConfig()

  • java.lang.String getServletInfo()

  • 初始化:void init(ServletConfig config)

    • 服务器创建当前Servlet对象的时候调用一次
  • void service(ServletRequest req,ServletResponse res)

  • 来自客户端浏览器的请求对象 request,响应处理对象 response

  • 编写Servlet程序

    • 实现Servlet接口

    • 编译:将tomcat/lib目录中的servlet-api.jar配置到系统环境变量

    • 部署

      • 将 编译生成的.class文件,带包结构放到tomcat/webapps/项目中的classes目录下
      • 编写web.xml ( Web应用部署描述符 )
    • 测试

      • 启动服务器
      • 访问:http://localhost:8989/项目名/url-pattern
      public class ShowTime implements Servlet{ //实现Servlet接口
      	public void init(ServletConfig config) throws ServletException{ }
      	//处理请求的方法
      	public void service(ServletRequest request,ServletResponse response)
      		throws ServletException,IOException{
      			//设置响应的类型和编码格式
      			response.setContentType("text/html");//设置响应类型:父类型/子类型
      			response.setCharacterEncoding("UTF-8");
      			//获取输出流对象
      			//获取字节输出流:response.getOutputStream();
      			//获取字符输出流:response.getWriter();
      			PrintWriter out = response.getWriter();
      			//响应结果
      			out.println("<html>");
      			out.println("<body>");
      			out.println("<h1>"+new Date()+"</h1>");
      			out.println("</body>");
      			out.println("</html>")
      
      			out.flush();
      			//流不需要手动关闭,未来自己会自动关
      		}
      	public void destroy(){ }
      	public ServletConfig getServletConfig(){	return null;	}
      	public String getServletInfo(){	return null;	}
      }
      
        <!-- 编写一个Servlet实现,都要书写以下配置:目的是告知服务器Servlet程序的相关信息 -->
        <servlet>
        	<!-- 指定一个别名,主要是区分不同的Servlet程序
      			多个Servlet别名不能重复,同一个Servlet别名必须一样
        	 -->
        	<servlet-name>upShowTime</servlet-name>
        	<!-- 指定servlet实现类的全限定名:包名.类名 -->
        	<servlet-class>com.hang.servlet.ShowTime</servlet-class>
        </servlet>
        <servlet-mapping>
        	<!-- 当前servlet别名 与上面保持一致 -->
        	<servlet-name>upShowTime</servlet-name>
        	<!-- 配置当前servlet网络请求路径,必须以 / 开头,代表从当前项目根开始 
      		请求方式:http://localhost:8989/项目名/url-patten,起名方式见名知意即可
        	-->
        	<url-pattern>/upShowTime</url-pattern>
        </servlet-mapping>
      
    • 实现GenericServlet接口

    • 继承HttpServlet类

  • 客户端请求一个Servlet的过程

    • 客户端发出请求,tomcat服务器会解析出请求的目标资源路径
    • 找到目标项后,开始解析web.xml
    • 根据解析到的实现类全限定名,调用目标servlet实现类的service方法
    • 响应结果
Servlet生命周期
  • 单例模式,区分23种设计模式种的单例设计模式

    • 一个servlet实现类,在服务器中只会创建一个对象,当请求一个Servlet的时候,服务器会检查一下当前内存中是否有当前请求的Servlet对象,如果有直接使用,如果没有则创建新的(做了判断,取出来的一个,单例设计模式是只允许一个)
    • Servlet在多线程并发的环境下,属于临界资源,线程不安全,所以不要轻易的定义成员变量,在开发中基本上不在Servlet实现类定义成员变量。(可做同步锁保证线程安全)
  • 初始化:初始化的时候会调用一次init方法

    • servlet实例初始化的时机
      • 默认当客户端第一次请求一个servlet的时候
      • 可以手动在web.xml中配置指定的servlet在服务器启动的时候初始化,使用标签n来指定,值配置为非负数,多个servlet在启动服务器创建,可以通过不同的整数值设置创建的优先级,0最优先
  • 服务:请求到达服务器调用service方法

  • 销毁:销毁的时候调用destroy方法。关闭服务器的时候销毁Servlet

路径概念区分
  • URL:统一资源定位符,用于在互联网上唯一的定位、标识一个资源
    • 格式:http://IP:PORT/项目名/url-pattern 即:http://IP:端口:项目名/url-pattern
  • URI:统一资源描述符,用于在指定的服务器中唯一的定位、标识一个资源
    • 格式:/项目名/url-pattern
  • url-pattern:
    • Servlet程序的url-pattern就是在web.xml中的标签的配置
    • html资源的url-pattern就是相对于项目的根目录的相对路径
请求一个Servlet的几种方式
  • 地址栏请求

  • HTML页面超链接请求

    <a href="URI"></a>
    
  • HTML页面表单请求

    <form action="URI" method="POST">
    </form>
    
客户端与服务器数据的交互
  • 客户端提交参数的方式

    • 通过地址栏拼接的方式

      http://localhost:8989:upupServlet_day2/ParamServlet?name=ceshi&age=18
      
    • 在超链接地址后拼接

      <a href="/upServlet_day2/ParamServlet?name=ceshi&age=18">点我</a>
      
  • 服务器端接收请求参数的方式

  • 处理请求参数的中文乱码

    • post请求参数中文乱码
    • get请求中文乱码
  • 接收一个参数

    • String param = request.getParameter(“name”);
  • 接收一组参数

    • String[] param = request.getParameterValues(“name”);
Servlet整合JDBC开发
  • Junit单元测试

    • 方法修饰符必须是public
    • 返回值类型必须是void
    • 方法名和类名不能叫Test
    • 参数列表必须为空
  • 开发环境搭建

    • 引入.jar包:ojdbc6.jar
    • 引入配置文件:工具类配置文件等
    • 编写代码
      • 建表、封装实体类、编写DAO、编写Service、编写Servlet、部署测试
跳转
  • 请求转发

    • 指将两个Servlet连接在一起,形成一个工作流,共同完成一个任务

      //请求转发
      //获得一个调度对象,用于指定目标跳转的下一个Servlet的url-pattern
      RequestDispatcher rd = request.getRequestDispatcher("url-pattern");
      //执行跳转
      rd.forward(request, response);
      
    • 特点

      • 请求转发连接的两个Servlet属于一次请求
      • 请求转发的地址栏不发生改变。请求转发的动作是发生在服务器的内部,客户端并不知道
      • 只有最后一个Servlet才能向客户端响应结果
    • request作用域

      • 一块存储数据的空间,可以存储命名属性

      • 可以被请求转发连接的两个Servlet共享的一个数据空间,主要用于存储命名属性

        //传递 存数据
        request.setAttribute(String name,Object value);
        //接收 取数据
        Object obj = request.getAttribute("name");
        
      • 数据共享范围:一次请求连接的两个Servlet可以共享

      • request作用域生命周期:一次请求有效;请求到达服务器创建,响应回到客户端销毁

  • 请求重定向

    • 实现在两个Servlet之间进行跳转
    • response.sendRedirect(“URI”);
    • 特点
      • 地址栏发生改变
      • 发生在服务器外部(客户端)
      • 重定向跳转的两个Servlet处于两次请求;请求重定向连接的两个Servlet不可以使用request作用域传递数据
请求转发
request.setRequestDispatcher(“url-pattern”).forward(request,response)
请求重定向
response.sendRedirect(“URI”)
一次请求两次请求
地址栏不发生改变地址栏发生改变
发生在服务器内部发生在服务区外部
可以使用request作用域不能使用request作用域
跳转前后的两个Servlet之间需要request作用域传递数据,使用用请求转发跳转前后的两个Servlet不需要request作用域传递数据,使用请求重定向
Servlet跳转
  • Servlet到Servlet
    • 请求转发:可以使用request作用域传递数据
    • 请求重定向:不能使用request作用域传递数据
  • Servlet跳转到html:html不能操作作用域,所有通常使用重定向跳转html
    • 请求转发:通常不使用
    • 请求重定向
  • html跳转到Servlet
    • 超链接
    • 表单
  • html跳转到html
    • 超链接
    • 表单:表单主要是提交数据使用,但是html没有办法处理数据,所有没意义
  • 动态获取项目名
    • String path = request.getContextPath();
会话追踪
  • 会话指用户使用客户端浏览器与某一个网站进行的一次完整的交互过程

  • 一次会话是指同一个客户端浏览器,向同一个web应用服务器发送的一到多次的请求,会话的开始是打开浏览器发起第一次请求,会话的终止以关闭客户端浏览器为准

  • HTTP协议:HTTP协议进行数据交互,但http协议天生是无状态的,服务器不会主动的记录用户信息

  • 会话追踪为了实现服务器可以识别同一个客户端浏览器与服务器建立的一次会话中多次请求之间的关联所采取的实现

  • 实现会话追踪:cookie和session

  • cookie技术

    • 保存在客户端浏览器上的一小段字符串信息。以key-value方式进行存储,主要用于记录用户相关的信息,且value不能存储中文
    • 通常由服务器颁发给客户端浏览器,通常客户端浏览器发送请求的时候都会携带cookie
    • cookie基本使用:
      • 创建cookie对象:Cooke cookie = new Cookie(name,value);
      • 设置到客户端浏览器:response.addCookie(cookie);
      • 获取客户端的cookie:Cookie[] cookies = request.getCookies();
        • 只能获取到自己服务器设置到浏览器上的cookie
      • 获取cookie的名字:cookie.getName();
      • 获取cookie的值:cookie.getValue();
      • 生命周期:默认关闭浏览器会话自动销毁
      • 控制cookie的存活时间:cookie.setMaxAge(秒);
        • cookie.setMaxAge(正数); 设置的存活时间为秒
        • cookie.setMaxAge(0); 清除指定的cookie
        • cookie.setMaxAge(负数); 与浏览器会话绑定,结束会话则销毁(默认)
    • cookie的缺陷:
      • 不支持存储中文
      • cookie保存的信息量少,默认4KB
      • cookie明文存储信息,不安全
      • cookie存在被禁用的风险
  • session会话技术

  • session是服务器端保存用户状态信息的技术,一个session对象代表了一个会话,session属于一个作用域,可以存储命名属性,类型是HttpSession,也叫做session作用域

  • session作用域基本使用

    • 获取session对象:
      • HttpSession session = request.getSession(); 不写相当于true
      • HttpSession session = request.getSession(true);
      • true:如果已经创建的当前会话的session对象,直接拿来使用;如果没有创建过当前对话session对象,则创建一个新的
      • false:如果已经创建的当前会话的session对象,直接拿来使用;如果没有创建过当前对话session对象,也不会创建新的
    • 存数据:session.setAttribute(String name,Object value);
    • 取数据:Object obj = session.getAttribute(String name);
    • 获取session唯一标识:session.getId(); 每一个session的唯一标识,由32个字符组成
    • 移除session作用域的一个命名属性:session.removeAttribute(“name”);
  • session数据共享范围:一次会话,一到多次请求

  • session作用域生命周期:

    • 创建第一次请求:request.getSession(true); / request.getSession();

    • 销毁:

      • 超时策略:计时规则是从最后一次请求结束开始计时

        • tomcat服务器默认30分钟销毁session,可以手动在tomcat/conf/web.xml中标签中修改默认时间,针对与当前服务器下所有项目的超时时间

        • 可以对单独项目修改,在当前项目下的web.xml配置,时间只能设置为整数,单位为分钟

          <!-- 针对当前项目设置session超时时间 -->
            <session-config>
            	<session-timeout>30</session-timeout>
            </session-config>
          
      • 手动销毁:session.invalidate();

  • session的钝化和活化

    • 钝化:当正常关闭服务器的时候,服务器会将内存中没有超时的session写到本地的文件上保存
    • 活化:当服务器启动时,会将本地保存的session信息的文件读入内存,将没有超时的session恢复
  • session应用场景

    • 主要保存用户的相关信息
      • 强制登陆验证
      • 用户登录验证码的存储
      • 存储购物车实现
  • session实现原理

    • 当用户第一次请求服务器创建session后,服务器会自动将session id设置到客户端浏览器的cookie中保存
    • 当用户再次请求服务器时,服务器会自动获取客户端浏览器上的cookie,得到session id,进而找到对应的session给用户使用
  • URL重写

    • 由于session的实现依赖于客户端浏览器上保存的session id信息,而cookie是存在被禁用的风险,一旦客户端浏览器的cookie被禁用,session则不能正常使用;为了保证cookie被禁用时session的不丢失,所以采取URL重写的策略

    • URL重写指将session的id在请求地址的后面进行拼接传递,即使cookie禁用了,也可以正常传递session id

    • URL;jsessionid=32个字符组成的session id,之后服务器会自动获取jsessionid,去找到对应的session,实际开发中使用下面方法:

      //自动检测客户端是否禁用了cookie,如果禁用自动完成URL重写
      String path = response.encodeURL(request.getContextPath()+"/SessionB");
      response.sendRedirect(path);
      
谷歌验证码使用
  • 引入依赖jar包

  • 配置web.xml

    <!-- 配置验证码 -->
      <servlet>
      	<servlet-name>kaptcha</servlet-name>
      	<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>kaptcha</servlet-name>
        <url-pattern>/kaptcha</url-pattern>
      </servlet-mapping>
    
  • HTML页面使用

    <img alt="" src="/项目名/url-pattern">
    
  • 从session作用域中取出并验证

    String serverCode = (String)session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
    serverCode.equalsIgnoreCase(code) //不区分大小写验证
    
ServletContext作用域
  • 存储命名属性

  • 创建:

    //创建
    HttpSession session = request.getSession(true);
    ServletContext application = session.getServletContext();
    //存值
    application.setAttribute(String name,Object obj)
    //取值
    Object obj = application.getAttribute(String name);
    
  • 数据共享范围:整个Web应用共享 ( 一个Web应用只有一个ServletContext对象 )

  • 生命周期

    • 创建:启动应用的时候创建
    • 销毁:关闭服务器销毁
  • 应用场景:存储涉及到被整个应用都共享的数据,比如在线的用户数、服务区大区列表选项等,且这类数据不经常改变

  • 三大作用域

requestsessionapplication
原始类型ServletRequest接口HttpSession接口ServletContext接口
数据共享范围一次请求一次会话整个Web应用
生命周期创建:请求到达服务器
销毁:响应回到客户端
创建:第一次请求
销毁:超时策略、手动销毁
创建:应用启动
销毁:服务器关闭
过滤器 filter
  • 过滤来自客户端的请求,请求在到达目标servlet之前,会先经过过滤器,执行过滤器里的功能代码
  • 开发中通常在过滤器里定义从多个servlet里面提取出来的共性的冗余代码,提高代码的复用性
  • 实现一个过滤器
    • 实现Filter接口
  • 配置过滤器
	<!-- 配置过滤器 -->
  <filter>
  	<!-- 过滤器别名,全局唯一即可 -->
  	<filter-name>MyFilter1</filter-name>
  	<!-- 实现类全限定名 -->
  	<filter-class>com.hang.filter.MyFilter1</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>MyFilter1</filter-name>
  	<!-- 配置过滤范围 -->
    <!--  第一种过滤方式
<url-pattern>/Target1</url-pattern> //代表过滤指定的target1的请求
<url-pattern>/Target2</url-pattern> //过滤指定的/target2的请求,可以有多个url-patterns -->
    <!--  第二种过滤方式
<url-pattern></url-pattern> //过滤来自客户端的所有请求
		-->
    <!--  第三种过滤方式 对Servlet进行差别化的配置
<url-pattern>/标签/*</url-pattern> //过滤来自客户端的部分请求
		-->
  </filter-mapping>
  • 多个过滤器的执行顺序是由过滤器配置的先后顺序决定,配置在前的先执行

  • 使用过滤器进行重定向跳转,注意全部过滤时容易出现重定向循环(死循环)

    • 配置过滤范围等时候,不要包含重定向的资源请求
    		HttpServletResponse response = (HttpServletResponse)res;
    		HttpServletRequest request = (HttpServletRequest)req;
    		
    		//获取请求参数
    		String name = request.getParameter("name");
    		if(null == name){
    			//重定向到错误页面
    			response.sendRedirect(request.getContextPath()+"/error.html");
    		}else{
    			//让请求继续向下执行
    			chain.doFilter(req, res);
    		}
    
  • 过滤器过滤请求规则

    • 默认只过滤来自客户端的直接请求,默认值为REQUEST

      • 直接请求包含:地址栏请求,超链接请求,表单请求、请求重定向
    • 配置过滤请求的规则

      <!-- 配置过滤请求规则 -->
      <dispatcher>FORWARD</dispatcher> <!--只过滤转发请求,设置后就覆盖了默认的请求规则-->
      <dispatcher>REQUEST</dispatcher><!--同时过滤转发和直接请求-->
      
      
  • chain.doFilter()后的代码,就是响应回到客户端时的过滤

  • 过滤器的创建和销毁

    • 启动服务器创建
    • 关闭服务器销毁
  • 过滤器的应用

    • 编码过滤器
    • 强制登录验证过滤器
监听器 listener
  • 监听器主要用于监听应用中指定的一些特定事件,当发生这些特定事件时,可以执行监听器中的相关处理代码

  • 实现一个监听器:实现指定监听器的接口

  • 监听器执行的时机是发生所监听的事件时执行

    • 实现ServletContextListener,监听ServletContext对象的创建和销毁

      public class MyServletContextListener implements ServletContextListener{
      	//销毁时执行
      	@Override
      	public void contextDestroyed(ServletContextEvent event) {
      		System.out.println("ServletContext销毁了");
      	}
      	/**
      	 * ServletContext创建时执行
      	 * 参数ServletContextEvent:是一个事件对象,可以获取到事件源,即ServletContext
      	 */
      	@Override
      	public void contextInitialized(ServletContextEvent event) {
      		System.out.println("ServletContext创建了");
      	}
      }
      
      <!-- 监听器配置 -->
      	<listener>
      		<!-- tomcat根据实现类全限定名创建监听器即可 -->
      		<listener-class>
      			com.hang.listener.MyServletContextListener
      		</listener-class>
      	</listener>
      
    • 实现HttpSession监听器,可以实现统计在线人数

      public class SessionListeners implements HttpSessionListener {
      	@Override
      	public void sessionCreated(HttpSessionEvent arg0) {
      		System.out.println("创建了一个session");
      	}
      	@Override
      	public void sessionDestroyed(HttpSessionEvent arg0) {
      		System.out.println("销毁了一个session");
      	}
      }
      
连接池
  • 连接池技术主要用于优化项目,着重于数据库连接角度优化,通过减少频繁的与数据库交互来提高系统的性能 ( 频繁创建、销毁数据库连接 )

  • 使用连接池后,我们的应用在需要访问数据库的时候,直接到连接池中拿出预先创建好的连接,用完了之后再还回连接池,提高了连接资源的复用性,进而减少了频繁的创建和销毁连接

  • 连接池中的连接,有闲置和忙碌两种状态

    • 当一个连接在使用中属于忙碌
    • 使用完毕属于闲置
    • 通常用完一个连接将其还回连接池
  • 使用tomcat提供的连接池

    • 配置tomcat/conf/context.xml,指定连接池中的相关参数,数据库连接的参数等

      <Resource 
            name="jdbc/oracle"		通过程序获取连接池的依据,连接池名字
            auth="Container"			连接池管理者,Container指当前容器管理,tomcat
            type="javax.sql.DataSource"  连接池资源类型
            username="hr"
          	password="hr"
          	driverClassName="oracle.jdbc.OracleDriver"
          	url="jdbc:oracle:thin:@localhost:1521:xe"
          	maxActive="10"				设置连接池最大活跃连接数
            maxIdle="4"  设置最大空闲连接数
      ></Resource>	
      
    • 获取连接池中的连接

      //创建一个连接池的上下文对象 保存连接池信息
      Context cxt = new InitialContext();
      //获取到连接池
      DataSource ds = (DataSource)cxt.lookup("java:comp/env/jdbc/oracle");
      //获取连接池中的连接
      Connection conn = ds.getConnection();
      
      //第二种写法
      //初始化context.xml中的配置信息对象
      Context initCtx = new InitialContext();
      //获取到连接池
      Context envCtx = (Context)initCtx.lookup("java:comp/env");
      DataSource ds = (DataSource)envCtx.lookup("jdbc/oracle");
      //获取连接池中的连接
      Connection conn = ds.getConnection();
      
    • 由于tomcat提供的连接池与服务器耦合太强,使用时不方便,所以不建议使用

      • 关闭服务器后,不能再使用tomcat连接池
JNDI Java naming directory interface
  • JAEE规范的其中一个

  • Java命名目录接口,数状结构存储,从根节点往下查找

  • Java:comp/env

    • jdbc
      • oracle
      • mysql
常见异常
  • 404
    • 地址错误
    • web.xml没配置servlet
    • 服务器启动出现错误 —> 由于加载当前项目出现的错误
    • 项目没有部署到tomcat中
    • 在代码跳转到位置路径写错
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值