servlet

1:为什么要出现JSP跟servlet?

前言:JSP跟servlet技术是用来开发动态网页的web技术;JSP在本质上就是一个servlet,servlet在本质上就是一个Java类;

(1)   除了Linux,unix操作系统之外,当前windows操作使用数量是最多的,C语言跟C++的存在,使得java在C/S模式开发下,受到了打压,所以java要想活下去,就得转向B/S模式的开发,就是web应用的开发

(2)   刚开始java语言在web应用的开发领域只有servlet,servlet就是一个java类,要想做网页,在类中就必须写大量的html标签,使得代码很乱,也不容易维护

(3)   为了解决html标签跟java代码的分离,就出现JSP技术,由JSP(Java server page)单独负责写html标签,servlet只负责写java代码,这样java代码跟html标签就可以想分离出来,不论是在项目开发还是维护上,都容易;

(4)   但是JSP是动态web开发技术,单独写html代码就成了html文件了,那是静态网页了;JSP中也允许写少量的java代码,这些java代码在JSP中称之为脚本元素,但是依据MVC的设计理念来看【MVC—model—view—controller:简单的来说就是servlet只写java代码,jsp只写html标签,供用户看的,java代码跟html不能凑合到一起】,脚本元素的存在也是不合理的;所以在JSP中就包含JSP标签【也称之为JSP动作àJSPAction】另外也有el(表达式语言)跟JSTL(JSP standardTag Library JSP标准标签库)这两项技术在JSP中代替java代码

 

2:javaweb常用名词解释

(1)web服务器:专门处理web服务请求,也就是HTTP协议的80端口或者是8080端口的数据处理以及交互。常用的Tomcat,jBoss,WebLogic,IIS(Internet Information Server)

应用服务器:通过特定的网络通道来传输数据进行特定的数据交互;FTP,网游服务器

广义上说web服务器也属于应用服务器的一种;

(2)JSP引擎,servlet引擎

Servlet引擎:servlet是由服务器调用的,而调用servlet的程序称为servlet程序

JSP引擎:服务器将JSP翻译成servlet的程序叫做JSP引擎;

 

3:Tomcat服务器的目录结构

 

Bin:启动跟关闭Tomcat的批处理文件存在里面

Conf:Tomcat的配置文件,里面的几个文件

(1)   servlet.xml:配置server的相关信息,Tomcat的启动端口;配置Host跟Context

Tomcat默认端口8080

问:一个Tomcat配置多个应用怎么做?

在该文件中配置service节点,名称要唯一,端口不能重复;

   

(2)   web.xml配置web应用的,但是这个文件跟我们自己开发的应用中的web.xml文件不是一个文件;这个文件中有一个servlet节点元素

我们自己的web.xml文件都会默认的继承该web.xml文件,如果用户的请求在我们web.xml中找不到资源,会去找这个DeaultServlet,找到了资源,返回给用户,找不到,则404错误;

(3)tomacat-users.xml:配置Tomcat的用户名跟密码

Lib:存放运行Tomcat的jar包,这里面有两个很重要的jar包

是servlet跟jsp的,在Eclipse下开发servlet,不需要导入这两个包,是因为Tomcat自带的;若是你的Tomcat没有启动,在导入别人的项目时候,会报错,因为支持servlet的jar包就是放在这个目录下的

Logs:存放日志文件的

Temp:临时目录

Webapps:我们自己开发的项目就得放在这个目录下,而且是有一定的存放规则的

       打开我们的项目中,在WebContent目录下的文件都存在第一级目录,写的servlet则存在WEB-INF目录下,classes目录放的是java类,lib目录是jar包,web.xml文件也在这里面

Work:Tomcat的工作目录,存放的是被访问后的jSP翻译成servlet的文件跟.class文件

要记住这个index-jsp文件

 

Jsp跟servlet开发遵循了MVC【model---view—controller】的开发模式:

Model:javabean、entity类,在hibernate中叫做PO类【persistent objects】

规则:

(1)   implements Serializable

(2)   无参构造函数

(3)   Setter getter方法

(4)   主键是Integer or Long类型

View:JSP页面

Controller:servlet

在SSH中会划分成表现层,业务逻辑层,数据访问层,持久层

 

                                          JSP  java server pages

 

jsp页面的执行过程:

       1.jsp页面在第一次被访问的时候,web容器(jsp引擎)会将jsp翻译成一个Servlet,然后调用servlet的service方法;;(jsp翻译后的Servlet会被放到tomcat的work目录下)

       2.当jsp页面被再次访问的时候,web容器会去直接调用Servlet的service()方法,所以通常来讲jsp只是在第一次被访问的时候比较慢

       3.如果jsp页面做了修改,此时web容器会重新翻译jsp

 

4:JSP文件包含什么?

(1)   注释

(2)   模板元素

(3)   脚本元素

(4)   指令元素

(5)   动作元素(也称JSP标签)

 

 

注释:<!-- --> <%--  --%>

第一种注释跟html的注释一样,第二种注释后,客户端查看不到被注释的代码,第一种可以查看到,第二种注释也是用于注释<%%>小脚本的;

 

模板元素:就是html标签或者XML元素

 

脚本元素:分为三种

脚本声明<%!   %> 要写在<html>之前

脚本表达式<%=   %>写表达式的地方

小脚本<%   %>写java代码

 

这三种脚本都对应在JSP翻译后的servlet文件中;上面的work目录:index_jsp.java

脚本声明是处于类中的成员属性的位置,后两者都存在_jspService()方法中;

例子

<%= new java.util.Date()%>  %前面不能有分号

<%

       Java.util.Datedate=new java.util.Date();

       out.print();

%>

 

其中JSP中html标签【模板元素】,是通过翻译后servlet的_jspService()方法中的out.print();打印给客户端的;

 

 

指令元素:

常用的是pageinclude taglib指令

<%@ page pageEncoding="UTF-8"%>

<%@ includefile="/include/common.jsp"%>【静态include】

<%@ taglib uri="http://java.sun.com/jsp/jstl/core"prefix="c"%>

<%@ tagliburi="http://java.sun.com/jsp/jstl/fmt"prefix="fmt"%>

其中page指令中还包括另外两个属性

<%@ page pageEncoding="UTF-8"isErrorPage="false" isELIgnored="false"%>

其中pageEncoding属性是解决JSP页面的中文乱码问题,默认是ISO8859-1,改为UTF-8;

page指令也可以用于导入java的类库

<%@page import=”java.util.Date”>

上面两句合并

<%@ page pageEncoding="UTF-8"isErrorPage="false" isELIgnored="false"%  import=”java.util.Date”>

 

****在写java代码中,一个类会默认的importjava.lang.*;

在JSP中,JSP引擎会自动导入以下的包

java.lang.*

javax.servlet.*

javax.servlet.jsp.*

javax.servlet.http.*

 

 

动作元素:jsp标签】

<jsp:includepage=included.jsp flush=true />【动态include】

<jsp:forword>

<jsp:param  >

//jsp与javaBean相关的三个标签

<jsp:userBean>

<jsp:setProperty>

<jsp:getProperty>

 

问:jsp中的动态include跟静态include的区别

(1)动态include用jsp:include动作实现<jsp:include page=include.jspflush=true>,总会检查所包含文件的变化,适用于包含动态的页面,并且可以带参数

静态include,用指令元素实现,不会检查所包含文件的变化,适用于包含静态页面

(2)<jsp:include>动态include设计到的两个jsp文件会被翻译成两个servlet

       <%@include file=””%>静态include会把两个jsp文件翻译成一个servlet,但是两者最后都是将内容合并为一个jsp文件,返回给客户

 

 

 

5:JSP中的九大隐式对象

JavaEE API

       request- javax.servlet.http.HttpServletRequest(Interface)

       response - javax.servlet.http.HttpServletReponse(Interface)

       session -javax.servlet.http.HttpSession(Interface)

       application- javax.servlet.ServletContext(Interface)

       config- javax.servlet.ServletConfig(Interface)

       pageContext- javax.servlet.jsp.PageContext(Class)

       out- javax.servlet.jsp.JspWriter(Class)

 

JavaSE API

       page- java.lang.Object(class)

       exception- java.lang.Throwable(class)

 

 

6:request对象的常有方法:

 

(1)setAttribute(Stringname,Object):设置名字为namerequest 的参数值

getAttribute(Stringname):返回由name指定的属性值

removeAttribute(Stringname):删除请求中的一个属性

(2)getSession([Booleancreate]):返回和请求相关Session

(3)getParameter(Stringname):获得客户端传送给服务器端的有name指定的参数值

(4) getParametervalues(Stringname):获得有name指定的参数的所有值

 

getCookies():返回客户端的所有Cookie 对象,结果是一个Cookie 数组

getCharacterEncoding():返回请求中的字符编码方式

getContentLength():返回请求的Body 的长度

getHeader(String name):获得HTTP 协议定义的文件头信息

getHeaders(String name):返回指定名字的request Header 的所有值,结果是一个枚举

的实例

getHeaderNames():返回所以request Header 的名字,结果是一个枚举的实例

getInputStream():返回请求的输入流,用于获得请求中的数据

getMethod():获得客户端向服务器端传送数据的方法

getParameterNames():获得客户端传送给服务器端的所有参数的名字,结果是一个枚

举的实例

getProtocol():获取客户端向服务器端传送数据所依据的协议名称

getQueryString():获得查询字符串

getRequestURI():获取发出请求字符串的客户端地址

getRemoteAddr():获取客户端的IP 地址

getRemoteHost():获取客户端的名字

getServerName():获取服务器的名字

getServletPath():获取客户端所请求的脚本文件的路径

getServerPort():获取服务器的端口号

getAttributeNames():返回request对象所有属性的名字集合,结果是一个枚举的实例

 

重点讲解红色标记的几个方法

 

(1)setAttribute()跟getAttribute()

request.setAttribute()常用于:当用户登录失败,可以给用户发送一个用户名或者密码错误的提示;例子:

 

UserAction class

request.setAttribute(“message”,”用户名或者密码错误”);

 

user.jsp中

request.getAttribute(“message”);

 

但是上面Action类中还缺一条语句,所以现在在jsp页面是获取不到错误信息的;request的setAttribute()涉及到一个很重要的问题:转发跟重定向的问题

页面的跳转就只有转发跟重定向两种方式:request,set后的属性必须通过转发的方式才能传递到JSP中;不能用sendRedirect(“user.jsp”)的方式,否则jsp中获取不到值,

修改上面的代码:

UserAction class

request.setAttribute(“message”,”用户名或者密码错误”);

    request.getRequestDispatcher(“user.jsp”).forword(request,response);

user.jsp中

request.getAttribute(“message”);

 

复习一下转发forword跟重定向redirect的区别:

【重定向:你找我借钱,我没有,你自己去找张三借】

【转  发:你找我借钱,我没有,我帮你去找张三借】

(1)Forword转发是控制权的转向,在浏览器地址栏中不会显示转向后的url

   Redirect会重新发送请求,浏览器地址栏会出现新的url

(2)前者更加高效,在不使用request.setAttrbute()的前提下,使用forword可以隐藏实际的请求地址;

  后者可以跳转到其他的服务器资源上;在SSH中,一个Actionextends BaseAction后,可以使用getRequest.put()的方式来代替该方法;

(3) 请求重定向客户端向服务器发送两次请求请求转发发送一次请求

 

request.removeAttribute()可以删除前面request.setAttribute()设置后的属性

 

(2)getSession()方法讲解

一般用在后台上;

request.getSession().setAttribute(“username”,username);

这个方式request.setAttribute()的区别就是

(a)session以及request的作用域范围不同;本方法主要用于,用户登录成功后,在本站点下的所有页面中都可以用session.getAttribute()的方法获取该用户的用户名

(b)方法的参数不一样,前者参数全是String类型,后者的参数,第一个参数是String,第二个参数是obj

(c)它既可以使用转发,也可以重定向;

 

 

 

(3)request.getParameter()

该方法主要在后台获取表单或者超链接提交过来的数据

例子:

request.getParameter(“username”);

request.getParameter(“password”);

 

(4)requet.getParameterValues()

用来获取表单中复选框提交过来的数据,在checkbox一定要写value的属性值,才可以获取值,否则没有返回值;

 

 

问:request.getAttribute() request.getParameter()有何区别?

(1)getAttribute()必须先setAttribute()才能使用,若是request.setAttribute();必须用forword的方式若是rquest.geSession().setAttribute()的方式,两种跳转方式都可以;

(2)getParameter()方法主要用来获取表单或者超链接提交过来的数据;

 

 

7:session对象的常有方法:

setAttribute()

getAttribute()

invalidate()

getId()

 

前两个方法在request对象的时候,已经说过;setAttribute(),需要request.getSession().setAttribute(String,Object)才能使用;

Session.getAttribute(String),用在JSP中

 

invalidate();-àrequest.getSession().invalidate();注销用户

getId();获取该session在创建的时候,JSP引擎为它设置的ID;

 

 

 

 

8:JSP的属性范围(四种会话跟踪技术,4个域对象)

(1)page的作用域只在当前页面中,调转到另一个页面,就会失效;

(2)request:从一次请求开始,在结果的返回,这是一次request的作用范围(可能涉及到多个page)

(3)session:从打开一个网站的首页(不包含登陆)开始,一直到注销(退出)登陆为止,都是session作用范围(多个用户跟一个用户的多次登陆都是session)

(4)application:(在服务器没有关闭的前提下,任何时刻,多个用户,一个用户的多次登陆,对网站的访问都在application的范围内);生命周期:web应用被加载的时候开始,web应用被移除或者服务器关闭的时候,生命周期结束

 

page----àpageContext

request--àrequest

session--àsession

application--àservletContext

 

 

 

9:Response

有 3 个 HTTP 响应头字段都可以禁止浏览器缓存当前页面,它们在 Servlet 中的示例代码如下:

response.setDateHeader("Expires",-1);

response.setHeader("Cache-Control","no-cache"); 

response.setHeader("Pragma","no-cache"); 

并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是三个响应头同时使用。

Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面

Cache-Control响应头有两个常用值:

no-cache指浏览器不要缓存当前页面。

max-age:xxx指浏览器缓存页面xxx秒。

 

 

                                             Servlet

11:servlet 的运行流程

Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:

①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。

②装载并创建该Servlet的一个实例对象。

③调用Servlet实例对象的init()方法。

④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。

⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

 

 

12:servlet的生命周期

Servlet一共有五个方法:init(),serveice(),destroy(),getServletConfig(),getServletInfo();其中前三个方法是与servlet生命周期相关的方法;

Servlet先被服务器实例化后,web容器运行其init()方法,初始化servlet,接着调用service()方法,根据用户的请求方式,分别调用doGet(),doPost()方法,当servlet结束服务的时候,容器调用destory()方法,将servlet销毁;

 

针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至服务器关闭或者web应用被移除的时候,servlet实例对象才会销毁。

Servlet的整个生命周期内,Servletinit方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servletservice方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servletservice()方法,service方法再根据请求方式分别调用doXXX方法。

 

 

13:servlet开发方式:

(1)   extends HttpServlet

(2)   extends GenericServlet

(3)   implements Servlet

通常是extendsHttpServlet,因为抽象类HttpServlet对Servle接口进行了功能扩展,提供了一些与HTTP协议相关的方法;

 

14:servlet配置

<web-app>

       <servlet>

              <servlet-name>userAction</servlet-name>

              <servlet-class>UserAction</servlet-class>

       </servlet>

       <servlet-mapping>

              <servlet-name>userAction</servlet-name>

              <url-pattern>/*</url-pattern>

       </servlet-mapping>

</web-app>

 

15:servelt结构

主要是doGet()跟doPost()方法,get请求一般都调用doPost()方法去处理

publicclass TestServletextends HttpServlet{

   

    @Override

    protectedvoid doGet(HttpServletRequest req, HttpServletResponse resp)

           throws ServletException, IOException {

       doGet(req, resp);

    }

    @Override

    protectedvoid doPost(HttpServletRequest req, HttpServletResponse resp)

           throws ServletException, IOException {

       //这一句是通过表单的隐藏域传递过来的值

       String action=req.getParameter("action");

       if (action.equals("register")) {

           register(req,resp);

       }  

    }

    privatevoidregister(HttpServletRequestreq, HttpServletResponse resp) {

      

       //写要操作的代码。。。。【包括JDBC

    }

}

 

 

 

ELJSTL

El:${rqeustScope.message}  ${sessionScope.message}

el表达式不能写if,for等逻辑语句,所以需要对web域中的list和map集合进行迭代就需要结合 jstl 迭代标签

 

Jstl:常用的<c:foreach ><c:if>

 

Filter

在项目中用Filter做中文乱码的处理,也需要对MySQL的配置文件进行修改

写一个类实现Filter接口,实现里面的init(),destroy(),doFilter(ServletRequest  arg0,ServletResponse arg1,FilterChain arg2);

 

 

EncodingFilter类跟servlet一样,写完之后,要在web.xml中进行配置,Listeren监听器也需要在web.xml中配置

 

 

 

 

JDBC[java data base connectivity]

1:JSBC的面向接口编程

Sun公司为了统一大家对数据库的操作,定义了一套API,这套API就称为JDBC;这些api都是接口,主要有具体的数据库厂商去实现,而实现这些接口的实现类就称之为数据库的驱动,存放在mysql的jar包中;

 

2:JDBC的操作步骤(四步)

加载数据库驱动---à取得跟数据库的连接--àCRUD操作-à关闭资源

 

3:JDBC要操作的类跟接口全部存放在java.sql包下

 

 

3:JDBC连接mysql或者Oracle,有几点区别就是,要加载的数据库驱动,以及url,user,password,而在Hibernate中,将会增加一个dialect(方言)的区别

下面列举出jdbc连接mysql跟oracle的驱动加载跟连接的代码

Mysql:

Class.forName(“com.mysql.jdbc.Driver”);或者直接new Driver();

url=”jdbc:mysql://localhost:3306/mysqlname”;或者url=”jdbc:mysql:///mysqlname”;

如果是本机ip,就用后一种方式

User=“root”,password=“root”

 

Oracle:

Class.foraName(“oracle.jdbc.driver.OracleDriver”);

url=”jdbc:oracle:thin:@localhost:1521:数据库名称”;

user=”scott”;  password=”tiger”;

 

 

一般将【加载数据库驱动,取得跟数据库连接,关闭资源】都放在一个util工具类中(工具类都是static的method,用类名调用)

 

publicclass DB {

   

    privatestaticfinal StringURL ="jdbc:mysql:///jdbctest";

    privatestaticfinal StringUSER ="root";

    privatestaticfinal StringPASSWORD ="sys";

 

    publicstatic Connection getConnection() {

       try {

           new Driver();

           return DriverManager.getConnection(URL,USER,PASSWORD);

       } catch (SQLException e) {

           e.printStackTrace();

       }

       returnnull;

    }

    publicstaticvoid close(ResultSet resultSet, PreparedStatementpreparedStatement, Connection connection) {

       if (resultSet !=null) {

           try {

              resultSet.close();

           } catch (SQLException e) {

              e.printStackTrace();

           }

       }

       if (preparedStatement !=null) {

           try {

              preparedStatement.close();

           } catch (SQLException e) {

              e.printStackTrace();

           }

       }

       if (connection !=null) {

           try {

              connection.close();

           } catch (SQLException e) {

              e.printStackTrace();

           }

       }

    }

 

}

 

 

4:jdbc中Date()的类型是java.sql.Date类型的,而不是java.util.Date

在entity类中定义Date类型的成员属性,就用java.util.Date,而在jdbc的prepareStatement.setDate(int,Date)的时候,传递的就应该是java.sql.Date类型的Date数据;

 

 

JDK中的定义

 

 

Java.sql.Date 是继承自java.util.Date的,在preparedStatement.setDate()的时候,要对util中的Date做个类型转换,转成sql中的Date;

在entity class中的Date是util中的Date类型,在servlet中使用request.getParameter(“date”);得到的是字符串类型的;

 

对未定参数date进行set

此时的date3就是sql包下的Date类型;

 

 

5:在jdbc的API中,除了DriverManage是类之外,常用操作的API都是接口,如

Driver,Connection,ResultSet,PreparedStatement,Statement,CallableStatement

 

DriverManage.getConnection(url,user,password);-à获取跟数据库的连接,处于JDBC操作的第二步

 

Connection中常见的方法,createStatement(),preparedStatement(String),commit,setAutoCommit(false),rollback();

因Statement存在SQL注入攻击的问题,所以用preparedment代替Statement;CallableStatement是有关存储过程的操作

后面三个是jdbc针对事务处理相关的方法;

 

Resuset:主要的方法next(),以及很多getXXX()方法

next()主要遍历结果集中的元素,使用getXXX()方法取出,一般使用getXXX(String);

 

 

6:JDBC对事务的处理

 

(1)    事务的概念:针对数据库进行的一组操作,包含多条SQL语句,比如从张三的账户扣除100元到李四的账户上,那么这件事情,要么全都做完,要么不做;谁都不能吃亏,谁也不能白拿钱;

(2)    事务的作用:将扣钱跟加钱的SQL放在同一事务中,一旦出现异常,没有损失;

(3)    事务实现原理:原先的SQL语句是自动提交的,执行事务的话,就需要手动的提交事务connection.setAutoCommit(false)。一旦在事务中间出现异常,也可以使用回滚connection.rollback()功能,避免损失,如果没有出现异常,那么在最后使用connection.commit()手动提交时事务

(4)    事务的四大特性:原子性,一致性,隔离性,持久性

原子性

原子性是事务最小的单元,不可再分割,这些操作必须全部完成,或者全部失败;张三转账给李四,李四接受张三的转账,都是不可再分的,有一个失败,则全部失败

一致性

指对数据库进行操作的前后,数据库中数据是一致的,即便转账失败,张三跟李四都没有受到损失;转账成功后,张三跟李四的金额数量,跟转账前的总金额也是一致的

隔离性

多个并发事务可以同时进行但是在操作的时候彼此之间不能进行访问,只有操作之后才可以访问数据;现在有张三李四两个账户:各2000元;现在也有两个事务T1跟T2,T1将对张三跟李四两个账户进行转账,而T2事务做的事情是:如果发现张三跟李四的账户总金额不足4000元的话,将对每个人的账户扣除500元;而T1的事务现在只做了一半,此时T2就来对账户进行检测,这样是不合理的;

持久性:

系统崩溃了,事务也能提交;提交后的数据,不可回滚,永久有效;

 

            //取消自动提交

           connection.setAutoCommit(false);

          

           preparedStatement.setString(1, title);

           preparedStatement.setString(2, author)

          

           try{

              preparedStatement.executeUpdate();

              //所有操作成功,提交事务

              connection.commit();

           }catch(Exception exception){

              //否则回滚

              connection.rollback();

           }

 

在Hibernate中,事务是默认关闭事务的自动提交的,需要手动提交

transaction.commit();

 

 

7:元数据

DatabaseMetaData跟ResultMetaData

区别

(1)    D~~~是通过connection.getMetaData()获取的

(2)    R~~~是通过preparedStatement.getMetaData()获取的

 

(3)    D~~~~是获取数据库的相关信息

(4)    R~~~~是获取结果集的相关信息

 

 

 

8:数据库连接池:DBCP

就是数据库连接对象的一个缓存池,传统的方式,dao层完成数据库查询更新操作后,就会断开与数据库的连接,但是数据库的连接是非常耗费时间的,现在先创建一个跟数据库连接的缓存池,这样用户发送请求过来后,dao不需要再次连接数据库,直接从连接池获取就行李,用完之后也不需要关闭;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值