JavaWeb

1、初识JavaWeb


1.1、什么是JavaWeb?

JavaWeb是指用Java技术来解决Web互联网领域的技术栈。Web包括:Web客户端和Web服务端两部分

Web开发:

  • Web(World Wide Web),即全球广域网,也称为万维网,它是一种基于超文本和HTTP的、全球性的、动态交互的、跨平台的分布式图形信息系统。是建立在Internet上的一种网络服务,为浏览者在Internet上查找和浏览信息提供了图形化的、易于访问的直观界面

  • 静态Web

    • HTML、CSS
    • 早些年,提供给所有人观看的数据始终不会发生变化!
  • 动态web

    • 现在,几乎所有的网站都是动态Web的
    • 提供给所有人观看的数据会发生变化,每个人在不同的时间、地点看到的信息各不相同
    • 技术栈:Servlet/JSP、ASP、PHP

2、Web服务器


2.1、技术栈

ASP:

  • 微软:国内最早流行的就是ASP
  • 在HTML嵌入了VB的脚本,ASP+COM
  • 在ASP开发中,基本一个页面都有几千行的业务代码,页面及其紊乱(维护成本高)
  • C#

PHP:

  • PHP开发速度快,功能强大,跨平台,代码简单(70%,WP)
  • 无法承载大访问量的情况(局限性)

JSP/Servlet:

BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端(维护方便、体验一般)

CS架构:Client/Server,客户端/服务器架构模式(开发、维护麻烦,体验较好)

  • SUN公司主推的B/S架构
  • 基于Java语言的(所有的大公司,或者一些开源的组件,都是用Java写的)
  • 可以承载三高(高性能、高可用、高并发)问题带来的影响;

2.2、WEB服务器-Tomcat

什么是服务器?

服务器硬件:也是计算机,只不过服务器要比我们平常使用的计算机在各方面性能好很多!

服务器只是一台设备,必须安装服务器软件才能提供相应的服务

Tomcat

Tomcat服务器软件是一个免费的开源的Web应用服务器。是Apache软件基金会的一个核心项目。由 Apache,Sun和其他一些公司及个人共同开发而成

由于Tomcat只支持Servlet/JSP少量JavaEE规范,所以是一个开源免费的轻量级Web服务器

JavaEE规范: Java Enterprise Edition(Java企业版) JavaEE规范就是指Java企业级开发的技术规范总和。包含13项技术规范:JDBC、JNDI、EJB、 RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF

因为Tomcat支持Servlet/JSP规范,所以Tomcat也被称为Web容器、Servlet容器。JavaWeb程序需要依赖Tomcat才能运行

下载与安装

官网

解压缩安装到指定目录即可!

2.3、tomcat启动和配置

目录结构

在这里插入图片描述

启动和关闭Tomcat

Tomcat的默认端口为8080,所以在浏览器的地址栏输入: http://127.0.0.1:8080,即可访问 tomcat服务器!

关闭:

  • ctrl+c:正常关闭
  • 运行命令:bin/shutdown.bat:正常关闭
  • 直接关闭运行窗口,强制关闭

可能遇到的问题

  1. Java环境变量没有配置。因为Tomcat是用Java写的,所以运行需要JRE,JDK包含JRE(配置环境变量即可)
  2. 控制台乱码问题

乱码问题解决方案:修改conf/logging.prooperties文件解决,修改为GB

可以配置启动的端口号

  • tomcat的端口号为:8080
  • mysql:3306
  • http:80
  • https:443

如果端口被占用,可在conf/server.xml配置端口号

<Connector port="80" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

Tomcat部署项目:将编写好的项目放到 webapps目录下,即可部署完成!

3、HTTP协议


3.1、什么是HTTP协议?

HTTP:Hyper Text Transfer Protocol(超文本传输协议),规定了浏览器与服务器之间数据传输的规则

  • http是互联网上应用最为广泛的一种网络协议
  • http协议要求:浏览器在向服务器发送请求数据时,或是服务器在向浏览器发送响应数据时,都必须按照固定的格式进行数据传输

3.2、HTTP协议特点

  • 基于TCP协议:面向连接,安全
    • TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层 通信协议,在数据传输方面更安全
  • 基于请求-响应模型:一次请求对应一次响应(先请求后响应)
    • 请求和响应是一 一对应关系,没有请求,就没有响应
  • HTTP协议是无状态协议:对于数据没有记忆能力。每次请求和响应都是独立的
    • 无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息
    • 缺点:多次请求间不能共享数据
    • 优点:速度快

3.3、HTTP-请求协议

浏览器和服务器是按照HTTP协议进行数据通信的

HTTP协议又分为:请求协议和响应协议

请求协议:浏览器将数据以请求格式发送到服务器

  • 包括:请求行、请求头 、请求体

响应协议:服务器将数据以响应格式返回给浏览器

  • 包括:响应行 、响应头 、响应体
GET和POST请求的区别
区别方式GET请求POST请求
请求参数请求参数在请求行中请求参数在请求体中
请求参数长度请求参数长度有限制(浏览器的不同限制也不同)请求参数长度无限制
安全性安全性低。原因:请求参数暴露在浏览器地址栏中安全性相对高

3.4、HTTP-响应协议

与HTTP的请求一样,HTTP响应的数据也分为3部分:响应行、响应头 、响应体

响应状态码
状态码说明
1xx响应中,临时状态码。表示请求已经接受,告诉客户端应该继续请求或者如果已经完成则忽略
2xx成功,表示请求已经被成功接收,处理已完成
3xx重定向,重定向到其它地方,让客户端再发起一个请求以完成整个处理
4xx客户端错误,处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等
5xx服务器端错误,处理发生错误,责任在服务端,如:服务端抛出异常,路由出错, HTTP版本不支持等

详解

4、Maven


4.1、什么是Maven?

为什么要学习Maven

  • 在JavaWeb开发中,需要使用大量的jar包,需要手动去导入
  • 如何能够让一个工具自动帮助我导入和配置这个jar包;由此,Maven就诞生了!

Maven是Apache旗下的一个开源项目,是一款用于管理和构建java项目的工具

Maven的核心思想:约定大于配置

官网

Apache 软件基金会,成立于1999年7月,是目前世界上最大的最受欢迎的开源软件基金会,也是一个专门为支持开源项目而生的非盈利性组织

Maven的作用
  • 依赖管理:方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题
  • 统一项目结构:提供标准、统一的项目结构
  • 项目构建:maven提供了标准的、跨平台(Linux、Windows、MacOS) 的自动化项目构建方式
    • 清理、编译、打包、测试、发布

Maven模型

  • 项目对象模型 (Project Object Model)
  • 依赖管理模型(Dependency)
    • 坐标,就是资源(jar包)的唯一标识,通过坐标可以定位到所需资源(jar包)位置(gav)
  • 构建生命周期/阶段(Build lifecycle & phases)

之前在项目中需要jar包时,直接就把jar包复制到项目下的lib目录,而现在书写在pom.xml文件中的坐标又是怎么能找到所要的jar包文件的呢?Maven仓库

Maven仓库

仓库:用于存储资源,管理各种jar包

仓库的本质就是一个目录(文件夹),这个目录被用来存储开发中所有依赖(就是jar包)和插件

Maven仓库分为:

  • 本地仓库:自己计算机上的一个目录(用来存储jar包)
  • 中央仓库:由Maven团队维护的全球唯一的。仓库地址
  • 远程仓库(私服):一般由公司团队搭建的私有仓库

当项目中使用坐标引入对应依赖jar包后,首先会查找本地仓库中是否有对应的jar包

  • 如果有,则在项目直接引用
  • 如果没有,则去中央仓库中下载对应的jar包到本地仓库

还可以搭建远程仓库(私服),将来jar包的查找顺序则变为: 本地仓库 —> 远程仓库—> 中央仓库

4.2、下载安装并配置Maven

官网

下载之后解压缩到指定位置即可;

目录结构如下:

  • bin目录 : 存放的是可执行命令
  • conf目录 :存放Maven的配置文件
  • lib目录 :存放Maven依赖的jar包。(Maven也是基于java开发的,所以它也依赖其他的jar包)
配置环境变量
  • 在系统变量处新建一个变量MAVEN_HOME

    • MAVEN_HOME环境变量的值,设置为maven的解压安装目录
  • 在Path中进行配置

    • PATH环境变量的值,设置为:%MAVEN_HOME%\bin
  • 使用mvn -version测试是否配置成功(在cmd窗口中)

配置本地仓库

在maven安装目录中新建一个目录:MavenRepository(本地仓库,用来存储jar包)

进入到conf目录下修改settings.xml配置文件,大概50行左右

<!-- maven本地仓库地址 -->
<localRepository>E:\Environment\apache-maven-3.8.1\MavenRepository</localRepository>
配置阿里云私服

由于中央仓库在国外,所以下载jar包速度可能比较慢,而阿里公司提供了一个远程仓库,里面基本也都有开源项目的jar包

进入到conf目录下修改settings.xml配置文件,大概160行左右,在<mirrors></mirrors>标签中添加 mirror 子节点:

<!-- 阿里云(私服)远程仓库 -->
<mirror>
    <id>aliyunmaven</id>
    <mirrorOf>*</mirrorOf>
    <name>阿里云公共仓库</name>
    <url>https://maven.aliyun.com/repository/public</url>
</mirror>

4.3、IDEA中集成Maven

  1. 启动IDEA
  2. 创建一个mavenweb项目

  1. 等待项目初始化完毕

注:项目创建成功后,看下Maven的配置!

4.4、创建一个普通的Maven项目

Maven项目的目录结构

first-maven-project

— src (源代码目录和测试代码目录)

  --- main (源代码目录)

  	--- java (源代码java文件目录)

  	--- resources (源代码配置文件目录)

  --- test (测试代码目录)

  	--- java (测试代码java目录)

  	--- resources (测试代码配置文件目录)

— target (编译、打包生成文件存放目录)

注:在web应用下才会出现

4.5、标记文件夹功能

4.6、在IDEA中配置tomcat


解决警告问题
必要的配置:为什么会有这个问题?访问一个网站,需要指定文件夹名字!

启动
img

4.7、pom文件

POM (Project Object Model) :指的是项目对象模型,用来描述当前的maven项目

使用pom.xml文件来实现

<?xml version="1.0" encoding="UTF-8"?>

<!--Maven的版本和头文件-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- POM模型版本 -->
    <modelVersion>4.0.0</modelVersion>

    <!--  当前项目坐标 GAV-->
    <groupId>com.baidu</groupId>
    <artifactId>javaweb-01-maven</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <!-- 项目的打包方式
        jar:java应用
        war:javaWeb应用
    -->
    <packaging>war</packaging>

    <name>javaweb-01-maven Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>
    
    <!-- 配置 -->
    <properties>
        <!-- 项目的默认构建编码 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- 编译版本 -->
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>
    
    <!-- 项目依赖 -->
    <dependencies>
        <!-- 具体依赖的jar包配置文件 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
    </dependencies>
</project>

maven由于它的约定大于配置,之后可以会遇到我们写的配置文件,无法被导出或者生效的问题,解决方案:

<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

4.8、依赖管理

依赖传递和依赖排除

直接依赖:在当前项目中通过依赖配置建立的依赖关系

间接依赖:被依赖的资源如果依赖其他资源,当前项目间接依赖其他资源

排除依赖:指主动断开依赖的资源。(被排除的资源无需指定版本)

<!-- 排除依赖, 主动断开依赖的资源 -->
<exclusions>
    <exclusion>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </exclusion>
</exclusions>
依赖范围

在项目中导入依赖的jar包后,默认情况下,可以在任何地方使用

作用范围:

  • 主程序范围有效(main文件夹范围内)
  • 测试程序范围有效(test文件夹范围内)
  • 是否参与打包运行(package指令范围内)
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

给junit依赖通过scope标签指定依赖的作用范围。 那么这个依赖就只能作用在测试环境,其他环境下不能使用

scope标签的取值范围:

scope值主程序测试程序打包(运行)范例
compile(默认)YYYlog4j
test-Y-junit
providedYY-servlet-api
runtime-YYjdbc驱动
生命周期

Maven的生命周期就是为了对所有的构建过程进行抽象和统一。 描述了一次项目构建,经历哪些阶段

Maven对项目构建的生命周期划分为3套(相互独立):

  • clean:清理工作
  • default:核心工作。如:编译、测试、打包、安装、部署等
  • site:生成报告、发布站点等

常用生命周期命令

  • clean:移除上一次构建生成的文件
  • compile:编译项目源代码
  • test:使用合适的单元测试框架运行测试(junit)
  • package:将编译后的文件打包,如:jar、war等
  • install:安装项目到本地仓库

生命周期的顺序是:clean —> validate —> compile —> test —> package —> verify —> install —> site —> deploy

注:在同一套生命周期中,在执行后面的生命周期时,前面的生命周期都会执行

执行可通过idea可视化界面操作或者打开cmd窗口输入:mvn clean/test/package...

5、Servlet


5.1、什么是Servlet?

  • Servlet是Sun公司开发动态web的一门技术

  • Servlet是JavaEE规范之一,规范就是接口

  • Servlet是JavaWeb三大组件之一,三大组件分别是:Servlet程序、Filter过滤器、Listener监听器

  • Servlet是运行在服务器上的一个java小型程序,它可以接收客户端发送过来的请求,并响应数据给客户端

  • sun在这些API中提供一个接口:Servlet,如果想开发一个Servlet程序,只需要完成两个小步骤

    • 编写一个类,实现Servlet接口
    • 把开发好的Java类部署到web服务器中

把实现了Servlet接口的Java程序叫做:Servlet
Servlet可以动态生成HTML内容从而对客户端进行响应

5.2、Servlet生命周期

1.执行Servlet构造器方法
2.执行init初始化方法
第一、二步,是在第一次访问的时候创建Servlet程序会调用
3.执行service方法
第三步,每次访问都会调用
4.执行destroy销毁方法
第四步,在web工程停止的时候调用

5.3、Servlet类的继承体系

Servlet接口有两个默认的实现类:HttpServlet、GenericServlet

  • Servlet接口,只负责定义Servlet程序的访问规范
  • GenericServlet类实现了Servlet接口,做了很多实现,并持有一个ServletConfig类的引用,并对ServletConfig的使用做了一些方法
  • 该类抽取类实现了service()方法,并实现了请求的分发处理
  • 我们只需要根据自己的业务需要去重写doGet或doPost方法即可
  1. 构建一个普通Maven项目,删除里面的src目录,在这个项目里面建立Module;这个空的工程就是Maven主工程

  2. 环境优化

    1. 修改web.xml为最新的
    2. 将maven的结构搭建完整
  3. 编写一个servlet程序

    1. 编写一个普通类
    2. 实现Servlet接口,直接继承HttpServlet,根据自己的业务需要去重写doGet或doPost方法即可!
    public class HelloServlet extends HttpServlet {
        //由于get或者post只是请求实现的不同的方式,可以相互调用,因为业务逻辑都一样
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //ServletOutputStream outputStream = resp.getOutputStream();
            PrintWriter writer = resp.getWriter();//响应流
            writer.println("hello,Servlet!");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    }
    
  4. 编写servlet的映射

为什么需要映射:写的是java程序,但要通过浏览器访问,而浏览器需要连接web服务器,所以要在web服务注册我们写的servlet。还需要给他能够访问的路径

<!--servlet标签给Tomcat配置Servlet程序-->
<servlet>
	<!--servlet-name标签 给Servlet程序起一个别名(一般是类名)-->
    <servlet-name>hello</servlet-name>
    <!--servlet-class是Servlet程序的全类名-->
    <servlet-class>com.zhang.servlet.HelloServlet</servlet-class>
    <!--init-param是初始化参数!-->
        <init-param>
            <!--参数名-->
            <param-name>username</param-name>
            <!--参数值-->
            <param-value>root</param-value>
        </init-param>
</servlet>

<!--servlet-mapping标签给Servlet程序配置访问地址-->
<servlet-mapping>
<!--servlet-name标签的作用是告诉服务器,我当前配置的地址给那个Servlet程序使用-->
    <servlet-name>hello</servlet-name>
        <!--url-pattern标签配置访问地址-->
        <!--
            / 斜杆在服务器解析的时候,表示地址为:http://ip地址:端口号/工程路径
            /hello 表示地址为:http://ip地址:端口号/工程路径/hello
            配置的url-pattern(资源路径)就是你要访问的地址!
        -->
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 配置tomcat

    注:配置项目发布的路径就OK了

  2. 启动测试

5.4、Servlet原理

Servlet是由Web服务器调用,web服务器是在收到浏览器请求之后会:

5.5、ServletContext

  • ServletContext类是一个接口,它表示Servlet上下文对
  • 一个web工程,只有一个ServletContext对象实例
  • Servlet对象是一个域对象
  • ServletContext是在web工程部署启动的时候创建,在web工程停止的时候销毁!

1、共享数据

  • 我在这个Servlet保存的数据,可以在另外一个servlet拿到
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //this.getInitParameter();      // 初始化参数
        //this.getServletConfig();      // servlet配置
        //this.getServletContext();     // servlet上下文
        ServletContext context = this.getServletContext();
        context.setAttribute("username","张三");  // 将一个数据保存着在servletContext   键=>值对
    }
}
public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String username = (String) context.getAttribute("username");
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().println("名字是:" + username);
    }
}
<servlet>
    <servlet-name>getServlet</servlet-name>
    <servlet-class>com.zhang.servlet.GetServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>getServlet</servlet-name>
    <url-pattern>/getServlet</url-pattern>
</servlet-mapping>

测试访问结果

2、获取初始化参数

<!--配置一些web应用初始化参数-->
<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://127.0.0.1:3306//mybatis</param-value>
</context-param>
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext servletContext = this.getServletContext();
    String url = servletContext.getInitParameter("url");
    resp.getWriter().println(url);
}

3、请求转发

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext servletContext = this.getServletContext();
    System.out.println("进入了s3");
    // RequestDispatcher dispatcher = servletContext.getRequestDispatcher("/gp");      //转发的请求路径
    // dispatcher.forward(req,resp);   //调用forward实现请求转发
    servletContext.getRequestDispatcher("/s2").forward(req,resp);
}

4、读取资源文件

Properties

  • 在Java目录下新建properties
  • 在resources目录下新建properties

发现:都被打包了同一个路径下,classes,俗称这个路径为classpath

需要一个文件流:

username = root
password = 123456
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
    Properties prop = new Properties();
    prop.load(is);
    String user = prop.getProperty("username");
    String pwd = prop.getProperty("password");
    resp.getWriter().println(user + ":" + pwd);
}

访问测试即可ok;

5.6、HttpServletRequest

  • 获取请求数据:HttpServletRequest
  • 设置响应数据:HttpServletResponse

每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析好封装到Request对象中然后传递到service 方法( doGet和doPost)中给我们使用。可以通过HttpServletRequest对象,从而获取到所有请求的信息

1、获取前端传递的参数

2、请求转发

方法名称说明
String getParameter(String name)根据表单组件名称获取提交数据
String[ ] getParameterValues(String name)获取表单组件对应多个值时的请求数据
// 后台接收中文乱码问题
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("====================");
System.out.println(username);
System.out.println(pwd);
System.out.println(Arrays.toString(hobbys));
System.out.println("====================");
// 通过请求转发
req.getRequestDispatcher("/success.jsp").forward(req, resp);

5.7、HttpServletResponse

HttpSerletRespise类和HttpServletRequest类一样。每次请求进来Tomcat 服务器都会创建一个Response对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,可以通过HttpServletResponse对象来进行设置

1、简单分类

负责向浏览器发送数据的方法

// 字节流  常用于下载(传递二进制数据)
ServletOutputStream getOutputStream() throws IOException;
// 字符流	 常用于回传字符串(常用)	
PrintWriter getWriter() throws IOException;

注:两个流同时只能使用一个,如果使用了字节流,就不能再使用字符流,反之亦然

2、常见应用

  1. 向浏览器输出消息
  2. 下载文件
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1. 获取下载文件的路径
    String realPath = "E:\\Program Files (x86)\\IntelliJ IDEA Project\\Jsp\\javaweb-01-servlet\\target\\classes\\6.jpeg";
    System.out.println("下载的文件的路径:" + realPath);
    //2. 下载的文件名是什么?
    String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
    //3. 设置想办法让浏览器支持(Content-disposition)下载我们需要的东西      中文文件名URLEncoder.encode编码,否则可能乱码
    resp.setHeader("Content-Disposition", "attachment;filename" + URLEncoder.encode(fileName,"utf-8"));
    //4. 获取下载文件的输入流
    FileInputStream in = new FileInputStream(realPath);
    //5. 创建缓冲区
    int len = 0;
    byte[] buffer = new byte[1024];
    //6. 获取OutputStream对象
    ServletOutputStream out = resp.getOutputStream();
    //7. 将FileOutStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
    while ((len = in.read(buffer)) > 0) {
        out.write(buffer, 0, len);
    }
    //8. 关闭流
    in.close();
    out.close();
}

3、实现重定向

B一个web资源收到客户端A请求后,B它会通知A客户端区访问另外一个web资源C,这个过程叫重定向

常见场景:用户登录

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.sendRedirect("/login.jsp");  // 重定向
}

面试题:请你聊聊重定向和转发的区别?

请求转发是指:服务器收到请求后,从一次资源跳转到另一个资源的操作叫请求转发
请求重定向是指:客户端给服务器发请求,然后服务器告诉客户端说。我给你一个地址。你去新地址访问。叫请求重定向

请求转发的特点:

  • 浏览器地址栏无变化
  • 它们是一次请求
  • 所以他们共享request域中的数据
  • 可以转发到WEB-INF目录下
  • 不可以访问工程外的资源

请求重定向的特点:

  • 浏览器地址栏会发生变化
  • 两次请求
  • 不共享Request域中数据
  • 不能访问WEB-INF目录下的资源
  • 可以访问工程外的资源

6、Cookie、Session


6.1、什么是会话?

  • 在日常生活当中,会话指的就是谈话、交谈
  • 在web开发当中,会话指的就是浏览器与服务器之间的一次连接,我们就称为一次会话,在一次会话当中,是可以包含多次请求和响应的
    • 浏览器不能关闭、服务器不能断开,否则该次会话结束!

7.2、保存会话的两种技术

  • Cookie(客户端会话跟踪技术)
    • 数据存储在客户端浏览器当中
  • Session(服务端会话跟踪技术)
    • 数据存储在储在服务端

常见场景:网站登录之后,你下次不用登录了,第二次访问直接就登录上去了

  1. 从请求中拿到Cookie信息
  2. 服务器响应给客户端
Cookie[] cookies = req.getCookies();		// 获取Cookie
cookie.getName();		// 获取Cookie的key
cookie.getValue();		// 获得Cookie的值
new Cookie("lastLoginTime", System.currentTimeMillis() + "");	// 新建一个Cookie
cookie.setMaxAge(24 * 60 * 60);		// 设置Cookie的有效期
resp.addCookie(cookie);		// 响应给客户端一个Cookie
  • 响应头 Set-Cookie :设置Cookie数据的
  • 请求头 Cookie:携带Cookie数据的

删除Cookie:

  • 不设置有效期,关闭浏览器,自动失效;
  • 设置有效期时间为0;

优缺点

  • 优点:HTTP协议中支持的技术(像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带,都是浏览器自动进行的,是无需我们手动操作的)
  • 缺点:
    • 移动端APP(Android、IOS)中无法使用Cookie
    • 不安全,用户可以自己禁用Cookie
    • Cookie不能跨域

7.4、Session

优缺点

  • 优点:Session是存储在服务端的,安全
  • 缺点: 服务器集群环境下无法直接使用Session
    • 移动端APP(Android、IOS)中无法使用Cookie
    • 用户可以自己禁用Cookie
    • Cookie不能跨域

Session和Cookie的区别:

会话自动过期:web.xml配置

<!--设置session默认注销的时间-->
<session-config>
    <!-- 15分钟后session自动失效,以分钟为单位-->
    <session-timeout>15</session-timeout>
</session-config>

7、JSP (Java Server Pages)


7.1、什么是Jsp?

JSP (Java Server Pages):Java服务器端页面,也和Servlet一样,用于动态web技术!

jsp的主要作用是代替Servlet程序回传html页面的数据

因为Servlet程序回传html页面数据是一件非常繁锁的事情。开发成本和维护成本都极高

最大的特点:

  • 写Jsp就像在写HTML,较为方便
  • 区别:
    • HTML只给用户提供静态的数据
    • Jsp页面中可以嵌入Java代码,为用户提供动态数据

7.2、Jsp原理

Jsp到底是如何执行的?

  • 在代码层面和上看不出任何问题!
  • 服务器内部工作
    • tomcat中有一个work目录;
    • IDEA中使用tomcat的会在IDEA的tomcat中产生一个work目录

发现页面转变成了Java程序!

浏览器想服务器发送请求,不管访问什么资源,实际上都是在访问Servlet!

Jsp最终也会被转换成为一个Java类,Jsp本质上就是一个Servlet程序

//初始化
public void _jspInit() {
}
//销毁
public void _jspDestroy() {
}
//JspService
public void _jspService(HttpServletRequest request, HttpServletResponse response)

7.3、JSP基础语法

任何语言都有自己的语法,JSP 作为Java技术的一种应用, 它拥有一些自己扩充的语法,Java中的所有语法都支持!

jsp表达式

<%--jsp表达式 作用:用来将程序的输出,输出到客户端
    <%=变量或者表达式%>
--%>
<%=new java.util.Date()%>

jsp脚本片段

<%--jsp脚本片段--%>
<%
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
        sum += i;
    }
    out.print("<h1>Sum=" + sum + "</h1>");
%>

<!--打印5次hello,world!-->
<%
    for (int i = 1; i <= 5; i++) {
%>
    <h2>hello,world!<%=i%></h2>
<%
    }
%>

jsp声明

<%--jsp声明--%>
<%!
    static {
        System.out.println("Loading Servlet!");
    }

    private int globalVar = 0;

    public void sayHello(){
        System.out.println("hello,jsp!");
    }
%>

jsp声明:会被编译到Jsp生成Java的类中!其他的,就会生成到_jspSServlet方法中!

在Jsp中,嵌入Java代码即可!

<%%>	<!--脚本片段 -->
<%=%>	<!--表达式 -->
<%!%>	<!--声明 -->
<!--我是html的注释-->		<!--注释 -->
<%--我是Jsp的注释--%>

Jsp的注释,不会再客户端显示,html注释会在客户端显示!

小结:

7.4、JSP指令

<%@ page language="java" import="java.util.*,java.text.*" contentType="text/html; charset=utf-8" %>
  • language:指定JSP页面使用的脚本语言
  • import:通过该属性来引用脚本语言中使用到的类文件
  • contentType:用来指定JSP页面所采用的编码方式
<%--设置页面编码--%>
<%@ page pageEncoding="utf-8" %>
<%--定制错误页面--%>
<%@ page errorPage="error/500.jsp"%>
<%--显示的声明这是一个错误页面--%>
<%@ page isErrorPage="true"%>
<%--设置公共部分--%>
<%@ include file="common/header.jsp"%>
<%--设置公共部分--%>
<%@ include file="common/header.jsp"%>
<h2 class="content">页面主体</h2>
<%--设置公共部分--%>
<%@ include file="common/footer.jsp"%>

<%--jsp标签--%>
<jsp:include page="common/header.jsp"/>
<h2 class="content">页面主体</h2>
<jsp:include page="common/footer.jsp"/>

两者区别:

  • @include会将两个页面合二为一
  • jsp标签:拼接页面,本质上还是三个

7.5、9大内置对象

  • pageContext
  • request 请求对象
  • Response 响应对象
  • Session 会话对象
  • Application ServletContext对象
  • config ServletConfig对象
  • out jsp输出流对象
  • page 指向当前jsp的对象
  • exception 异常对象
<%
   //存放数据
   //(PageContextImpl类) 	保存的数据值能在一个页面中有效
   pageContext.setAttribute("name1", "小张");   
   // (HttpServletRequest类)	一次请求内有效
   request.setAttribute("name2", "小赵"); 
   //(HttpSession类)	 一个会话范围内有效(打开浏览器访问服务器,直到关闭浏览器)   默认30分钟    	
   session.setAttribute("name3", "小林");
   // (ServletContext类) 整个web工程范围内都有效(只要web工程不停止,所有数据都在)
   application.setAttribute("name4", "小李");   
%>

<%
   //通过pageContext取出保存的值,通过寻找的方式来找
   //从底层到高层(作用域): page-->request-->session-->application
   //JVM:双亲委派机制
   String name1 = (String) pageContext.findAttribute("name1");
   String name2 = (String) pageContext.findAttribute("name2");
   String name3 = (String) pageContext.findAttribute("name3");
   String name4 = (String) pageContext.findAttribute("name4");
   String name5 = (String) pageContext.findAttribute("name5");     //不存在
%>

<%--使用EL表达式输出  ${} --%>
<h1>取出的值为</h1>
<h2>${name1}</h2>
<h2>${name2}</h2>
<h2>${name3}</h2>
<h2>${name4}</h2>
<%--自动过滤掉--%>
<h2>${name5}</h2>

request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!

session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车

application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数据

7.6、Jsp标签、JSTL标签库、EL表达式

<!-- JSTL表达式依赖 -->
<dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>
<!-- stardust标签库 -->
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

EL表达式:${ }

  • 获取数据
  • 执行运算
  • 获取web开发的常用对象

Jsp标签

<jsp:include page="header.jsp"></jsp:include>
<%--http:localhost:8082/jsp04.jsp?name=zhangtao&age=19--%>
<jsp:forward page="jsp04.jsp">
    <%-- 携带参数--%>
    <jsp:param name="name" value="zhang"/>
    <jsp:param name="age" value="19"/>
</jsp:forward>

JSTL表达式

JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,可以供我们使用,标签的功能和java代码一样!

核心标签

JSTL标签库使用步骤

  • 引入对应的taglib
  • 使用其中的方法
  • 在tomcat也需要引入jstl的包,否则会报错;jstl解析错误

c:if

<form action="coreif.jsp" method="get">
    <%--
    EL表达式获取表单的数据
    ${param.参数名}
    --%>
    <input type="text" name="username" value="${param.username}">
    <input type="submit" value="登录"/>
</form>
<%--判断,如果提交的用户名是管理员,则登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
    <c:out value="管理员,欢迎你!"/>
</c:if>
<%--自闭和标签--%>
<c:out value="${isAdmin}"/>

c:choose c:when

<%--定义一个变量score,值为85--%>
<c:set var="score" value="98"/>
<c:choose>
    <c:when test="${score>=90}">你的成绩为优秀!</c:when>
    <c:when test="${score>=80}">你的成绩为一般!</c:when>
    <c:when test="${score>=70}">你的成绩为良好!</c:when>
    <c:when test="${score<=60}">你的成绩为一般般,继续努力!</c:when>
</c:choose>

c:forEach

<%
ArrayList<String> person = new ArrayList<>();
person.add("张三");
person.add("李四");
person.add("王五");
person.add("赵六");
request.setAttribute("list", person);
%>
<%--
    var,每一次遍历处理的变量
    items,要遍历的对象
    begin,  哪里开始
    end,    到哪里
    step, 步长
    --%>
<c:forEach var="person" items="${list}">
    <c:out value="${person}"/><br/>
</c:forEach>
<hr/>
<c:forEach var="person" items="${list}" begin="1" end="3" step="1">
    <c:out value="${person}"/><br/>
</c:forEach>

8、Filter过滤器


8.1、什么是过滤器?

  • Filter过滤器它是JavaWeb的三大组件之一。三大组件分别是:Servlet程序、Listener 监听器、Filter 过滤器
  • Filter过滤器它是JavaEE的规范。也就是接口
  • Filter 过滤器它的作用是:拦截请求,过滤响应

拦截请求常见的应用场景有
权限检查
日记操作
事务管理

8.2、Filter过滤器的使用步骤

  1. 编写一个类实现Filter接口
  2. 实现过滤方法 doFilter()
  3. 到web.xml去配置Filter的拦截路径

代码示例:

public class AdminFilter implements Filter {
	@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpSession session = req.getSession();
        Object user = session.getAttribute("user");
        // 如果等于空,说明用户还没有登录!
        if (user == null) {
            HttpServletResponse resp = (HttpServletResponse) servletResponse;
            resp.sendRedirect(req.getContextPath() + "/login.jsp");
        } else {
            // 让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }
}
<!--filter标签用于配置一个Filter过滤器-->
    <filter>
        <!--给Filter起一个别名-->
        <filter-name>AdminFilter</filter-name>
        <!--配置Filter的全类名-->
        <filter-class>com.zhang.filter.AdminFilter</filter-class>
        <init-param>
            <param-name>username</param-name>
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3303/flower</param-value>
        </init-param>
    </filter>
    <!--filter-mapping 配置Filter过滤器的拦截路径-->
    <filter-mapping>
        <!--表示当前的拦截路径给那个filter使用-->
        <filter-name>AdminFilter</filter-name>
        <!--
            url-pattern 表示:http://ip:port/工程路径/     映射到IDEA的web目录
            /admin/*    表示请求地址到 http://ip:port/工程路径/admin/*
        -->
        <url-pattern>/admin/*</url-pattern>
    </filter-mapping>

8.3、Filter的生命周期

1.构造器方法
2.init初始化方法
第1、2步,在web工程启动的时候执行(Filter已经创建)
3.doFilter过滤方法
第3步,每次拦截请求,就会执行
4.destroy销毁
第4步,停止web工程的时候,就会执行(停止web工程的时候,也会销毁过滤器

8.4、FilterChain 过滤器链

FilterChain:多个过滤器如何一起工作

FilterChain()方法的作用:

执行下一个Filter过滤器(如果有)
执行目标资源(如果没有)
在多个Filter过滤器执行的时候,他们执行的优先顺序是由它们在web.xml中从上到下配置的顺序决定
多个Filter过滤器执行的特点:
1.所有Filter的目标资源默认都执行在同一个线程中
2.多个Filter共同执行的时候,它们都使用同一个Request对象

9、Ajax


9.1、JSON

  • JSON(JavaScript Object Notation)是一种轻量级的数据交换格式
  • 采用完全独立于编程语言的文本格式来存储和表示数据
  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换格式。
  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率

注:轻量级指的是跟xml做比较!

数据交换指的是客户端和服务器之间业务数据的传递格式

9.2、JSON的定义

json是由键值对组成,并且由花括号(大括号) 包裹。每个键由引号引起来,键和值之间使用冒号进行分隔,多组键值对之间进行逗号进行分隔

let jsonStr = {
    "id": 1001,
    "name": '晓琳',
    "age": 19,
    "hobby": ['girl', 'code', 'swim', 'game'],
}

json的两个常用方法

JSON.stringify()	// 把Js对象转换成为json字符串
JSON.parse()		// 把json字符串转换成为Js对象

let jsonStr = JSON.stringify(jsonObj);     // 把js对象转换成为json字符串
console.log(jsonStr);

let parseJsonObj = JSON.parse(jsonStr);     // 把json字符串解析成为json对象
console.log(parseJsonObj);

9.3、JSON在Java中的使用

需导入fastjson1.2.78.jar包

JavaBean和Json的互转

public void test1() {
        // JavaBean和Json的互转
        Person p1 = new Person(1001, "admin");

        // toJSONString()  将Java对象转换成为json字符串
        String personJsonStr = (String) JSON.toJSONString(p1);
        System.out.println(personJsonStr);

        // parseObject()  将json字符串转换成为Java对象
        Person person = JSON.parseObject(personJsonStr, Person.class);
        System.out.println(person);
    }

List和Json的互转

@Test
    public void test2() {
        // List和Json的互转
        List<Person> list = new ArrayList<>();
        Person p1 = new Person(1001, "admin");
        Person p2 = new Person(1002, "小张");
        Person p3 = new Person(1003, "晓琳");
        list.add(p1);
        list.add(p2);
        list.add(p3);

        // 将list集合转换成为Json字符串
        String listStr = JSON.toJSONString(list);
        System.out.println(listStr);

        // 将Json字符串转换为list集合  匿名内部类
        ArrayList<Person> personList = JSON.parseObject(listStr, new TypeReference<ArrayList<Person>>() {});
        System.out.println(personList);
    }

Map和Json的互转

@Test
public void test3() {
    // Map和Json的互转
    Map<Integer, Person> map = new HashMap<>();
    map.put(1, new Person(100, "晓琳"));
    map.put(2, new Person(102, "小张"));
    map.put(3, new Person(103, "小赵"));

    // 将map转换成为json字符串
    String mapStr = JSON.toJSONString(map);
    System.out.println(mapStr);

    // 将json字符串转换成为map
    HashMap<Integer, Person> personHashMap = JSON.parseObject(mapStr, new TypeReference<HashMap<Integer, Person>>() {
    });
    System.out.println(personHashMap);
}

9.4、原生Ajax(XMLHttpRequest)

什么是Ajax请求?

  • Ajax即"Asynchronous Javascript And XML"(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术
  • ajax是一种浏览器通过js异步发起请求。局部更新页面的技术
  • Ajax请求的局部更新,浏览器地址栏不会发送变化
  • 局部更新不会舍弃原来页面的内容

使用原生Ajax技术实现异步交互

五步使用法:

1.创建XMLHTTPRequest对象

2.使用open方法设置和服务器的交互信息

3.设置发送的数据,开始和服务器端交互

4.注册事件

5.更新界面

get请求:

<button>点击发送请求</button>
<div class="result">

</div>
<script>
    document.querySelector("button").addEventListener("click", () => {
        let uname = "小昭"
        // 1.创建对象
        const xhr = new XMLHttpRequest()
        // 2.初始化 设置请求方法 和 url
        xhr.open("POST", `http://localhost:8000/server?name=${uname}`)

        // 设置请求头
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
        // 设置响应体数据的类型
        xhr.responseType = "json"
        // 超时设置 超过2s取消发送请求
        xhr.timeout = 2000
        // 超时回调
        xhr.ontimeout = () => alert("请求超时 请稍后重试")
        // 网络异常回调
        xhr.onerror = () => alert("你的网络似乎出现了一些问题 请稍后重试")

        xhr.onreadystatechange = () => {
            // readyState = 4 请求已完成并且响应已经准备好
            // status = 200 说明可以接收到响应数据,表示服务端已经返回所有的结果
            if (xhr.readyState === 4 && xhr.status === 200) {
                // 处理结果
                console.log(xhr.status);    	// 状态码
                console.log(xhr.statusText);    // 状态字符串
                console.log(xhr.getAllResponseHeaders());    // 所有响应头
                console.log(xhr.response);      // 响应体

                document.querySelector(".result").innerHTML = xhr.response
            }
        }

        // 3.发送请求
        xhr.send(`name:${uname}&pwd:${upwd}`)
    })
</script>

9.5、使用jQuery实现Ajax

ajax格式:

$.ajax方法
url		 表示请求的地址
type	 表示请求的方式GET/POST请求
data	 表示发送给服务器的数据
			两种格式:
				1.name=value&name=value
				2.{key:value}
dataType 预期服务器端返回的数据类型
			常用的数据类型有:text(纯文本)、xml(xml数据)、json(json对象)
contentType 	设置请求头			
success	请求成功时调用次函数
error	请求失败时调用次函数

示例:

<div class="container">
    <button class="btn btn-danger">POST</button>
    <button class="btn btn-info">通用型(ALL)</button>
</div>
<script>
    $("button").eq(0).click(() => {
        $.post("http://localhost:8000/user", { uname: "小昭", age: 18 }, function (data) {
            console.log(data)
        })
    })

    $("button").eq(1).click(() => {
        $.ajax({
            // 请求地址
            url: "http://localhost:8000/user",
            // 请求类型
            type: "POST",
            // 请求参数
            data: { uname: "小昭", age: 18 },
            // 响应体结果
            dataType: "json",
            // 成功回调
            success: function (data) {
                console.log(data)
            },
            // 超时时间
            timeout: 2000,
            // 失败回调
            error: function () {
                console.log("出错了");
            },
            // 头信息
            headers: {
                uname: "Mr zhang"
            }
        })

        // 表单序列化 serialize(),serialize()可以把表单中所有表单项的内容都获取到
        // 并以name=value&name=value的形式进行拼接
        $("#submit").click(function () {
            // 序列化表单内容为字符串,用于 Ajax 请求!
            // alert($("#form1").serialize());
            $.getJSON("http://localhost:8080/07_json_ajax/ajax", "action=jquerySerialize&" + $("#form1").serialize(), function (data) {
                $("#msg").html(" serialize方法 编号:" + data.id + ",姓名:" + data.name);
            });
        })
    })
</script>
同源策略

同源策略(Same-Origin Policy)最早由Netscape 公司提出,是浏览器的一种安全策略

  • 同源:协议、域名、端口号必须完全相同
  • 违背同源策略就是跨域!

如何解决跨域?

CORS

CORS是什么?

CORS (Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持get和post请求。跨域资源共享标准新增了一组HTTP首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源

CORS 是如何工作的?

CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行

CORS的使用

主要是服务器端的设置:

<script>
    document.querySelector("button").addEventListener("click", () => {
        $.get("http://localhost:8000/cors-server", function (resp) {
            console.log(resp)
        })
    })

    // 服务器端设置
    app.get("/cors-server", (req, resp) => {
        // 设置响应头 允许跨域
        resp.setHeader("Access-Control-Allow-Origin", "*")                       		// 所有地址地址都可跨域请求
        // resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000")      // 只有该地址才可跨域请求
        resp.setHeader("Access-Control-Allow-Headers", "*")                       // 所有请求头都可跨域请求
        resp.setHeader("Access-Control-Allow-Method", "*")                       // 所有请求方式都可跨域请求
</script>   
  • 2
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaWeb demo 是一个简单的 JavaWeb 应用程序示例。它主要用于展示如何创建一个简单的动态网站,并介绍了一些常用的 JavaWeb 技术。 这个 demo 可能包含以下几个方面的内容: 1. 前端页面:demo 可能包含多个静态 HTML 页面,通过 CSS 和 JavaScript 来实现页面的样式和交互效果。这些页面可能包含表单、按钮、导航栏等常见的网页组件。 2. 后端处理:demo 可能使用 Java Servlet 来处理前端页面的请求。Servlet 是 JavaWeb 开发中的一种常用技术,通过它可以接收和处理 HTTP 请求,并生成对应的响应。在 demo 中,Servlet 可能会读取表单数据、调用业务逻辑处理模块,并将处理结果返回给前端页面。 3. 数据库连接:demo 可能会使用 Java 中的数据库连接技术,如 JDBC,来与后端数据库进行交互。通过 JDBC,demo 可能会连接到数据库、执行 SQL 查询和更新操作,以及处理数据库返回的结果。 4. 业务逻辑:demo 可能会展示一些简单的业务逻辑,如用户注册、登录等。在这些功能中,demo 可能会将用户的输入数据存储到数据库中,或者验证用户的登录信息。 通过这个示例,开发者可以了解到如何搭建一个简单的 JavaWeb 应用程序,并了解到如何使用常见的 JavaWeb 技术来实现各种功能。这个 demo 还可以作为 JavaWeb 开发的一个入门教程,帮助开发者迅速上手和理解如何构建一个简单的动态网站。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值