JavaWeb基础入门到上手项目

目录

前言

  • Java Web 其实就是一个技术的总和,把Web看成一个容器而已主要使用JavaEE技术来实现.在加上各种中间件
  • 整个javaWeb阶段的内容通过实际的案例贯穿学习, 所涉及到的技术知识点会在案例中根据不同的需求引入
  • 我们首先了解javaWeb的整个技术体系,掌握常用的技术知识点

注:本篇博客是基于B站遇见狂神说的JavaWeb讲解视频进行编写是前面JavaWeb所有博客的总结,如有遗漏和错误欢迎指出,原视频地址为【狂神说Java】JavaWeb入门到实战,大家记得三连啊✧(≖ ◡ ≖✿)

下面我们进入正题!

1、基本概念

1.1、前言

web开发:

  • web,网页的意思
  • 静态web
    • html,css
    • 提供给所有人看的数据始终不会变化
  • 动态web
    • 淘宝,几乎所有的网站
    • 提供给所有人看的数据始终会变化,每个人在不同时间不同地点看到的信息各不相同
    • 技术栈:Servlet/JSP,ASP,PHP

在Java中,动态web资源开发的技术统称为JavaWeb

1.2、web应用程序

web应用程序:可以提供浏览器访问的程序

  • a.html b.html......多个web资源,这些web资源可以被外界访问,对外界提供服务

  • 我们能访问到的任何一个页面或者资源,都存在于这个世界上的某一个角落的计算机上

  • URL

  • 这些统一的web资源会被放在同一个文件夹下,web应用程序---->Tomcat:服务器

  • 一个web应用由多部分组成(静态web 动态web)

    • html,css,js
    • jsp,servlet
    • java程序
    • jar包
    • 配置文件

web应用程序编写完毕后,若想提供给外界访问,需要一个服务器来统一管理

1.3、静态web

  • *.html, *.html,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以直接进行读取

  • 静态web存在的缺点
    • web页面无法动态更新,所有用户看到的都是同一个页面
      • 轮播图,点击特效:伪动态
      • JavaScript(实际开发中使用最多)
      • VBScript
    • 它无法和数据库交互(数据无法持久化,用户无法交互)

1.4、动态web

页面会动态展示:web的页面展示的效果因人而异(不同的人打开的页面是不一样的)

缺点

  • 加入服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序,重新发布(停机维护)

优点

  • web页面可以动态更新,所有用户看到的都不是同一个页面
  • 它可以和数据库交互(数据持久化:注册,用户信息)

2、web服务器

2.1、技术讲解

ASP:

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

PHP:

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

JSP/Servlet:

  • sun公司主推的B/S架构
  • 基于Java语言
  • 可以承载高并发,高可用,高性能带来的影响
  • 语法像ASP

2.2、web服务器

服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息

IIS:

  • 微软
  • ASP
  • Windows中自带的

Tomcat

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为比较流行的Web 应用服务器。

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,他是最佳选择

Tomcat 实际上运行JSP 页面和Servlet。另外,Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能。

下载Tomcat:

  • 安装/解压
  • 了解配置文件及目录结构
  • 作用

3、Tomcat

3.1、安装Tomcat

在安装之前给大家提一个建议,我在学习新知识的过程中经常会到软件版本不匹配报错的问题,而且经常卸载安装好几次也不成功,
所以建议大家在安装之前先去查看以下版本匹配的问题(不是最新的版本就是最好的),我在这里使用是Tomcat9
具体的下载流程大家可以参考以下博客,tomcat安装及配置教程(保姆级)_爱你的阿白~的博客-CSDN博客_tomcat安装及配置教程

3.2、Tomcat启动和配置

文件夹作用:

启动,关闭Tomcat:

可能遇到的问题:

  1. Java环境变量没有配置
  2. 闪退问题:需要配置兼容性
  3. 乱码问题:配置文件中设置

3.3、配置


可以配置启动的端口号

  • Tomcat默认端口号为8080
  • Mysql:3306
  • http:80
  • https:443

可以配置主机的名称

  • 默认的主机名为:localhost->127.0.0.1
  • 默认网站应用存放的位置为:webapps

思考问题

网站是如何进行访问的:

  1. 输入一个域名:回车
  2. 检查本机的hosts配置文件下有没有这个域名映射
    1. 有--->直接返对应的IP地址,这个地址中有我们需要访问的web程序,可以直接访问
    2. 没有--->去DNS服务器寻找(找到就返回,找不到就返回找不到)

3.4、发布一个web网站

  1. 将自己写的网站放到服务器(Tomcat)指定的webapp的文件夹下,就可以访问了

网站应该有的结构:

--webapps : Tomcat服务器的web目录
    --ROOT
    --Qstudy : 网站的目录名
        --index.html : 默认的首页
        --static
            --css
            --js
            --img
            --...
        --WEB-INF
            --classes : java程序
            --lib : web应用所依赖的jar包
            --web.xml : 网站的配置文件

4、HTTP

4.1、什么是HTTP

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。

http端口:80 https端口:443 (s代表安全)

  • 文本:html,字符串,...
  • 超文本:图片,音乐,视频,定位,地图....

4.2、两个时代

  • http1.0
    • HTTP/1.0:客户端可以与Web服务器连街沟,只获得一个web资源,断开连接
  • http2.0
    • HTTP/1.1:客户端可以与Web服务器连街沟,可以获得多个web资源

4.3、HTTP请求

  • 客户端------发请求(request)-----服务器

    Request URL: https://www.baidu.com/                      //请求地址(以百度为例)
    Request Method: GET                                      //GET/POST方法
    Status Code: 200 OK                                      //状态码:200代表OK
    Remote Address: 110.242.68.3:443                         //地址
    Referrer Policy: strict-origin-when-cross-origin         //协议
    
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9//语言
    Cache-Control: max-age=0
    Connection: keep-alive
    
  • 请求行

  • 消息头

    Accept:             //告诉浏览器它所支持的数据类型
    Accept-Encoding:    //支持哪种编码格式   GBK   UTF-8    GB2312   ISO8859-1
    Accept-Language:    //告诉浏览器他的语言环境
    Cache-Control:      //缓存控制
    Connection:         //告诉浏览器,请求完是断开还是保持连接
    HOET:               //主机
    

4.4、HTTP响应

  • 服务器-----响应(response)-----客户端
Cache-Control: private                           // 缓存控制
Connection: keep-alive                           // 连接
Content-Encoding: gzip                           // 编码
Content-Type: text/html;charset=utf-8            // 类型
  • 响应体
Accept:             //告诉浏览器它所支持的数据类型
Accept-Encoding:    //支持哪种编码格式   GBK   UTF-8    GB2312   ISO8859-1
Accept-Language:    //告诉浏览器他的语言环境
Cache-Control:      //缓存控制
Connection:         //告诉浏览器,请求完是断开还是保持连接
HOET:               //主机
REFRESH:            //告诉客户端多久刷新一次
Location:           //让网页重新定位
  • 响应头

    200:请求响应成功

    202:接受

    3**:

    • 重定向:你重新到我给你的新位置去

    400:错误的请求

    404:找不到资源

    5**:服务器代码错误(502网关错误)

5、Maven

  1. 在JAvaweb开发中,需要使用大量的jar包,我们需要手动导入
  2. 如何能够让一个东西自动帮我们导入和配置这个jar包

5.1、Maven项目架构管理工具

我们目前用来就是方便导入jar包的

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

  • 有约束不要去违反

Maven会规定好你该如何去编写我们的Java代码,必须要按照规范

5.2、下载安装Maven

具体下载步骤大家可以参考这篇博客maven安装及配置(详细版)

这里尤其要注意Maven版本与IDEA版本对应的问题

官网;https://maven.apache.org/
这里我保存的位置是:E:\JavaBackend\Maven\apache-maven-3.5.2

5.3、配置环境变量

在我们的系统环境变量中

配置如下配置:

  • M2_HOME maven目录下的bin目录
  • MAVEN_HOME maven的目录
  • 在系统的path中配置%MAVEN_HOME%\bin

测试是否安装成功

5.4、阿里云镜像

  • 镜像:mirrors,可以加速我们的下载

setting.xml配置文件位置

<mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>central</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

5.5、本地仓库

本地仓库 localRepository,远程仓库

建立一个仓库:

  • 原来的地址:
C:\Users\86134\.m2\repository
  • 修改后的地址:
 <localRepository>E:/JavaBackend/Maven/apache-maven-3.5.2/maven_repository</localRepository>

5.6、在IDEA中使用Maven

  1. 启动IDEA

  2. 创建一个Mavenweb项目



3.等待项目初始化完毕

4.观察maven仓库中多了哪些东西

5.idea中的maven设置(idea中经常使用自带的maven,需要手动修改,退出项目在最开始的页面可以修改默认设置)

5.7、创建一个普通maven项目

5.8、标记文件夹功能

(1)

(2)

5.9、在idea中配置Tomcat

(1)
(2)

(3)警告问题:我们访问一个网站的时候,需要指定一个文件夹名字


(4)启动测试

5.10、pom文件

pom.xml是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">
  <modelVersion>4.0.0</modelVersion>
<!--这里就是我们刚才配置的GAV-->
  <groupId>org.example</groupId>
  <artifactId>Javaweb-01-maven</artifactId>
  <version>1.0-SNAPSHOT</version>
<!--packaging打包方式
java:jar
Javaweb:war
-->
  <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>
      <!--maven会帮你导入这个jar包所依赖的其他jar包    -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
<!--项目构建用的东西-->
  <build>
    <finalName>Javaweb-01-maven</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

在build中配置resources,来防止我们资源导出失败的问题

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

5.11、Maven仓库的使用

Maven仓库

常见的问题

  • maven版本与jdk版本不相容(jdk1.8 maven3.5)

  • Tomcat闪退

  • IDEA中每次都要重复配置Maven(修改全局)

  • Maven项目中Tomcat无法配置

  • Maven默认web项目中的web.xml版本问题

      • 替换为webapp4.0版本和Tomcat一致
      <?xml version="1.0" encoding="UTF-8"?>
      
      <web-app metadata-complete="true" version="4.0" 
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://xmlns.jcp.org/xml/ns/javaee">
               
      </web-app>
      

6、Servlet

6.1、Servlet简介

  • Servlet就是sun公司开发动态web的一门技术
  • sun公司在这些api中提供了一个接口叫Servlet,如果想发开一个Servlet程序,则需要两步
    • 编写一个类实现Servlet接口
    • 把开发号的java类部署到web服务器中

通常我们把实现了Servlet接口的java程序叫做Servlet

6.2、HelloServlet

Servlet接口Sun公司有两个默认的实现类:HttpServlet,GenericServlet

1.构建一个普通的Maven项目,删掉里面的src目录,以后我们的学习就在这个项目里面建立Moudel;这个空的工程就题Maven主工程;

关于Maven父子工程的理解

父项目中会有

  <modules>
          <module>servlet-01</module>
      </modules>

子项目通常会有(这里如果没有可以手动添加)

<parent>
    <artifactId>javaweb-02-servlet</artifactId>
    <groupId>com.qjd</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>

注:父项目中的java子项目可以直接使用

3.Maven环境优化

​3.1.修改web.xml为最新的
在这里可以把最新的web.xml保存下来,下次直接拿来稍微修改就可以使用

web.xml


<web-app metadata-complete="true" version="4.0"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee">

    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class></servlet-class>
    </servlet>

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

    </servlet-mapping>

</web-app>

​3.2.将Maven的结构搭建(java,resources)

4.编写一个Servlet程序

  • 编写一个普通类

    • 实现Servlet接口,这里我们直接继承HttpServlet
   public class HelloServlet extends HttpServlet {

   //由于get或post只是请求实现的方式不同,可以相互调用,业务逻辑都一样
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       System.out.println("进入了doGet方法");
       
   //ServletOutputStream outputStream = resp.getOutputStream();
       PrintWriter writer = resp.getWriter();//响应流
       writer.println("Hello,Servlet");
   }

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

5.编写Servlet的映射

为什么需要映射?

我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给它一个浏览器能够访问的路径

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

<web-app metadata-complete="true" version="4.0"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee">
<!--注册Servlet-->
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.qjd.servlet.HelloServlet</servlet-class>
    </servlet>
<!--Servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

6.配置Tomcat

注意:配置项目发布的路径就可以了

7.启动测试

(1)为部署标记工件

(2)启动Tomcat发现在服务这个模块中显示了我们在HelloServlet中编写的“进入了doGet方法“”

(3)测试结果(注意这里的访问路径不要写错)

6.3、Servlet原理

Servlet是由web服务器调用,web服务器在收到浏览器的请求之后,会执行以下流程:

6.4、Mapping

1.一个Servlet可以指定一个映射路径

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

2.一个Servlet可以指定多个映射路径

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

    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello2</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello3</url-pattern>
    </servlet-mapping>

3.一个Servlet可以指定通用映射路径

    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
<!--默认请求路径(不会进入index.jsp)-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

4.指定一些后缀或者前缀等

<!--可以自定义后缀实现请求映射(*前面不能加项目映射的路径)-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>*.qjd</url-pattern>
    </servlet-mapping>

5.优先级问题

指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求

如下启动Tomcat默认进入ErrorServlet的404,但输入http://localhost:8080/s001/hello可以进入Hello.Servlet而不是404

public class ErrorServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        PrintWriter writer = resp.getWriter();
        writer.println("<h1>404</h1>");
        
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
<!--注册Servlet-->
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.qjd.servlet.HelloServlet</servlet-class>
    </servlet>
<!--Servlet的请求路径-->
<!--localhost:8080/s001/hello -->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

<!-- 404-->
    <servlet>
        <servlet-name>error</servlet-name>
        <servlet-class>com.qjd.servlet.ErrorServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>error</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

6.5、ServletContext

web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用

1、共享数据

我们在这个Servlet中保存的数据可以在另一个Servlet中拿到

例子:

  • 放置username的类

    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();
    
            String username = "ikun";//数据
            context.setAttribute("username",username);//将一个数据保存在了ServletContext中,名字为:username,值为username (= "ikun";)
    
        }
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  • 读取username 的类

    public class GetServlet extends HelloServlet{
        @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");
            resp.setCharacterEncoding("utf-8");
            resp.getWriter().println("名字是"+username);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doGet(req, resp);
        }
    }
    
  • 配置web.xml

    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.qjd.servlet.HelloServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
    
    <servlet>
        <servlet-name>getc</servlet-name>
        <servlet-class>com.qjd.servlet.GetServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>getc</servlet-name>
        <url-pattern>/getc</url-pattern>
    </servlet-mapping>
    
  • 测试访问(理解6.5开始的图):

    • 直接访问http://localhost:8080/s002/getc不能显示出名字(名字存在http://localhost:8080/s002/hello中)

    • 先访问http://localhost:8080/s002/hello再访问http://localhost:8080/s002/getc可以得到名字

2、获取初始化参数

  • 在web.xml中配置参数

    <!--    配置一些web应用的初始化信息-->
        <context-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8</param-value>
        </context-param>
    
  • 得到参数

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        ServletContext context = this.getServletContext();
        String url = context.getInitParameter("url");
        resp.getWriter().println(url);
    }
    
  • 在web.xml中注册

    <servlet>
        <servlet-name>gp</servlet-name>
        <servlet-class>com.qjd.servlet.ServletDemo03</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>gp</servlet-name>
        <url-pattern>/gp</url-pattern>
    </servlet-mapping>
    

3、请求转发

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("进入了ServletDemo04");

    ServletContext context = this.getServletContext();
    context.getRequestDispatcher("/gp").forward(req,resp);//转发的请求路径并调用forward实现请求转发
    
}

4、读取资源文件

Properties

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

发现都被打包到同一个路径下:classes,我们俗称这个路径为classpath(类路径)

注意:如果这里没有将properties放在resources目录下,可能会导致读取不成功,我们需要在build中配置resources , 来防止我们资源导出失败的问题

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

思路:需要一个文件流

  1. db.properties

    username=root
    password=123456
    
  2. ServletDemo05

    public class ServletDemo05 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("进入了ServletDemo05");
            InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
            Properties prop = new Properties();
            prop.load(is);
            String username = prop.getProperty("username");
            String password = prop.getProperty("password");
    
            resp.getWriter().println(username+":"+password);
    
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doGet(req, resp);
        }
    }
    
  3. web.xml

    <servlet>
        <servlet-name>sd5</servlet-name>
        <servlet-class>com.qjd.servlet.ServletDemo05</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>sd5</servlet-name>
        <url-pattern>/sd5</url-pattern>
    </servlet-mapping>
    
  4. 访问测试

6.6、HttpServletResponse

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象和一个代表响应的HttpServletResponse对象

  • 如果要获取客户端请求过来的参数:找HttpServletRequest
  • 如果要给客户端响应一些信息:找HttpServletResponse

1、简单分类

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

ServletOutputStream getOutputStream() throws IOException;

PrintWriter getWriter() throws IOException;

负责向浏览器发送响应头的方法

void setCharacterEncoding(String var1);

void setContentLength(int var1);

void setContentLengthLong(long var1);

void setContentType(String var1);

响应的状态码常量
200:请求响应成功

202:接受

3**:

重定向:重新到我给你的新位置去
400:错误的请求

404:找不到资源

5**:服务器代码错误(502网关错误)
以下具体的状态码了解即可,重点记住以上关于Http重点的状态码

    int SC_CONTINUE = 100;
    int SC_SWITCHING_PROTOCOLS = 101;
    int SC_OK = 200;
    int SC_CREATED = 201;
    int SC_ACCEPTED = 202;
    int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    int SC_NO_CONTENT = 204;
    int SC_RESET_CONTENT = 205;
    int SC_PARTIAL_CONTENT = 206;
    int SC_MULTIPLE_CHOICES = 300;
    int SC_MOVED_PERMANENTLY = 301;
    int SC_MOVED_TEMPORARILY = 302;
    int SC_FOUND = 302;
    int SC_SEE_OTHER = 303;
    int SC_NOT_MODIFIED = 304;
    int SC_USE_PROXY = 305;
    int SC_TEMPORARY_REDIRECT = 307;
    int SC_BAD_REQUEST = 400;
    int SC_UNAUTHORIZED = 401;
    int SC_PAYMENT_REQUIRED = 402;
    int SC_FORBIDDEN = 403;
    int SC_NOT_FOUND = 404;
    int SC_METHOD_NOT_ALLOWED = 405;
    int SC_NOT_ACCEPTABLE = 406;
    int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
    int SC_REQUEST_TIMEOUT = 408;
    int SC_CONFLICT = 409;
    int SC_GONE = 410;
    int SC_LENGTH_REQUIRED = 411;
    int SC_PRECONDITION_FAILED = 412;
    int SC_REQUEST_ENTITY_TOO_LARGE = 413;
    int SC_REQUEST_URI_TOO_LONG = 414;
    int SC_UNSUPPORTED_MEDIA_TYPE = 415;
    int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    int SC_EXPECTATION_FAILED = 417;
    int SC_INTERNAL_SERVER_ERROR = 500;
    int SC_NOT_IMPLEMENTED = 501;
    int SC_BAD_GATEWAY = 502;
    int SC_SERVICE_UNAVAILABLE = 503;
    int SC_GATEWAY_TIMEOUT = 504;
    int SC_HTTP_VERSION_NOT_SUPPORTED = 505;

2、常见应用

1.向浏览器输出消息

(前面的练习就是)

2.下载文件
  • 要获取下载文件的路径

  • 下载的文件名是什么

  • 设置想办法让浏览器能够支持下载我们需要的东西

  • 获取下载文件的输入流

  • 创建缓冲区

  • 获取OutputStream对象

  • 将FileOutputStream写入到buffer缓冲区

  • 使用OutputStream将缓冲区中的数据输出到客户端

代码实现

public class FileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //要获取下载文件的路径
        String realPath = "E:\\JavaWeb\\Javaweb\\javaweb-02-servlet\\response\\src\\main\\resources\\3.jpg";
        System.out.println("下载文件的路径"+realPath);

        //下载的文件名是什么
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);

        //设置想办法让浏览器能够支持Content-Disposition下载我们需要的东西,中文命名的文件用 
        //URLEncoder.encode(fileName,"utf-8")解决乱码问题
        resp.setHeader("Content-Disposition","attachment; filename="+ URLEncoder.encode(fileName,"utf-8"));

        //获取下载文件的输入流
        FileInputStream in = new FileInputStream(realPath);

        //创建缓冲区
        int len = 0;
        byte[] buffer = new byte[1024];

        //获取OutputStream对象
        ServletOutputStream out = resp.getOutputStream();

        //将FileOutputStream写入到buffer缓冲区
        //使用OutputStream将缓冲区中的数据输出到客户端
        while ((len = in.read(buffer))>0){
            out.write(buffer,0,len);
        }
        in.close();
        out.close();

        
    }

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

测试访问

3.验证码功能

验证怎么来的?

  • 前端实现

  • 后端实现,需要用到Java的图片类,生成一个图片

    public class ImageServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //如何让浏览器3秒自动刷新一次
            resp.setHeader("refresh","3");
    
            //在内存中创建一个图片
            BufferedImage bufferedImage = new BufferedImage(80,60,BufferedImage.TYPE_INT_RGB);
    
            //得到图片
            Graphics2D graphics =(Graphics2D) bufferedImage.getGraphics();//画笔
    
            //设置图片的背景颜色
            graphics.setColor(Color.BLUE);
            graphics.fillRect(0,0,80,60);
    
            //给图片写数据
            graphics.setColor(Color.CYAN);
            graphics.setFont(new Font(null,Font.BOLD,20));
            graphics.drawString(makeNum(),0,20);
    
            //告诉浏览器这个请求用图片的方式打开
            resp.setContentType("image/jpg");
    
            //网站存在缓存
            resp.setDateHeader("expires",-1);
            resp.setHeader("cache-control","no-cache");
    
            //把图片写给浏览器
            ImageIO.write(bufferedImage,"jpg", resp.getOutputStream());
    
        }
    
        //生成随机数
        private String makeNum(){
            Random random = new Random();
            String s = random.nextInt(9999999) + "";
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 7-s.length() ; i++) {
                sb.append("0");
            }
            String s1 = sb.toString() + s;
            return s;
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           doGet(req, resp);
        }
    }
    

测试访问:每5秒钟刷新生成随机数(这个页面有点丑,大家请见谅)


4.实现重定向

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

常见场景:

用户登录

 void sendRedirect(String var1) throws IOException;

测试:

public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //重定向
        resp.sendRedirect("/r/img");

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

测试访问

注:网页404时代表代码没有问题,只是路径出错了

jsp测试

  • index.jsp

    <html>
    <body>
    <h2>Hello World!</h2>
    
    <%--这里提交的路径需要寻找到项目的路径--%>
    <%--pageContext.request.contextPath代表当前的项目--%>
    <form action="${pageContext.request.contextPath}/login" method="get">
        用户名: <input type="text" name="username"><br>
    
        密码:  <input type="password" name="password"><br>
        <input type="submit">
    </form>
    
    
    
    <%@page contentType="text/html; ISO-8859-1" pageEncoding="UTF-8" %>
    
    </body>
    </html>
    
  • success.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    
    
    <h1>Success</h1>
    
    </body>
    </html>
    
  • RequestTest

    public class RequestTest extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("进入这个请求了");
    
            //处理请求
            String username = req.getParameter("username");
            String password = req.getParameter("password");
    
    
            System.out.println(username+":"+password);
    
            //重定向时候一定要注意,路径问题,否则404
            resp.sendRedirect("/r/success.jsp");
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

测试访问:

6.7、HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest,

通过这个HttpServletRequest的方法,获得客户端的所有信息

获取参数,请求转发

代码实现

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");

        String password = req.getParameter("password");
        String[] hobbies = req.getParameterValues("hobby");
        System.out.println("=====================================");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbies));
        System.out.println("=====================================");


        //通过请求转发
        //注意:这里的    /     代表当前的web应用(转发不用写/ 重定向写/)
        req.getRequestDispatcher("/success.jsp").forward(req,resp);

    }

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

测试访问

思考题:请你聊聊重定向和转发的区别

相同点:页面都会实现跳转

不同点:

  • 请求转发的时候,URL不会发生变化 307
  • 重定向的时候,URL地址栏会发生变化 302

7、Cookie、Session

7.1、会话

会话:用户打开了一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话

有状态会话:记录了一些信息

大家在浏览网站的过程中有没有思考过这样一个问题,一个网站怎么证明客户访问过呢?

客户端 服务端

1.服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了 : cookie

2.服务器登记客户已经来过了,下次访问的时候直接匹配登记过的客户 ; session

3.大家可以先看一下这张图,下面对Cookie与Session的讲解就是围绕这张图展开的,最后会对这张图进行详细的讲解

7.2、保存会话的两种技术

7.2.1、cookie

  • 客户端技术(响应,请求)
  • cookie一般是以键值对的形式进行表示的(key-value)
  • 我们登录过一个网站,当我们再次打开就不需要再次登录了

7.2.2、session

  • 服务器技术(保存用户的会话信息,可以把信息或者数据放在session中)
  • 每个客户端都有自己的一个Session会话
  • Session会话中,我们经常用来保存用户登录之后的信息

大部分的网站:网站登录之后,下次就不用再登录了,第二次访问直接就可以登录进去

7.3、Cookie

1.cookie中常用属性的解释

  • Name:这个是cookie的名字
  • Value:这个是cooke的值
  • path:这个定义了Web站点上可以访问该Cookie的目录
  • Expircs:这个值表示cookie的过期时间,也就是有效值,cookic在这个值之前都有效。
  • size:这个表示cookic的大小

2.Cookie的Http传输

Cookie的Http传输 我们会在接下来的例子中进行抓包展示

3.Cookie的生命周期

cookie有2种存储方式,一种是会话性,一种是持久性

  • 会话性:如果cookie为会话性,那么cookie仅会保存在客户端的内存中,当我们关闭客服端时cookie也就失效了
  • 持久性:如果cookie为持久性,那么cookie会保存在用户的硬盘中,直至生存期结束或者用户主动将其销毁。

cookie我们是可以进行设置的,我们可以人为设置cookie的有效时间,什么时候创建,什么时候销毀。

4.cookie 使用的常见方法

java中Cookie对象的方法进行讲解

  • new Cookie(String name, String value):创建一个Cookie对象,必须传入cookie的名字和cookic的值
  • setValue():得到cookie保存的值
  • getName():获取cookie的名字
  • setMaxAge(int expiry):设置cookie的有效期,默认为-1。这个如果设置负数,表示客服端关闭,cookie就会删除。0表示马上删除。正数表示有效时间,单位是秒
  • setPath(String uri):设置cookie的作用域

HttpServletRequest和Http ServletResponse对Cookie进行操作的常见方法

  • response.addCookie(Cookie cookie):将cookie给客户端进行保存
  • resquest.getCookies0:得到客服端传过来的所有cookie对象

这张图是客户端携带Cookie访问服务器的流程图

7.3.1、用代码实现保存用户上一次访问的时间

  1. 从请求中拿到cookie信息

  2. 服务端响应给客户端cookie

//保存用户上一次访问的时间

public class CookieDemon01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //服务器告诉你你来的时间,把这个时间封装为一个信件,你下次来,我就知道你来了


        //解决中文乱码,这里解决乱码需要根据实际情况自己进行调试
        req.setCharacterEncoding("gbk");
        resp.setCharacterEncoding("gbk");

        PrintWriter out = resp.getWriter();

        //cookie,服务器端从客户端获取
        Cookie[] cookies = req.getCookies();//返回数组说明cookie可能存在多个

        //判断cookie是否存在
        if (cookies != null) {
        //如果存在怎么办
            out.write("您上一次访问的时间是:");

            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                //获取cookie的名字

               if( cookie.getName().equals("LastLogTime")){
                   //获取cookie的值

                   long lastLogTime = Long.parseLong(cookie.getValue());
                   Date date = new Date(lastLogTime);
                   out.write(date.toLocaleString());

               }
            }
            
        }else {
            out.write("这是您第一次访问本站");
        }
        
        //服务器给客户端响应一个cookie

        Cookie cookie = new Cookie("LastLogTime", System.currentTimeMillis()+"");
        //设置cookie有效期为一天(浏览器关闭cookie还存在一天)
        cookie.setMaxAge(24*60*60);

        resp.addCookie(cookie);

        
    }

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

3.注册Servlet

 <servlet>
    <servlet-name>CookieDemo01</servlet-name>
    <servlet-class>com.qid.servlet.CookieDemon01</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>CookieDemo01</servlet-name>
    <url-pattern>/c1</url-pattern>

4.测试访问

第一次访问

第二次访问

接下来我们抓包查看Cookie的Http传输

注:cookie一般会保存在本地的用户目录下AppData ,浏览器只会携带在当前请求的URL中包含了改cookie中path值的cookie

了解一下:
一个网站cookie是否存在上限?

  • 一个cookie只能保存一个信息
  • 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
  • cookie大小上限为4kb
  • 300个cookie大约为浏览器上限

7.3.2、删除cookie:

  • 不设置有效期,关闭浏览器,自动失效

  • 设置有效期为0

    public class CookieDemon02 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       
            //创建一个cookie.名字必须和要删除的名字一致
            Cookie cookie = new Cookie("LastLogTime", System.currentTimeMillis()+"");
            //有限期为0
            cookie.setMaxAge(0);
    
            resp.addCookie(cookie);
            
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

7.3.3、中文传输数据乱码问题

这里再用上面记录访问时间的例子说一下传输中文数据的问题:

如果一直中文传输数据乱码,可以尝试以下代码:

编码解码

//编码
Cookie cookie = new Cookie("name", URLEncoder.encode("张三","utf-8"));
 //解码
 out.write( URLDecoder.decode(cookie.getValue(),"utf-8"));

整体代码:


//中文数据传递

public class CookieDemon03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //解决中文乱码
        req.setCharacterEncoding("utf-16");
        resp.setCharacterEncoding("utf-16");
        //cookie,服务器端从客户端获取
        Cookie[] cookies = req.getCookies();//返回数组说明cookie可能存在多个


        PrintWriter out = resp.getWriter();

        //判断cookie是否存在
        if (cookies != null) {
            //如果存在怎么办
            out.write("您的名字是:");

            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                //获取cookie的名字

                if( cookie.getName().equals("name")){
                    //获取cookie的值
                    System.out.println(cookie.getValue());
                   //解码
                    out.write( URLDecoder.decode(cookie.getValue(),"utf-8"));

                }
            }

        }else {
            out.write("这是您第一次访问本站");
        }
        //编码
        Cookie cookie = new Cookie("name", URLEncoder.encode("张三","utf-8"));
        resp.addCookie(cookie);
    }

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

注册servlet后测试结果

7.4、Session(重点)

7.4.1、Session的概念

1.什么是session

  • 服务器会给每一个用户(浏览器)创建一个Session
  • 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
  • 用户登录之后,整个网站它都可以访问--->保存用户的信息

2.Session的常用方法

  • request.getSession():创建(第一次调用)或获取Session会话对象
  • isNew():判断当前Session会话是否是新创建出来的
  • getId():得到Session的会话ID
  • setAttribute():往Session中保存数据
  • getAttribute():获取Session域中的数据

3.Session的生命周期

  • 手动注销:session.invalidate();

  • 自动注销:在web.xml 中配置

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

4.session应用:

  • 保存一个登录用户的信息
  • 购物车信息
  • 在整个网站中经常会使用的数据,我们将它保存在Session中

下面这张图是服务器登记session,客户端用sessionID访问的流程图

7.4.2、具体使用session

1.保存数据:

  • Person.java
  • SessionDemon01(设置信息)
  • SessionDemon02(得到信息)
public class Person {

    private String name;
    private int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class SessionDemon01 extends HttpServlet {

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

        //解决乱码问题
        req.setCharacterEncoding("gbk");
        resp.setCharacterEncoding("gbk");
        resp.setContentType("text/html;charset = gbk");

        //得到Session

        HttpSession session = req.getSession();

        //给Session中存东西
        session.setAttribute("name",new Person("李四",18));


        //获取session的id
        String sessionId = session.getId();

        //判断session是不是新创建的
        if(session.isNew()){
            resp.getWriter().write("session创建成功,id:"+sessionId);
        }else {
            resp.getWriter().write("session已经在服务器中存在了,id:"+sessionId);
        }


        //Session创建的时候做了什么事情
//        Cookie cookie = new Cookie("JSESSIONID",sessionId);
//        resp.addCookie(cookie);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
public class SessionDemon02 extends HttpServlet {

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

        //解决乱码问题
        req.setCharacterEncoding("gbk");
        resp.setCharacterEncoding("gbk");
        resp.setContentType("text/html;charset = gbk");

        //得到Session

        HttpSession session = req.getSession();
        Person person = (Person) session.getAttribute("name");
        resp.getWriter().write(person.toString());
        System.out.println(person.toString());


    }

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

注册Servlet测试访问
(1)访问设置信息s1

(2)访问得到信息s2

2.会话过期

  • 手动

    public class SessionDemon03 extends HttpServlet {
    
            //注销,移除
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            HttpSession session = req.getSession();
            session.removeAttribute("name");
            //手动注销session
            session.invalidate();//一旦注销会重新产生一个sessionId
            
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  • 自动

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

最后我们再回到刚开始的问题,一个网站怎么证明你来过呢?

思考 : Session和Cookie的区别

①cookie可以存储在浏览器或者本地,Session只能存在服务器
②session能够存储任意的java对象,cookie 只能存储String 类型的对象
③Session比tCookie更具有安全性(Cookie有安全隐患,通过拦截或本地文件找得到你的cookie后可以进行攻击)
④Session占用服务器性能,Session过多,增加服务器压力
⑤单个Cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个Cookie,Session是没有大小限制和服务器的

8、JSP

8.1、什么是JSP

JavaServletPages: Java服务器端页面,也和Serlvet一样,用于动态web技术

最大的特点:

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

8.2、JSP原理

思路:JSP到底是怎么执行的

  • 代码层面没有任何问题

  • 服务器内部工作

    ​ Tomcat中有一个work目录:

    ​ idea中使用Tomcat会在idea的Tomcat中产生一个work目录

  • 发现页面变成了Java程序

JSP最终也会被转成一个java类

JSP本质上就是一个Servlet

浏览器向服务器发送请求,不管访问什么资源,其实都是在访问servlet

1.判断请求

2.内置一些对象

final javax.servlet.jsp.Pagecontext pagecontext;
//页面上下文
javax.servlet.http.Httpsession session null;
//session
final javax.servlet.Servletcontext application;
//applicationContext
final javax.servlet.servletconfig config;
//config
javax.servlet.jsp.Jspwriter out null;
//out
final java.lang.object page this;
//page:当前
HttpservletRequest request
//请求
HttpservletResponse response
//响应

3.输出页面前增加的代码

response.setcontentType("text/htm1");
//设望响应的页面类型
pagecontext = _jspxFactory.getPagecontext(this,request,response,
null,true,8192,true);
_jspx_page_context = pagecontext;
application = pagecontext.getservletcontext();
config = pagecontext.getservletconfig();
session = pagecontext,getsession();
out = pagecontext.getout();
_jspx_out=out;

以上的这些对象我们可以在JSP页面上直接使用

在JSP页面中,只要是Java代码就会原封不动的输出

如果是HTML代码就会被转换为:

out.write("<html>\n");

这样的格式输出到前端

8.3、JSP基础语法

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

JSP表达式

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

JSP脚本片段

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

脚本片段的再实现

   <%
   int  x = 10;
   out.println(x);
   %>
  <p>这是一个jsp文档</p>
  <%
    int y = 20;
    out.println(2);
  %>

  <hr>


  <%--在代码中嵌入HTML元素  --%>
  <%
    for (int i = 0; i < 5; i++) {
  %>
  <h1>hello world <%=i%> </h1>
  <%
    }
  %>

JSP声明

<%!
static {
  System.out.println("Loading Servlet!");
}
private int global = 0;
public void qjd(){
  System.out.println("进入了方法qjd");
}

%>

JSP声明:(<%!%>)会被编译到JSP生成的Java类中!其他的(<%%>)就会被生成到jspService 方法中

在JSP中嵌入Java代码

<%----%>  注释
<%%>      片段
<%=%>     表达式输出一个值
<%!%>     全局
${}       el表达式

JSP的注释(<%----%>)不会在客户端显示,HTML()就会

8.4、JSP指令

<%@page args...%>
<%@ include file="" %>

1.定制错误页面<%@page args...%>

jsp2.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--定制错误页面(可以在这个jsp中引入也可以在web.xml 中配置)--%>

<%@ page errorPage="error/500.jsp" %>

<html>

<head>
    <title>Title</title>
</head>
<body>


<%
int x = 1/0;
%>

</body>
</html>

如果在web.xml中配置

<error-page>
    <error-code>500</error-code>
    <location>/error/500.jsp</location>
</error-page>

500.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<img src="../img/500.png" alt="500">

</body>
</html>

测试

500错误:

404错误:

2.共用部分<%@ include file="" %>

<head>
    <title>Title</title>
</head>
<body>

<%--方法一@ include会将三个页面合三为一--%>
<%@ include file="common/header.jsp" %>
<h1>网页主体</h1>
<%@ include file="common/footer.jsp" %>

<hr>

<%--方法二jsp标签
jsp:include:拼接页面,本质还是3个
--%>

<jsp:include page="/common/header.jsp"></jsp:include>
<h1>网页主体</h1>
<jsp:include page="/common/footer.jsp"></jsp:include>


</body>
</html>

测试

8.5、九大内置对象

  • PageContext (存东西)
  • Request (存东西)
  • Response
  • Session (存东西)
  • Application 【ServletContext】(存东西)
  • config 【ServletConfig】
  • out
  • page
  • exception

重点对象理解:

request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!
session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车;
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数
据;

具体实现:

  • 通过pageContext寻找的方式取出我们保存的值

pageContextDemon01.jsp(在一个页面取值)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%--内置对象--%>
<%
     pageContext.setAttribute("name1","张三1");//保存的数据只在一个页面中有效
     request.setAttribute("name2","张三2");//保存的数据只在一次请求中有效,请求转发会携带这个数据
     session.setAttribute("name3","张三3");//保存的数据只在一次会话中有效,打开到关闭浏览器
     application.setAttribute("name4","张三4");//保存的数据在服务器中有效,打开到关闭服务器
%>

<%
    //通过pageContext寻找的方式取出我们保存的值
    //从底层到高层(作用域):  page->request->session->application->找不到
    //JVM:双亲委派机制       应用->扩展类->rt.jar

    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>



</body>
</html>

pageDemon02.jsp(在另一个页面取值)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%--在另一个页面取pageContextDemon01.jsp中的值只能取到3和4
    如果  request请求转发就可以取出来
--%>

<%
    //通过pageContext寻找的方式取出我们保存的值
//从底层到高层(作用域)
    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>


</body>
</html>

手动修改作用域与请求转发

//手动修改作用域,第三个参数为作用域
pageContext.setAttribute("hello","hello1",PageContext.SESSION_SCOPE);//等价于  session.setAttribute("hello","hello1");


<%
    //请求转发
    pageContext.forward("/index.jsp");
    //等价于 request.getRequestDispatcher("/index.jsp").forward(request,response);
%>

8.6、JSP标签、JSTL标签、EL表达式

        <!-- jstl表达式依赖 -->
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- standard标签 -->
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>

EL表达式:${}

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

JSP标签

<%--jsp:include --%>
<h1>1</h1>

<%--转发时添加数据--%>
<jsp:forward page="/jsptag2.jsp">
    <jsp:param name="name" value="ikun"/>
    <jsp:param name="age" value="23"/>
</jsp:forward>


<%--取出参数--%>
名字:<%=request.getParameter("name")%>
年龄:<%=request.getParameter("age")%>

JSTL表达式

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

  • 核心标签(掌握部分)

  • 格式化标签

  • SQL标签

  • XML标签

JSTL标签库使用步骤:

  • 引入对应的taglib
  • 使用其中的方法
  • 在Tomcat也需要导入jstl的包。否则会报错:jstl解析错误

c:if:

<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>if测试</h3>
<hr>
<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:out>
</c:if>

<c:out value="${isAdmin}"></c:out>

</body>
</html>

c:choose:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入jstl核心标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--定义一个标量scroe,值为99--%>
<c:set var="score" value="99"/>

<c:choose>
    <c:when test="${score>=95}">
        你的成绩为优秀
    </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>

</body>
</html>

c:forEach:

<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入jstl核心标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%
    ArrayList<String> people = new ArrayList<>();
    people.add(0,"张三");
    people.add(1,"李四");
    people.add(2,"王五");
    people.add(3,"赵六");
    people.add(4,"田七");
    request.setAttribute("list",people);
%>

<%--var:每一次遍历出来的变量
    items:要遍历的对象
    begin:开始
    end:结束
    step:步长
--%>
<c:forEach var="people" items="${list}">
    <c:out value="${people}"></c:out>
    <br>
</c:forEach>

</body>
</html>

9、JavaBean

实体类

JavaBean有特定的写法:

  • 必须要有一个无参构造
  • 属性必须私有化
  • 必须有对应的set/get方法

一般用来和数据库字段做映射,ORM;

ORM:对象关系映射

  • 表--->类
  • 字段--->属性
  • 行记录--->对象

<%@ page import="com.qjd.pojo.People" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
//    People people = new People();
//    people.setId(001);
//    people.setAddress("");

%>

<jsp:useBean id="people" class="com.qjd.pojo.People" scope="page"/> 

<jsp:setProperty name="people" property="address" value="大连"/>
<jsp:setProperty name="people" property="id" value="01"/>
<jsp:setProperty name="people" property="age" value="18"/>
<jsp:setProperty name="people" property="name" value="张三"/>


姓名:<jsp:getProperty name="people" property="name"/>




</body>
</html>

10、三层架构

什么是MVC:Model View Controller (模型,视图,控制器)

10.1、早期开发

用户直接访问控制层,控制层就可以直接操作数据库

servlet--CRUD--数据库
弊端:程序十分臃肿,不利于维护
servlet的代码中!处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码
架构:设有什么是加一层解决不了的!
程序调用
JDBC
Mysql oracle sqlserver ...

10.2、MVC三层架构

Model:

  • 业务处理:业务逻辑(Service)
  • 数据持久层:CRUD(Dao)

View:

  • 展示数据
  • 提供连接发起Servlet请求(a,form,img...)

Controller:

  • 接受用户的请求:(req:请求参数、session信息...)
  • 交给业务层处理对应的代码
  • 控制视图的跳转
登录--->接收用户的登录请求--->处理用户的请求(获取用户登录的参数,username,password)--->交给业务层处理登录业务(判断用户名密码是否正确)--->Dao层查询用户名和密码是否正确--->数据库

11、Filter(重点)

Filter:过滤器,用来过滤网站的数据;

  • 处理中文乱码
  • 登录验证...

Filter开发步骤:

  1. 导包
  2. 编写过滤器
    1. 导包不要错 import javax.servlet.*;
    2. 实现Filter接口,重写对应的方法
    3. 在web.xml中配置Filter

CharacterEncodingFilter:

public class CharacterEncodingFilter implements Filter {

    //初始化    web服务器启动就初始化了
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("CharacterEncodingFilter初始化");

    }
    //Chain:链
    /*
    * 1.过滤中的所有代码,在过滤特定请求的时候都会执行
    * 2.必须要让过滤器继续通行filterChain.doFilter(servletRequest,servletResponse);
    * */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        servletResponse.setContentType("text/html;charset=utf-8");


        System.out.println("CharacterEncodingFilter执行前...");
        filterChain.doFilter(servletRequest,servletResponse);//让我们的请求继续走,如果不添加,程序到这里就被拦截停止
        System.out.println("CharacterEncodingFilter执行后...");
    }
    //销毁   web服务器关闭的时候销毁
    @Override
    public void destroy() {
        System.out.println("CharacterEncodingFilter销毁");
    }
}

web.xml:

<?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>ShowServlet</servlet-name>
        <servlet-class>com.qjd.servlet.ShowServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>ShowServlet</servlet-name>
        <url-pattern>/servlet/show</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>ShowServlet</servlet-name>
        <url-pattern>/show</url-pattern>
    </servlet-mapping>

    
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>com.qjd.filter.CharacterEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <!--只要是/servlet的任何请求,都会经过这个过滤器        -->
        <url-pattern>/servlet/*</url-pattern>
        <!--所有请求都过滤   -->
        <!--<url-pattern>/*</url-pattern>-->
    </filter-mapping>
    
</web-app>

ShowServlet:

public class ShowServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        resp.setCharacterEncoding("utf-8");
//        req.setCharacterEncoding("utf-8");
        resp.getWriter().write("你好啊");
    }

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

12、监听器

实现一个监听器的接口:(有N种)

  1. 编写一个监听器(实现监听器的接口)

    //统计网站在线人数:统计session
    public class OnlineCountListener implements HttpSessionListener{
        //    创建session监听
        //    一旦创建Session就会触发一次这个事件
        @Override
        public void sessionCreated(HttpSessionEvent se) {
    
            System.out.println(se.getSession().getId());
            ServletContext ctx = se.getSession().getServletContext();
            Integer onlineCount =(Integer) ctx.getAttribute("OnlineCount");
    
            if(onlineCount == null){
                onlineCount = new Integer(1);
            }else {
                int count = onlineCount.intValue();
                onlineCount = new Integer(count+1);
            }
    
            ctx.setAttribute("OnlineCount",onlineCount);
    
        }
    
    
    
        //    销毁session监听
        //    一旦销毁Session就会触发一次这个事件
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
    
            ServletContext ctx = se.getSession().getServletContext();
            Integer onlineCount =(Integer) ctx.getAttribute("OnlineCount");
    
            if(onlineCount == null){
                onlineCount = new Integer(0);
            }else {
                int count = onlineCount.intValue();
                onlineCount = new Integer(count-1);
            }
    
            ctx.setAttribute("OnlineCount",onlineCount);
    
        }
        /*
        * session 销毁
        * 1.手动销毁  se.getSession().invalidate();
        * 2.自动销毁  (在web.xml中配置)
        *   <session-config>
            <session-timeout>1</session-timeout>
            </session-config>
        * */
    
    }
    
  2. 配置监听器(web.xml中注册)

    <!--  注册监听器  -->
        <listener>
            <listener-class>com.qjd.listener.OnlineCountListener</listener-class>
        </listener>
    
  3. 根据情况是否使用

  4. 测试结果

13、过滤器、监听器常见应用

  • 监听关闭事件(GUI)

    public class TestPanel {
        public static void main(String[] args) {
            Frame frame = new Frame("好好学习");//新建一个窗口
            Panel panel = new Panel(null);//面板
            frame.setLayout(null);//设置窗体的布局
    
    
            frame.setBounds(300,300,500,500);
    
            frame.setBackground(new Color(0, 255, 34));
    
            panel.setBounds(50,50,300,300);
    
            panel.setBackground(new Color(128, 0, 255));
    
            frame.add(panel);
    
            frame.setVisible(true);
    
            //监听关闭事件
            frame.addWindowListener(new WindowListener() {
                @Override
                public void windowOpened(WindowEvent e) {
                    System.out.println("打开");
                }
    
                @Override
                public void windowClosing(WindowEvent e) {
                    System.out.println("正在关闭");
                    System.exit(0);//0是正常终止。1是非正常终止
                }
    
                @Override
                public void windowClosed(WindowEvent e) {
                    System.out.println("已关闭");
                }
    
                @Override
                public void windowIconified(WindowEvent e) {
    
                }
    
                @Override
                public void windowDeiconified(WindowEvent e) {
    
                }
    
                @Override
                public void windowActivated(WindowEvent e) {
                    System.out.println("激活");
                }
    
                @Override
                public void windowDeactivated(WindowEvent e) {
                    System.out.println("未激活");
                }
            });
    
    
        }
    }
    
  • 用户登录才能进入主页,用户注销后就不能进入主页(必须通过登录才能访问登录成功主页,通过直接访问登录成功主页不能进入)

  1. 用户登录之后,向Session中放入用户的数据

  2. 进入主页的时候要判断用户是否已经登录(在过滤器中实现)

    public class SysFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
            //ServletRequest      HttpServlet
            HttpServletRequest request1 = (HttpServletRequest) servletRequest;
            HttpServletResponse response1 = (HttpServletResponse) servletResponse;
            String user_session = (String) request1.getSession().getAttribute(Constant.USER_SESSION);
    
    
            if(user_session == null){
                response1.sendRedirect("/error.jsp");
    
            }
    
    
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
    

整体步骤:

  1. 制定登录页面Login.jsp
  2. LoginServlet
  3. 登录成功进入success.jsp
  4. 登录失败进入error.jsp
  5. 权限验证SysFilter
  6. 解决session名字重用问题(定义常量Constant)
  7. 配置web.xml

14、JDBC

什么是JDBC:Java DataBase Connection (Java连接数据库)

连接数据库需要jar包的支持:

  • java.sql
  • javax.sql
  • mysql-connector-java(连接驱动)

JDBC 固定步骤:

1.加载驱动
2.连接数据库,代表数据库
3.向数据库发送SQL的对象Statement : CRUD
4.编写SQL (根据业务,不同的SQL)
5.执行SQL
6.关闭连接(先开的后关)

实验环境搭建:

  1. 数据库信息

  2. 导入数据库依赖

    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.29</version>
    </dependency>
    
  3. IDEA中连接数据库

  1. JDBC六步

    public class TestJDBC {
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            //配置信息
            //解决中文乱码问题useUnicode=true&characterEncoding=utf-8
            String url = "jdbc:mysql://localhost:3306/jdbc?     useUnicode=true&characterEncoding=utf-8&useSSL=true";
            String username = "root";
            String password = "123456789";
    
            //1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.连接数据库,代表数据库
            Connection connection = DriverManager.getConnection(url, username, password);
            //3.向数据库发送SQL的对象   Statement:CRUD
            Statement statement = connection.createStatement();
             // PreparedStatement安全,可以防止sql注入(预编译)
            //4.编写sql
            String sql = "select *from jdbc.users";
    
            //5.执行查询sql,返回一个ResultSet结果集
            ResultSet rs = statement.executeQuery(sql);
            while (rs.next()){
                System.out.println("id="+rs.getObject("id"));
                System.out.println("name="+rs.getObject("name"));
                System.out.println("password="+rs.getObject("password"));
                System.out.println("email="+rs.getObject("email"));
                System.out.println("birthday="+rs.getObject("birthday"));
            }
            //6.关闭连接,释放资源(先创建的后关闭)
            rs.close();
            statement.close();
            connection.close();
    
    
        }
    }
    
  • 结果:
  1. 使用预编译防止sql注入

    public class TestJDBC2 {
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            //配置信息
            //解决中文乱码问题useUnicode=true&characterEncoding=utf-8
            String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=true";
            String username = "root";
            String password = "123456789";
    
            //1.加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
    
            //2.连接数据库,代表数据库
            Connection connection = DriverManager.getConnection(url, username, password);
            //3.编写sql
            String sql = "insert into jdbc.users(id, name, password, email, birthday) VALUES (?,?,?,?,?);";
    
            //4.预编译    PreparedStatement安全,可以防止sql注入(预编译)
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
    
            preparedStatement.setInt(1,4);//第一个占位符?的值为1
            preparedStatement.setString(2,"赵六");//第二个占位符?的值为赵六
            preparedStatement.setString(3,"523456");//第三个占位符?的值为123456
            preparedStatement.setString(4,"zl@qq.com");//第四个占位符?的值为1503349647@qq.com
            preparedStatement.setDate(5,new Date(System.currentTimeMillis()));//第五个占位符?的值为当前年月日
    
    
    
            //5.执行查询sql
            int i = preparedStatement.executeUpdate();
            System.out.println(i);
    
    
    
            //6.关闭连接,释放资源(先创建的后关闭)
    
            preparedStatement.close();
            connection.close();
    
    
        }
    }
    
  • 结果:

15、事务

要么都成功,要么都失败!

ACID原则:保证数据的安全

  • 原子性
  • 隔离性
  • 持久性
  • 一致性
开启事务
事务提交commit
事务回滚rollback
关闭事务

junit单元测试

  • 依赖

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
    
  • 简单使用:

    @Test注解只有在方法上有效,只要加了这个注解的方法就可以直接运行

搭建测试事务环境:

count表:

public class TestJDBC3 {

   @Test
    public void test() {
       //配置信息
       //解决中文乱码问题useUnicode=true&characterEncoding=utf-8
       String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&useSSL=true";
       String username = "root";
       String password = "123456789";

       Connection connection = null;

       //1.加载驱动
       try {
           Class.forName("com.mysql.cj.jdbc.Driver");

       //2.连接数据库,代表数据库
        connection = DriverManager.getConnection(url, username, password);
        
       //3.通知数据库开启事务(false是开启)
       connection.setAutoCommit(false);

       String sql = "update jdbc.account set money = money-100 where name = 'a';";
       connection.prepareStatement(sql).executeUpdate();


       //制造错误
       int i=1/0;

       String sql2 = "update jdbc.account set money = money+100 where name = 'b';";
       connection.prepareStatement(sql2).executeUpdate();



       connection.commit();//以上两条sql都提交成功了就提交事务
       System.out.println("提交成功!");
       } catch (Exception e) {
           try {
               //如果出现异常,就通知数据库回滚事务
               connection.rollback();
           } catch (SQLException throwables) {
               throwables.printStackTrace();
           }
           e.printStackTrace();
       }finally {
           try {
               connection.close();
           } catch (SQLException throwables) {
               throwables.printStackTrace();
           }
       }


    }


}
  • 出现错误就会回滚账户余额不变

  • 没有错误就会正常提交事务

16、SMBMS项目

SMBMS项目是一个用JavaWeb编写的超市管理项目,大家可以在下面这个网站下载到原始项目

SMBMS项目源码

对于这个超市管理项目,大家可以结合前面学习的知识分析代码,遇到不会的可以标记,最后再看狂神的视频讲解SMBMS项目

我相信大家经过这个项目大家会对JavaWeb有更深的理解

17、文件上传

使用类介绍

【文件上传的注意事项】
1.为保证服务器安全,上传文件应该放在深无法直接访问的目录下,比如放于WEB-NF目录下。
2.为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
3.要限制上传文件的最大值。
4.可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。

【需要用到的类详解】
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个Fileltem对象,在使用ServletFileUpload对象解析请求时需要DiskFileltemFactory对象。所以,我们需要在进行解析工作前构造好DiskFileltemFactory>对象,通过ServletFileUpload对象的构造方法或setFileltemFactory()方法设置ServletFileUpload?对象的fileltemFactory属性。

文件上传源码

  • FileSerlvet类
/**
 * Servlet implementation class FileSerlvet
 */
public class FileSerlvet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		// response.getWriter().append("Served at: ").append(request.getContextPath());
		
		// 判断上传的文件普通表单还是带文件的表单
		if (!ServletFileUpload.isMultipartContent(request)) {
			return;//终止方法运行,说明这是一个普通的表单,直接返回
		}
	    //创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访间上传的文件;
	    String uploadPath =this.getServletContext().getRealPath("/WEB-INF/upload");
	    File uploadFile = new File(uploadPath);
	    if (!uploadFile.exists()){
	    uploadFile.mkdir(); //创建这个月录
	    }
		
		// 创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
		String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
		File file = new File(tmpPath);
		if (!file.exists()) {
			file.mkdir();//创建临时目录
		}

		// 处理上传的文件,一般都需要通过流来获取,我们可以使用 request, getInputstream(),原生态的文件上传流获取,十分麻烦
		// 但是我们都建议使用 Apache的文件上传组件来实现, common-fileupload,它需要旅 commons-io组件;
		try {
			// 创建DiskFileItemFactory对象,处理文件路径或者大小限制
			DiskFileItemFactory factory = getDiskFileItemFactory(file);
			/*
			 * //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件 factory.setSizeThreshold(1024 *
			 * 1024); //缓存区大小为1M factory.setRepository (file);//临时目录的保存目录,需要一个File
			 */

			// 2、获取ServletFileUpload
			ServletFileUpload upload = getServletFileUpload(factory);

			// 3、处理上传文件
			// 把前端请求解析,封装成FileItem对象,需要从ServletFileUpload对象中获取
			String msg = uploadParseRequest(upload, request, uploadPath);
			
			// Servlet请求转发消息
			System.out.println(msg);
			if(msg == "文件上传成功!") {
				// Servlet请求转发消息
				request.setAttribute("msg",msg);
				request.getRequestDispatcher("info.jsp").forward(request, response);
			}else {
				msg ="请上传文件";
				request.setAttribute("msg",msg);
				request.getRequestDispatcher("info.jsp").forward(request, response);
			}

		} catch (FileUploadException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}

	public static DiskFileItemFactory getDiskFileItemFactory(File file) {
		DiskFileItemFactory factory = new DiskFileItemFactory();
		// 通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中;
		factory.setSizeThreshold(1024 * 1024);// 缓冲区大小为1M
		factory.setRepository(file);// 临时目录的保存目录,需要一个file
		return factory;
	}

	public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
		ServletFileUpload upload = new ServletFileUpload(factory);
		// 监听长传进度
		upload.setProgressListener(new ProgressListener() {

			// pBYtesRead:已读取到的文件大小
			// pContextLength:文件大小
			public void update(long pBytesRead, long pContentLength, int pItems) {
				System.out.println("总大小:" + pContentLength + "已上传:" + pBytesRead);
			}
		});

		// 处理乱码问题
		upload.setHeaderEncoding("UTF-8");
		// 设置单个文件的最大值
		upload.setFileSizeMax(1024 * 1024 * 10);
		// 设置总共能够上传文件的大小
		// 1024 = 1kb * 1024 = 1M * 10 = 10м

		return upload;
	}

	public static String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request, String uploadPath)
			throws FileUploadException, IOException {

		String msg = "";
		
		// 把前端请求解析,封装成FileItem对象
		List<FileItem> fileItems = upload.parseRequest(request);
		for (FileItem fileItem : fileItems) {
			if (fileItem.isFormField()) {// 判断上传的文件是普通的表单还是带文件的表单
				// getFieldName指的是前端表单控件的name;
				String name = fileItem.getFieldName();
				String value = fileItem.getString("UTF-8"); // 处理乱码
				System.out.println(name + ": " + value);
			} else {// 判断它是上传的文件
				
				// ============处理文件==============

				// 拿到文件名
				String uploadFileName = fileItem.getName();
				System.out.println("上传的文件名: " + uploadFileName);
				if (uploadFileName.trim().equals("") || uploadFileName == null) {
					continue;
				}

				// 获得上传的文件名/images/girl/paojie.png
				String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
				// 获得文件的后缀名
				String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);

				/*
				 * 如果文件后缀名fileExtName不是我们所需要的 就直按return.不处理,告诉用户文件类型不对。
				 */

				System.out.println("文件信息[件名: " + fileName + " ---文件类型" + fileExtName + "]");
				// 可以使用UID(唯一识别的通用码),保证文件名唯
				// 0UID. randomUUID(),随机生一个唯一识别的通用码;
				String uuidPath = UUID.randomUUID().toString();
				
				// ================处理文件完毕==============

				// 存到哪? uploadPath
				// 文件真实存在的路径realPath
				String realPath = uploadPath + "/" + uuidPath;
				// 给每个文件创建一个对应的文件夹
				File realPathFile = new File(realPath);
				if (!realPathFile.exists()) {
					realPathFile.mkdir();
				}
				// ==============存放地址完毕==============
				
				
				// 获得文件上传的流
				InputStream inputStream = fileItem.getInputStream();
				// 创建一个文件输出流
				// realPath =真实的文件夹;
				// 差了一个文件;加上翰出文件的名产"/"+uuidFileName
				FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);

				// 创建一个缓冲区
				byte[] buffer = new byte[1024 * 1024];
				// 判断是否读取完毕
				int len = 0;
				// 如果大于0说明还存在数据;
				while ((len = inputStream.read(buffer)) > 0) {
					fos.write(buffer, 0, len);
				}
				// 关闭流
				fos.close();
				inputStream.close();
				
				msg = "文件上传成功!";
				fileItem.delete(); // 上传成功,清除临时文件
				//=============文件传输完成=============
			}
		}
		return msg;

	}
}
  • 注册xml
<servlet>
  	<servlet-name>upload</servlet-name>
  	<servlet-class>com.qjd.FileSerlvet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>upload</servlet-name>
  	<url-pattern>/upload.do</url-pattern>
  • 导入依赖的jar包
	<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.4</version>
		</dependency>
	<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.6</version>
		</dependency>
  • index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<body>
		<%--
		GET:上传文件大小有限制
		POST:上传文件大小没有限制
		 ${pageContext.request.contextPath}
		 --%>
		<form action="upload.do" enctype="multipart/form-data"  method="post">
			上传用户:<input type="text" name="username"><br/>
			<P><input type="file" name="file1"></P>
			<P><input type="file" name="file1"></P>
			<P><input type="submit" value="提交"> | <input type="reset"></P>
		</form>
	</body>
</html>
  • info.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
 
<%=request.getAttribute("msg")%>
 
</body>
</html>

到这里JavaWeb绝大部分的知识就结束了,但是我们的学习还没有结束哦,大家要掌握JavaWeb就要多练习几个JavaWeb的实战项目,在项目中成长并深入理解JavaWeb,最后希望大家都得到自己满意的结果(◍•ᴗ•◍)
大家觉得JavaWeb这篇文章还可以的话可以点个赞啊๐•ᴗ•๐

posted @ 2022-08-30 07:49  鹤鸣呦呦、、  阅读(194)  评论(2编辑  收藏  举报