分享

javaweb笔记

 小样样样样样样 2021-06-01

JavaWeb

常见面试题:

当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?

基本概念

前言

web开发:

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

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

web应用程序

  • 可以提供给浏览器访问的程序
  • 由多个web资源组成,这些web资源可以被外界访问,对外界提供服务
  • 这些web资源真实地存在于这个世界上的某一个计算机上
  • 可以通过URL被访问到
  • 这些web资源统一放在同一个文件夹下
  • 一个web应用由多部分组成(静态web,动态web)
    • HTML CSS JS
      • jsp servlet
      • java程序
      • jar包
      • 配置文件(properties)

web应用程序若想提供给外部使用,需要一个服务器进行统一管理

静态web

*.htm, *.html,网页后缀,如果服务区上存在,就可以通过网络直接进行读取

image-20201019132437086

静态web的缺点:

  • 页面无法动态更新,所有的用户访问看到的都是都一个界面
    • 轮播图,点击特效:伪动态
    • JavaScript(在实际开发中,使用率最高)
    • VBScript
  • 无法与数据库进行交互(数据无法持久化,用户无法交互)

动态web

页面会动态展示,展示的效果会因人而异

image-20201019134134328

动态web的缺点:

  • 一旦加入服务区的web资源出现错误,需要将后台程序重新编写,重新发布
    • 往往意味着需要停机维护

动态web的优点:

  • 页面可以动态更新,所有的用户访问看到的都不是同一个界面
  • 可以和数据库进行交互(数据持久化:注册、商品信息、用户信息……)

web服务器

技术讲解

ASP

ASP是微软公司开发的代替CGI脚本程序的一种应用

在国内早期流行的就是ASP

在HTML中嵌入了VB的脚本

缺点:

  • 在ASP开发中,一个页面往往拥有几千行以上的业务代码,页面极其混乱、维护成本极高

在ASP中主要使用的是C#语言,使用IIS服务器

PHP

PHP开发速度很快,功能很强大,跨平台,代码很简单 (70% , WP)

缺点:

  • 无法承载大访问量的情况,同时也造成了PHP发展的局限性

但是中国大部分的公司都属于中小型公司,PHP完全能够解决:

WordPress

  • WordPress是使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL数据库的服务器上架设属于自己的网站。也可以把 WordPress当作一个内容管理系统CMS)来使用。

  • WordPress是一款个人博客系统,并逐步演化成一款内容管理系统软件,它是使用PHP语言和MySQL数据库开发的,用户可以在支持 PHP 和 MySQL数据库的服务器上使用自己的博客。

  • WordPress有许多第三方开发的免费模板,安装方式简单易用。不过要做一个自己的模板,则需要你有一定的专业知识。比如你至少要懂的标准通用标记语言下的一个应用HTML代码CSSPHP等相关知识。

  • WordPress官方支持中文版,同时有爱好者开发的第三方中文语言包,如wopus中文语言包。WordPress拥有成千上万个各式插件和不计其数的主题模板样式。

JSP/Servlet

使用sun公司(被Oracle收购)主推的B/S架构

基于java语言(恰好所有的互联网大公司或者是一些开源的组件,都是用java写的)

可以承载三高问题(高并发、高性能、高可用)带来的影响

语法与ASP相似,方便ASP程序员更好地转型到JSP,加强了市场强度

……

web服务器

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

IIS

微软公司发布,用于运行ASP等一些本土的东西,是Windows中自带的

image-20201019142500030

Tomcat

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

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

Tomcat 实际上运行JSP 页面和Servlet。Tomcat最新版本为9.0。

……

Tomcat

Tomcat 下载

步骤

  1. 进入到 Tomcat 官网,可以在此处下载 Tomcat。[Tomcat 官方网址][http://tomcat./]
  2. 点击左侧 Download 下的对应版本
  3. 在对应版本下载页面,下滑,找到 core
    • image-20200622224521918

注意

  • 安装包有有 zip 和 exe 两种格式
    • zip 是免安装版的(推荐使用)
    • exe 是安装版
  • 注意观察自己电脑是 64 位还是 32 位

Tomcat 安装

exe 版本安装

  1. 从下载位置双击下载的 zip,点击 next

img

  1. 同意安装协议:即点击I Agree

这里写图片描述

  1. 点开 Tomcat,选中 Service,以后将可以在管理的服务中启动和关闭 Tomcat(也可以默认,不改变配置),点击next

这里写图片描述

  1. 出现管理提示框,要求输入端口和管理密码,保持默认设置就行。默认的端口号就是8080,这里一般不用设置。点击Next。

这里写图片描述

5、点击 Next 后会出现下图,它会自动找到 JRE 位置

​如果用户没有安装 JRE,可以修改指向 JDK 目录(很多用户安装后无法编译 JSP,就是这里没找到JRE,请务必先要安装JDK,并把这个目录正确指向JRE或者JDK的目录)

这里写图片描述

  1. 点击 next,之后会出现 Tomcat 安装路径选择,一般默认安装到C盘,可以直接改为任意盘,没有的文件夹会自动创建。修改完毕后点击 Install。

这里写图片描述

  1. 安装完毕,点击 finish。

这里写图片描述

  1. 打开浏览器进入 http://localhost:8080 进入如下页面则表示安装成功:

这里写图片描述

zip 版(免安装版本)

  • 直接解压缩,找到目录 bin 下的 startup.bat,启动 Tomcat
    • shutdown.bat:关闭 Tomcat

Tomcat 配置

配置 Tomcat 之前要保证 JDK 已经配置完成

  1. 右键点击计算机——属性——高级系统设置——打开环境变量的配置窗口,在系统环境变量一栏点击新建

  2. 新建 CATALINA_BASE 变量

    • 变量名:CATALINA_BASE
    • 变量值: Tomcat 安装目录
      • 比如:D:\Program Files (x86)\Apache Software Foundation\Tomcat 9.0
  3. 新建 CATALINA_HOME 变量

    • 变量名:CATALINA_HOME
    • 变量值: Tomcat 安装目录
  4. 找到 Path 变量,点击编辑

    1. 变量名:Path
    2. 变量值:
      • Win 7:直接复制,添加在原变量值的末尾(前面的那个分号是用来和原有的变量值隔开的,如果原来有的话,就不用写了)
        • ;%CATALINA_HOME%\bin;%CATALINA_HOME%\lib
      • Win 10:点击编辑——新建,直接添加就可以
        • %CATALINA_HOME%\bin;%CATALINA_HOME%\lib

验证是否配置成功,那就运行 Tomcat,启动后,打开浏览器,敲入 http://localhost:8080 如果出现页面,那么配置成功

Tomcat 文件夹

  1. bin:启动、关闭的脚本文件
  2. conf:配置文件
  3. lib:依赖的 jar 包
  4. logs:日志文件
  5. webapps:存放的网站

核心配置文件

image-20201020153646396

可以配置启动的端口号

  • tomcat的默认端口号为:8080
  • mysql的默认端口号为:3306
  • http的默认端口号为:80
  • https的默认端口号为:443
<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

可以配置主机的名称

  • 默认的主机名为:localhost -> 127.0.0.1
  • 默认网站应用存放的位置为:webapps
<Host name="localhost"  appBase="webapps"
      unpackWARs="true" autoDeploy="true">

高难度面试题

请你谈谈网站是如何进行访问的!

  1. 输入一个域名;回车

  2. 检查本机的C:\Windows\System32\drivers\etc\hosts配置文件下有没有这个域名映射;

    1. 有:直接返回对应的 ip 地址,这个地址中,有我们需要访问的 web 程序,可以直接访问

      127.0.0.1       localhost
      ::1             localhost
      
    2. 没有:去DNS服务器找,找到的话就返回,找不到就返回找不到;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xYVeCN4T-1589698920891)(JavaWeb.assets/1567827057913.png)]

发布一个web网站

不会就先模仿

  • 将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了

网站应该有的结构

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

在IDEA中配置Tomcat

image-20201021180151039

image-20201021180223853

image-20201021180513860

解决警告问题

为什么会有这个问题:我们访问一个网站,需要指定一个文件夹名字

image-20201021180750121

image-20201021181053811

乱码问题:配置文件中设置

Windows 默认编码使用 GBK,eclipse 默认编码也是 GBK,IDEA 默认编码是 UTF-8

首先要分清是tomcat日志编码,与idea的日志显示控制台编码

tomcat日志编码:

第一种

  1. cmd内 "cd /d tomcat根目录" "bin\catalina.bat run" 运行
  2. "chcp65001"切换cmd为utf8,"chcp 936"切换cmd为gbk,确定tomcat日志编码

第二种

apache-tomcat-9.0.36\conf\logging.properties配置文件中默认使用 UTF-8

可以将java.util.logging.ConsoleHandler.encoding = UTF-8 中 UTF-8 换成 GBK 就可以

idea显示编码:

【一定】在 Help-- custom vm options 添加-Dfile.encoding=UTF-8,强制为utf8编码显示,不要自己改.vmoptions可能位置不对,idea会在用户目录复制一个

【切忌】自己改tomcat的logging.properties 为GBk 会导致调试时get/post参数乱码

Java环境变量没有配置

闪退问题:需要配置兼容性

HTTP

什么是HTTP

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

默认端口号是:80

HTTPS (全称:Hyper Text Transfer Protocol over SecureSocket Layer),是以安全为目标的 HTTP 通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性

默认端口号是:443

两个时代(个人划分)

HTTP1.0 :客户端与web服务器连接后,只能获得一个web资源,断开连接

  • HTTP/0.9:

    • HTTP协议的最初版本,功能简陋,仅支持请求方式GET,请求(Request)只有一行,并且仅能请求访问HTML格式的资源
  • HTTP/1.0:

    • 最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上

    • 在0.9版本上做了进步,增加了请求方式POST和HEAD;不再局限于0.9版本的HTML格式,根据Content-Type可以支持多种数据格式,即MIME多用途互联网邮件扩展,例如text/html、image/jpeg等;同时也开始支持cache,就是当客户端在规定时间内访问统一网站,直接访问cache即可

    • 但是1.0版本的工作方式是每次TCP连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接,就是不支持keepalive

    • TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。所以,HTTP 1.0版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段

       Connection: keep-alive
      

      这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。

      Connection: keep-alive
      

      一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。

HTTP2.0 :客户端可以与web服务器连接后,可以获得多个web资源

  • HTTP/1.1:
    • 则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议
    • 1.1 版的最大变化,就是引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。解决了1.0版本的keepalive问题,1.1版本加入了持久连接,一个TCP连接可以允许多个HTTP请求
    • 客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。

HTTP 请求

客户端向服务器发送请求(Request)

百度:

Request URL:https://www.baidu.com/   # 请求地址
Request Method:GET     # get方法/post方法
Status Code:200 OK     # 状态码:200
Remote(远程) Address:14.215.177.39:443

通用头(General header)

  • 请求行中的请求方式:GET
  • 请求方式:GETPOST,HEAD,DELETE,PUT,TRACT…
    • GET:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
    • POST:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。

请求头(Request Headers)

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

HTTP 响应

服务端响应(Response)客户端的请求

百度:

Cache-Control:private    # 缓存控制
Connection:Keep-Alive    # 连接
Content-Encoding:gzip    # 编码
Content-Type:text/html   # 类型

响应体

Accept:# 告诉浏览器,它所支持的数据类型
Accept-Encoding:    # 支持哪种编码格式  GBK、UTF-8、GB2312、ISO8859-1
Accept-Language:    # 告诉浏览器,它的语言环境
Cache-Control:# 缓存控制
Connection:# 告诉浏览器,请求完成是断开还是保持连接
HOST:www.baidu.com
Refresh:# 告诉客户端,多久刷新一次;
Location:# 让网页重新定位;

响应状态码

200:请求响应成功 200

3xx:请求重定向(转接)

4xx:找不到资源

  • 404:资源不存在

5xx:服务器代码错误

  • 502:网关错误

Maven

为什么要学习这个技术?

在 Javaweb 开发中,需要使用大量的 jar 包,我们手动去导入:

如何能够让一个东西自动帮我导入和配置这个jar包?

由此,Maven 诞生了

Maven 项目架构管理工具

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

Maven的核心思想:约定大于配置(有约束,不要去违反)

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

下载安装Maven

官网:https://maven./

image-20201021150144325

配置环境变量

在环境变量中进行如下配置:

  • MAVEN_HOME:maven 的解压路径
  • M2_HOME:maven 目录下的 bin 目录的路径
  • PATH:%MAVEN_HOME%\bin

检查配置是否成功

  • 打开 cmd ,在 cmd 中输入mvn -version

    image-20201021153009962

配置文件

maven 文件夹下 conf/settings.xml

配置阿里云镜像

在不配置镜像的情况下,maven默认会使用中央库

maven中央库在国外,有时候访问会很慢,尤其是下载较大的依赖的时候,有时候速度会很慢,甚至会出现无法下载的情况

为了解决依赖下载速度的问题,需要配置 maven 国内镜像

需要在<mirrors>标签中进行配置

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

maven镜像有两种配置:

  1. settings.xml中进行配置
  2. pom.xml中进行配置

settings.xml中进行配置会对所有 maven 工程有效,当在升级 maven 版本的时候,需要注意复制已经配置好的settings.xml到新的 maven 目录下

使用配置pom.xml的方式只会对当前工程有效

配置本地仓库

Maven 仓库的分类

  1. 本地仓库
    • 用来存储从远程仓库或中央仓库下载的插件和jar包,项目使用一些插件或jar包, 优先从本地仓库查找
    • 默认本地仓库位置在 {user.dir}/.m2/repository,${user.dir}表示windows 用户目录
  2. 远程仓库
    • 如果本地需要插件或者 jar 包,本地仓库没有,默认去远程仓库下载。 远程仓库可以在互联网内也可以在局域网内
  3. 中央仓库
    1. 在 maven 软件中内置一个远程仓库地址 http://repo1./maven2 ,它是中央仓库,服务于整个互联网,它是由 Maven 团队自己维护,里面存储了非常全的 jar 包,它包 含了世界上大部分流行的开源项目构件
 <localRepository>E:\maven\apache-maven-3.6.3\maven-repo</localRepository>

在 IDEA 中使用Maven

创建使用模版的 MAven 项目

image-20201021163232395

image-20201021163337709

image-20201021163552932

img

image-20201021175407209

创建一个普通的 Maven 项目

image-20201021174759300

image-20201021175219619

在IDEA中配置Maven

注意:

  • 经常在IDEA中会出现一个问题,项目自动创建完成后,MavenHom 会便用IDEA默认,如果发现了,手动改为本地的
  • IDEA项目创建成功后,看一眼Maven的配置

image-20201021173336458

image-20201021173445455

标记文件夹功能

方式一

image-20201021175801886

方式二

image-20201021180021416

pom.xml 文件

pom.xml 是Maven 的核心配置文件

image-20201021181441705

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

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

  <!--这里就是我们刚才配置的GAV-->
  <groupId>com.kuang</groupId>
  <artifactId>javaweb-01-maven</artifactId>
  <version>1.0-SNAPSHOT</version>
  <!--Package:项目的打包方式
  jar:java应用
  war:JavaWeb应用
  -->
  <packaging>war</packaging>


  <!--配置-->
  <properties>
    <!--项目的默认构建编码-->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!--编码版本-->
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <!--项目依赖-->
  <dependencies>
    <!--具体依赖的jar包配置文件-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </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./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>

Maven的高级之处

演示:Maven 仓库

image-20201021183831189

<!-- Maven的高级之处在于,他会帮你导入这个JAR包所依赖的其他 -->
<!-- https:///artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.9.RELEASE</version>
</dependency>

Maven 可能出现的问题

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

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

IDEA中的操作

目录树:

MAven 中的 jar 包的联系关联图

image-20201022103202477

Maven仓库的使用

地址:https:///

建议使用下载人数多的版本

Maven 遇到的问题

在使用idea的过程中,遇到其中一个maven模块变成灰色,如下所示:

造成这个的原因可能是忽略了maven模块,可以尝试如下解决方法:在idea中maven的setting中找到ignored files,看右边的面板中是否将变灰的maven模块忽略了。我的模块变灰就是因为这个原因,Settings–>Maven–>Ignored Files 看看是不是有勾选的。去掉就好了

Servlet

Servlet 简介

  • Servlet 是 sun 公司开发的动态 web 的一项技术
  • Sun 公司在 API 中提供一个接口叫做:Servlet,开发一个 Servlet 程序需要完成两个小步骤:
    1. 编写一个类,实现 servlet 接口
    2. 把开发好的 Java 类部署到 web 服务器上

把实现了Servlet接口的Java程序叫做,Servlet

编写HelloServlet程序

Serlvet 接口Sun公司有两个默认的实现类:HttpServletGenericServlet

  1. 构建一个普通的 Maven 项目,删掉里面的 src 目录,在这个项目里面建立 Moudel(模块)。这个空的工程就是Maven主工程

  2. 在 Maven 父子工程中

    • 父项目的pom.xml中自动生成
    <modules>
        <module>servlet-01</module>
        <module>servlet-02</module>
    </modules>
    <!-- 告诉编译器,在读取主 pom 时,去找两个子 pom -->
    
    • 子项目
    <parent>
        <artifactId>javaweb-maven</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <!-- 
    使子项目继承父项目的设置,避免重复导入依赖
    父项目中的,子项目可以直接使用
    -->
    

Maven 环境优化

  1. 修改web.xml为最新(到 tomcat 文件夹下的 webapps 下取)

  2. 将 maven 结构搭建完整。main下创建 java 文件夹、resources文件夹

    image-20201102192504133

编写一个 Servlet 程序

  1. 编写一个普通类

  2. 实现 Servlet 接口,这里我们直接继承HttpServlet(HttpServlet间接实现了Servlet接口)

    image-20201102193941381

  3. 编写 Servlet 的映射

    为什么需要映射?

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

    <!--注册Servlet-->
    <servlet>
        <servlet-name>hello_3</servlet-name>
        <servlet-class>org.ooo.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--Servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello_3</servlet-name>
        <url-pattern>/myserv3</url-pattern>
    </servlet-mapping>
    
  4. 配置 tomcat

    注意配置项目发布路径,参考 Tomcat

  5. 启动项目
    启动后通过请求路径访问程序:域名/发布路径/请求路径

Servlet 原理

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

image-20201102203002358

Mapping 问题

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

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

一个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>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello4</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello5</url-pattern>
</servlet-mapping>

一个Servlet可以指定通用映射路径,使用通配符 *

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

默认请求路径

<!--默认请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
  1. 使用通配符 * 可以指定一些前缀或者后缀……

    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>*.cxd</url-pattern>
    </servlet-mapping>
    
  2. 注意:

    • 默认请求路径*前面不能加项目映射的路径
    • 不建议经常使用默认请求路径,容易挤掉首页

优先级问题

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

<servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>cn.zafu.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

ServletContext

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

image-20201103155717144

共享数据

我们在一个Servlet中通过 ServletContext 对象保存(set)的数据,可以在另一个Servlet通过ServletContext对象获取(get)数据

保存数据:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        ServletContext servletContext = this.getServletContext();
        String username="安格";
        servletContext.setAttribute("username",username);
    }

获取数据:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();
        String username = (String)this.getServletContext().getAttribute("username");
        out.println("名字:"+username);
    }

注意:

  1. 创建Servlet需要在web.xml中进行注册
  2. 直接请求获取数据的Servlet是得不到值的,需要先请求一下保存数据的Servlet

获取初始化参数

可以在web.xml中,配置一些web应用初始化参数

<!--配置一些web应用初始化参数-->
<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</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().print(url);
}

请求转发

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");//转发的请求路径
    requestDispatcher.forward(req,resp);//调用forward实现请求转发
    //合并写  context.getRequestDispatcher("/gp").forward(req,rp);
}

转发和重定向

image-20201103162735775

读取资源文件

Properties 资源文件

  • 在java目录下新建.properties文件
  • 在resources目录下新建.properties文件

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

前提:首先需要一个资源文件

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        ServletContext servletContext = this.getServletContext();
        InputStream is = servletContext.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().print(user+":"+pwd);
    }

请求和响应

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

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

HttpServletResponse

简单分类

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

ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;

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

void setCharacterEncoding(String var1);

void setContentLength(int var1);

void setContentLengthLong(long var1);

void setContentType(String var1);

void setDateHeader(String var1, long var2);

void addDateHeader(String var1, long var2);

void setHeader(String var1, String var2);

void addHeader(String var1, String var2);

void setIntHeader(String var1, int var2);

void addIntHeader(String var1, int var2);

响应的状态码

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;

常见应用

向浏览器输出消息

下载文件
  1. 获取下载文件的路径
  2. 获取下载的文件名(通过截取路径最后一个\,在 Java 中注意转义\\
  3. 设置浏览器能够支持下载我们需要的东西
  4. 获取下载文件的输入流
  5. 创建缓冲区
  6. 获取OutputStream对象
  7. 将FileOutputStream流写入到buffer缓冲区
  8. 使用OutputStream将缓冲区中的数据输出到客户端(resp.getOutputStream()
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        // 1. 要获取下载文件的路径
        String realPath = "E:\\java\\IDEA\\projects\\javaweb-maven\\servlet-02\\src\\main\\1.jpg";
        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. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端!
        while ((len=in.read(buffer))>0){
            out.write(buffer,0,len);
        }

        in.close();
        out.close();
    }
验证码功能

实现验证的方式

  • 前端实现
  • 后端实现,需要用到 Java 的图片类,生产一个图片
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //如何让浏览器3秒自动刷新一次;
        resp.setHeader("refresh","3");
        //在内存中创建一个图片
        BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
        //得到图片
        Graphics2D g = (Graphics2D) image.getGraphics(); //笔
        //设置图片的背景颜色
        g.setColor(Color.white);
        g.fillRect(0,0,80,20);
        //给图片写数据
        g.setColor(Color.BLUE);
        g.setFont(new Font(null,Font.BOLD,20));
        g.drawString(makeNum(),0,20);
        //告诉浏览器,这个请求用图片的方式打开
        resp.setContentType("image/jpeg");
        //网站存在缓存,不让浏览器缓存
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");
        //把图片写给浏览器
        ImageIO.write(image,"jpg", resp.getOutputStream());
    }
    //生成随机数
    private String makeNum(){
        Random random = new Random();
        String num = random.nextInt(9999999) + "";
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 7-num.length() ; i++) {
            sb.append("0");
        }
        num = sb.toString() + num;
        return num;
    }
实现重定向

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

void sendRedirect(String var1) throws IOException;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.sendRedirect("/img");
        /*
        等价于
        resp.setHeader("Location","/img");
        resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
         */
    }

常见场景:

  • 用户登录

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

相同点

  • 页面都会实现跳转

不同点

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

image-20201103162735775

HttpServletRequest

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

获取参数,请求转发

String req.getParameter()
String[] req.getParameterValues()
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbys = req.getParameterValues("hobbys");
         //这里的 / 代表当前的web应用
        req.getRequestDispatcher("/success.jsp").forward(req,resp);
    }

Cookie、Session

会话

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

有状态会话:

一个同学来过教室,下次再来教室,我们会知道这个同学,曾经来过

一个网站怎么证明你来过?客户端 服务端

  1. 服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了;cookie
  2. 服务器登记你来过了,下次你来的时候我来匹配你;session

保存会话的两种技术

cookie

  • 客户端技术(通过响应,请求)

session

  • 服务器技术,利用这个技术,可以保存用户的会话信息?我们可以把信息或者数据放在Session中

常见问题:网站登录之后,下次不用再登录了,第二次就直接上去了

  1. 请求中拿到cookie信息

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

Cookie[] cookies=req.getCookiles();//获得cookie
cookie.getName();//获得cookie中的key
cookie.getValue();//获得cookie中的value
new Cookie("lastLoginTime",System.currentTimeMillis()+"");//新建一个cookie
cookie.setMaxAge(24*60*60);//设置cookie的有效期
resp.addCookie(cookie);//响应给客户端一个cookie
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        PrintWriter writer = resp.getWriter();
        writer.println("上一次的登陆时间为:");
        Cookie[] cookies = req.getCookies();
        if (cookies!=null){
            for (int i = 0; i < cookies.length; i++) {
                if (cookies[i].getName().equalsIgnoreCase("lastLoginTime")) {
                    long lastLoginTime = Long.parseLong(cookies[i].getValue());
                    writer.println(new Date(lastLoginTime).toLocaleString());
                }
                if (cookies[i].getName().equalsIgnoreCase("name")) {
                    String name = URLDecoder.decode(cookies[i].getValue(), "utf-8");
                    writer.println(name);
                }
            }
        }else {
            writer.println("这是第一次");
        }
        Cookie lastLoginTime = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        resp.addCookie(lastLoginTime);
        Cookie name = new Cookie("name", URLEncoder.encode("安格", "utf-8"));
        resp.addCookie(name);
    }

注意:

  • 虽然一般情况下,直接使用中文不会出现问题,但是为了以防万一,推荐使用

    • URLDecoder.decode(String value,String charset)进行解码
    • URLEncoder.encode(String value,String charset)进行编码
  • cookie一般会保存在本地的 用户目录下 appdata,但随着H5的流行,cookie逐渐被存储在浏览器中

  • 一个cookie只能保存一个信息(key,value)

  • 一个web站点可以给浏览器发送多个cookie,每个站点最多存放20个cookie,浏览器上限300个cookie

  • cookie大小有限制4kb

  • 删除cookie:

    • 不设置有效期,关闭浏览器,自动失效
    • 设置有效期时间为0
  • 在网页中会发现有一个cookie叫做jsessionid,这只是tomcat中对session id的叫法,在其它容器里面,不一定叫jsessionid

Session

什么是Session:

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

Session和cookie的区别

  • Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
  • Session把用户的数据写到用户独占Session中,服务器端保存(保存重要的信息,减少服务器资源的浪费)
  • session由服务器创建

使用场景:

  • 保存一个登陆用户的信息
  • 购物车信息
  • 在整个网站中经常会使用的数据,我们将它保存在session中
   @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("Text/html;charset=utf-8");
        //得到session
        HttpSession session = req.getSession();
        //给session中存东西
        session.setAttribute("name","赵东");
        //获取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);
    }

session.isNew()

session 就是一个全局变量,web服务程序只要打开,session就会存在,当你第一次访问时,session会自动为你分配一个session ID,所以session为新建立的,当你刷新页面时,这个session ID一直存在,不会消失,除非你关闭你要访问的web站点,或则关闭session,这个session ID才会消失。

print 和 writer

response.getWriter().print()不仅可以打印输出文本格式的(包括html标签),还可以将一个对象以默认的编码方式转换为二进制字节输出

response.getWriter().writer()只能打印输出文本格式的(包括html标签),不可以打印对象

设置session失效

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

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

session.invalidate();

  1. 调用invalidate() 方法会使该Session无效,无效只是不能调用setAttribute或者getAttribute之类的方法了,Session对象还在;

  2. 调用过invalidate()方法的Session对象如果再执行setAttribute或者getAttribute方法会抛出IllegalStateException

  3. 调用invalidate() 方法会将该Session绑定的对象全部解绑,因此如果调用request.getSession(false)方法,返回值会是null——即此时request没有绑定任何Session;

  4. 如果调用invalidate() 方法后执行request.getSession()或者request.getSession(true),那么此时会创建一个新的Session给该Request对象绑定;需要特殊说明的是,getSession()无参和参数为true的效果是一样的,并且此时如果你再次执行request.getSession(false)方法,返回的就不是null了,而是上面新创建的Session对象

request.getSession()request.getSession(boolean create)

  • request.getSession()
    • request.getSession()request.getSession(true)在效果上没有区别。只不过request.getSession()让你少打几个字而已
    • request.getSession()自动调用了request.getSession(true)
  • request.getSession(boolean create)
    • 如果有与当前的request相关联的HttpSession,那么返回与当前request关联的HttpSession
    • 如果没有,那么:
      • 如果 create == true,那么返回一个新建的HttpSession
      • 如果 create == false,那么返回 null

JSP

什么是JSP

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

在JSP技术出现之前,程序员都是通过Servlet,手动将前端代码变为Java代码

html 代码

<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

Servlet 实现

PrintWriter out = resp.getWriter();
out.write("<html>");
out.write("<body>");
out.write("<h2>Hello World!</h2>");
out.write("</body>");
out.write("</html>");

JSP最大的特点:写JSP就像在写HTML

区别:

  • HTML只给用户提供静态的数据
  • JSP页面中可以嵌入JAVA代码,为用户提供动态数据

JSP原理

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

  • 代码层面没有任何问题
  • 服务器内部工作

在tomcat中有一个work目录;这是 Tomcat 工作空间

image-20201107152437543

在IDEA中使用Tomcat的时候也会在IDEA的tomcat中生产一个work目录,作为工作空间

image-20201107152857059

我电脑的地址:

C:\Users\17289\.IntelliJIdea2019.3\system\tomcat\Unnamed_javaweb-maven_3\work\Catalina\localhost\ROOT\org\apache\jsp

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

JSP页面转换成了XX_JSP.java的Java程序

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    
public abstract class HttpJspBase extends HttpServlet

也就是说

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

JSP最终也会被转换成为一个Java类!

JSP 本质上就是一个Servlet

//初始化
  public void _jspInit()
//销毁
  public void _jspDestroy()
//JSPService
  public void _jspService(.HttpServletRequest request,HttpServletResponse response)

_jspService 做了什么

  1. 判断请求

    if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
        final java.lang.String _jspx_method = request.getMethod();
        if ("OPTIONS".equals(_jspx_method)) {
            response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
            return;
        }
        if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
            response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
            return;
        }
    }
    
  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/html");       //设置响应的页面类型
    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 访问流程图

image-20201107220854827

在JSP页面中

  • 只要是 JAVA代码就会原封不动的输出
  • 如果是HTML代码,就会被转换为:out.write("<html>\r\n"); 这样的格式,输出到前端

JSP基础语法

任何语言都有自己的语法,JAVA中有,JSP 作为Java技术的一种应用,它在支持Java所有语法前提下同时拥有一些自己扩充的语法

JSP表达式

作用:用来将程序的输出,输出到客户端 <%= 变量或者表达式%>

<%= new java.util.Date()%>
 
<%--转换为Servlet后就是--%>
 out.write(new java.util.Date());

JSP脚本片段

<% Java代码 %>

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

脚本片段的再实现

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

JSP声明

Service是类GenericServlet中最重要的方法,每次客户向服务器发出请求时,服务器就会调用这个方法。我们之前所写的JSP表达式和JSP脚本片段都是在service方法中,但有时我们需要在类中定义一些全局变量或者方法,这就需要JSP声明 <%! 全局声明 %>

<%!
    static {
    }
    private int globalVar = 0;
    public void method(){
    }
%>

JSP声明会被编译到JSP生成Java的类中。其他的就会被生成到_jspService方法中

JSP注释

<%--注释--%>

JSP的注释,不会在客户端显示,HTML的注释就会!

JSP 指令

JSP指令是为 JSP 引擎(比如 Tomcat)而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理 JSP 页面中的其余部分。

JSP 引擎会根据 JSP 的指令信息来编译 JSP,生成 Java 文件。在生成的 Java 文件中,指令就不存在了。

一般都会把 JSP 指令放到 JSP 文件的最上方,但这不是必须的。

指令通常以<%@标记开始,以%>标记结束,它的具体语法如下:

<%@ 指令名称 属性1="属性值1" 属性2="属性值2" ... 属性n="属性值n" %>

指令可以有很多个属性,它们以键值对的形式存在,并用逗号隔开。

JSP中的三种指令标签:

指令 描述
<%@ page ... %> 定义网页依赖属性,比如脚本语言、error页面、缓存需求等等
<%@ include ... %> 包含其他文件
<%@ taglib ... %> 引入标签库的定义

Page指令

用来设置整个JSP页面的相关属性和功能,其作用范围是整个JSP页面,包括使用include指令引用的其他文件。但是page指令不能作用于动态的包含文件,例如对使用jsp:include包含的文件,page指令的设置是无效的。一般情况下,page编译指令位于页面最上方,一个页面可以有多个编译配置指令

Page指令的语法格式:

<%@ page attribute="value" %>

属性

下表列出与Page指令相关的属性:

属性 描述
buffer 指定out对象使用缓冲区的大小。jsp的隐式对象out用于缓存jsp对客户端浏览器的输出,默认为8KB
autoFlush 当输出缓冲区即将溢出时,是否需要强制输出缓冲区的内容,默认为true。设置为true时可正常输出;则会在buffer溢出时产生一个异常
contentType 指定当前页面的MIME类型和字符编码。MIME类型有:text/plain、text/html(默认)、image/gif、image/jpeg等。默认字符编码方式:ISO-8859-1,若需要中文,可修改为GB2312或UTF-8
errorPage 定错误处理页面的地址。若本页面产生了异常或者错误,而该jsp页面没有对应的处理代码,此时就会自动调用该属性所指向的jsp页面
isErrorPage 与errorPage属性配合使用,指定当前页面是否可以作为另一个jsp页面的错误处理页面
extends 指定jsp编译生成的servlet所继承的父类或所实现的接口
import 导入使用的包。jsp中用import指明多个包,用逗号隔开
info 定义jsp页面的描述信息。在jsp页面中,可以直接调用getServletInfo()方法获取该值。这是由于jsp是servlet,而任何一个servlet都实现了servlet接口,servlet接口中含有getServletInfo()方法
isThreadSafe 指定对jsp页面的访问是否为线程安全,默认为true。若设置为true,则表示该jsp文件支持多线程;若为false则表示不支持
language 定义当前JSP页面使用的脚本语言,默认为java。少数服务器支持JavaBean
pageEncoding 设定jsp源文件保存时所使用的编码。由于jsp文件要响应客户端的请求,因此它会被编译成一个servlet。而servlet是一个java类,java类在内存中是以Unicode进行编码的,若jsp引擎不知道jsp的编码格式,就无法进行解码,并将其转换成内存中的Unicode编码
session 指定这个jsp页面是否支持session机制,默认为true
isELIgnored 指定是否执行EL表达式
isScriptingEnabled 确定脚本元素能否被使用
trimDirectiveWhitespaces 是否去掉指令前后的空白字符,默认为flase。该属性是jsp2.1规范中新增的;当属性值是true时,取消空白字符串

Include指令

include是jsp的静态包含指令,JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。

Include指令的语法格式如下:

<%@ include file="文件相对 url 地址" %>

include 指令中的文件名实际上是一个相对的 URL 地址

file必须是相对路径,不需要指定端口、协议、域名等。若路径以“/”开头,则该路径等同于参照jsp应用的上下文关系路径;若路径是以文件名或目录名开头,则路径就是当前jsp文件所在的路径

在include指令中,包含页面和被包含页面同一类型的参数不能被定义两次。include指令通常用来包含网址中经常出现的重复性页面,被包含文件中的任何一部分改变了,所有包含该文件的主jsp文件都需要重新进行编译

静态包含与动态包含

动态包含<jsp: include page="included.jsp"/>
静态包含<%@ include file="included.jsp"%>

静态包含的结果是把其他jsp引入当前jsp,两者合为一体,可以达到数据的共享即可以说是统一编译的

动态包含的结构是两者独立的,直到输出时才合并即为分别编译的

动态包含的jsp文件独立性很强,是一个单独的jsp文件,需要使用的对象、页面设置,都由自己创建

静态包含纯粹是把代码写在外面的一种共享方法,所有的变量都是可以和include它的主文件共享,两者高度紧密结合,不能有变量同名的冲突,而页面设置也可以借用主文件的

动态包含总是检查被包含页面的变化,静态包含不一定检查被包含页面的变化

动态包含可带参数,静态包含不能带参数,如<jsp:include page="included.jsp">放入参数</jsp:include>

Taglib指令

JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合

Taglib指令用于在 JSP 页面中导入标签库(JSP 标准标签库、第三方标签库、自定义标签库)。

Taglib指令的语法:

<%@taglib (uri="tagLibraryURI" | tagdir="tagDir") prefix="tagPrefix" %>

uri属性:用来指明自定义标记库的存放位置。该属性的唯一的标识和前缀相关的标签库描述符,可以是绝对或相对的URL

tagdir属性:指示前缀将被用于标识安装在/WEB-INF/tags/目录或子目录下的标签文件,一个隐含的标签库描述符被使用

prefix属性:定义一个prefix:tagname形式的字符串前缀,用于区分多个自定义标签。以jsp:、jspx:、java:、javax:、servlet:、sun:和sunw:开始的前缀被保留,前缀的命名必须遵循XML名称空间的命名约定

9大内置对象

JSP有九个内置对象(又叫隐含对象),不需要预先声明就可以在脚本代码和表达式中随意使用

JSP九大内置对象分为四类:

  • 输入输出对象:out对象、response对象、request对象
  • 通信控制对象:pageContext对象、session对象、application对象
  • Servlet对象:page对象、config对象
  • 错误处理对象:exception对象

九种对象简介:

final javax.servlet.jsp.PageContext pageContext;  //JSP的页面容器
javax.servlet.http.HttpSession session = null;    //用来保存每一个用户的信息
final javax.servlet.ServletContext application;   //表示所有用户的共享信息
final javax.servlet.ServletConfig config;         //服务器配置信息,可以取得初始化参数
javax.servlet.jsp.JspWriter out = null;           //页面输出
final java.lang.Object page = this;               //page:当前
HttpServletRequest request                        //获取用户的请求信息
HttpServletResponse response                      //服务器向客户端的回应信息

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

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

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

**request对象

request 对象是 javax.servlet.httpServletRequest类型的对象。 该对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)

request对象的作用域为一次请求。只在一次请求中有效,请求转发会携带这个数据

Request常用的方法:

方法 说明
getHeaderNames() 取出全部头信息
getParameterNames() 获取客户端提交的所有参数的名字
getServerPort() 获取服务器的端口号
getServerName() 获取服务器名称
getRemoteHost() 获取客户机的名称
getRermoteAddr() 获取客户的IP地址
getHeade() 获取HTTP头文件中的accept、accept-encoding和Host的值
getMethod() 获取客户提交信息的方式(get/post)
getServletPath() 获取客户提交信息的页面
getProtocol() 获取客户使用的协议
getParameter(String strTextName) 获取表单提交的信息

response对象

response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端

response对象也具有作用域,它只在JSP页面内有效

response对象方法:

方法名 说明
addCookie 添加一个Cookie对象
addHeader 添加Http文件指定名字头信息
containsHeader 判断指定名字Http文件头信息是否存在
encodeURL 使用sessionid封装URL
flushBuffer 强制把当前缓冲区内容发送到客户端
getBufferSize 返回缓冲区大小
getOutputStream 返回到客户端的输出流对象
sendError 向客户端发送错误信息
sendRedirect 把响应发送到另一个位置进行处理
setContentType 设置响应的MIME类型
setHeader 设置指定名字的Http文件头信息

**session对象

session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”

session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型

session对象保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器

session对象方法:

方法名 说明
getAttribute 获取指定名字的属性
getAttributeNames 获取session中全部属性名字,一个枚举
getCreationTime 返回session的创建时间
getId 获取会话标识符
getLastAccessedTime 返回最后发送请求的时间
getMaxInactiveInterval 返回session对象的生存时间单位千分之一秒
invalidate 销毁session对象
isNew 每个请求是否会产生新的session对象
removeAttribute 删除指定名字的属性
setAttribute 设定指定名字的属性值

**application对象

application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效

与session对象相比,application对象生命周期更长,类似于系统的“全局变量”

application对象方法:

方法名 说明
getAttribute 获取应用对象中指定名字的属性值
getAttributeNames 获取应用对象中所有属性的名,一个枚举
getInitParameter 返回应用对象中指定名字的初始参数值
getServletInfo 返回Servlet编译器中当前版本信息
setAttribute 设置应用对象中指定名字的属性值

out 对象

out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流

out对象方法介绍:

方法名 说明
print或println 输出数据
newLine 输出换行字符
flush 输出缓冲区数据
close 关闭输出流
clear 清除缓冲区中数据,但不输出到客户端
clearBuffer 清除缓冲区中数据,输出到客户端
getBufferSize 获得缓冲区大小
getRemaining 获得缓冲区中没有被占用的空间
isAutoFlush 是否为自动输出

**pageContext 对象

pageContext 对象的作用是取得任何范围的参数,通过它可以获取 JSP页面的out、request、reponse、session、application 等对象

提供了对jsp页面所有对象以及命名空间的访问

pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象

pageContext对象方法:

方法名 说明
forward 重定向到另一页面或Servlet组件
getAttribute 获取某范围中指定名字的属性值
findAttribute 按范围搜索指定名字的属性
removeAttribute 删除某范围中指定名字的属性
setAttribute 设定某范围中指定名字的属性值
getException 返回当前异常对象
getRequest 返回当前请求对象
getResponse 返回当前响应对象
getServletConfig 返回当前页面的ServletConfig对象
getServletContext 返回所有页面共享的ServletContext对象
getSession 返回当前页面的会话对象

config 对象

config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数

config对象方法:

方法名 说明
getServletContext 返回所执行的Servlet的环境对象
getServletName 返回所执行的Servlet的名字
getInitParameter 返回指定名字的初始参数值
getInitParameterNames 返回该JSP中所有的初始参数名,一个枚举

page 对象

page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针

page对象的方法:

方法名 说明
toString 将当前项目的信息打印出来
getClass 返回当前的object类
hashCode 返回page对象的hashCode值
equals 用于比较对象是否与当前对象相同

exception 对象

exception 对象的作用是显示异常信息,只有在包含 isErrorPage="true" 的页面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件

excepation对象和Java的所有对象一样,都具有系统提供的继承结构。exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch关键字来处理异常情况; 如果在JSP页面中出现没有捕获到的异常,就会生成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页面中,然后在错误页面中处理相应的 exception 对象

四种属性范围

page(pageContext):只在一个页面中保存属性。 跳转之后无效 pageContext.PAGE_SCOPE

request:只在一次请求中有效,服务器跳转之后有效。 客户端跳无效 pageContext.REQUEST_SCOPE

session:再一次会话中有效。服务器跳转、客户端跳转都有效。 网页关闭重新打开无效 pageContext.SESSION_SCOPE

application:在整个服务器上保存,所有用户都可使用。 重启服务器后无效 pageContext.APPLICATION_SCOPE

注意:如果设置过多的application属性范围会影响服务器性能

public void setAttribute(String name, Object o, int scope) {
    if (name == null) {
        throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name"));
    } else {
        if (o == null) {
            this.removeAttribute(name, scope);
        } else {
            switch(scope) {
                case 1:
                    this.attributes.put(name, o);
                    break;
                case 2:
                    this.request.setAttribute(name, o);
                    break;
                case 3:
                    if (this.session == null) {
                        throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession"));
                    }

                    this.session.setAttribute(name, o);
                    break;
                case 4:
                    this.context.setAttribute(name, o);
                    break;
                default:
                    throw new IllegalArgumentException("Invalid scope");
            }
        }

    }
}

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

首先需要添加Maven依赖

<!-- 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表达式简介

EL 全名为Expression Language。EL主要作用:

  1. 获取数据
    EL 表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的web域中检索java对象、获取数据。(某个web域中的对象,访问javabean的属性、访问list集合、访问map集合、访问数组)

    使用 EL 表达式获取数据语法:${标识符}

    EL 表达式语句在执行时,会调用pageContext.findAttribute方法,用标识符为关键字,分别从page、request、session、application四个域中查找相应的对象,找到则返回相应对象,找不到则返回”” (注意,不是null,而是空字符串

    EL 表达式可以很轻松获取JavaBean的属性,或获取数组、Collection、Map类型集合的数据

  2. 执行运算
    利用 EL 表达式可以在JSP页面中执行一些基本的关系运算、逻辑运算和算术运算,以在JSP页面中完成一些简单的逻辑运算

    <%--使用empty运算符检查对象是否为null(空)--%>
    empty(标识符)
    
  3. 获取web开发常用对象
    EL 表达式定义了一些隐式对象,利用这些隐式对象,web开发人员可以很轻松获得对web常用对象的引用,从而获得这些对象中的数据

    EL表达式语言中定义了11个隐含对象,使用这些隐含对象可以很方便地获取web开发中的一些常见对象,并读取这些对象的数据。

    语法:${隐式对象名称}获得对象的引用

  4. 调用Java方法(知道就好)
    EL 表达式允许用户开发自定义 EL 函数,以在JSP页面中通过 EL 表达式调用Java类的方法

隐含对象名称 描 述
pageContext 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。)
pageScope 代表page域中用于保存属性的Map对象
requestScope 代表request域中用于保存属性的Map对象
sessionScope 代表session域中用于保存属性的Map对象
applicationScope 代表application域中用于保存属性的Map对象
param 表示一个保存了所有请求参数的Map对象
paramValues 表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string[]
header 表示一个保存了所有http请求头字段的Map对象,注意:如果头里面有“-” ,例Accept-Encoding,则要header[“Accept-Encoding”]
headerValues 表示一个保存了所有http请求头字段的Map对象,它对于某个请求参数,返回的是一个string[]数组。注意:如果头里面有“-” ,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
cookie 表示一个保存了所有cookie的Map对象
initParam 表示一个保存了所有web应用初始化参数的map对象

JSP标签

JSP标签介绍

JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护

动作元素只有一种语法,它符合XML标准:

<jsp:action_name attribute="value" />

动作元素基本上都是预定义的函数,JSP规范定义了一系列的标准动作,它用JSP作为前缀,可用的标准动作元素如下:

语法 描述
jsp:include 在页面被请求的时候引入一个文件。
jsp:useBean 寻找或者实例化一个JavaBean。
jsp:setProperty 设置JavaBean的属性。
jsp:getProperty 输出某个JavaBean的属性。
jsp:forward 把请求转到一个新的页面。
jsp:plugin 根据浏览器类型为Java插件生成OBJECT或EMBED标记。
jsp:element 定义动态XML元素
jsp:attribute 设置动态定义的XML元素属性。
jsp:body 设置动态定义的XML元素内容。
jsp:text 在JSP页面和文档中使用写入文本的模板

JSP常用标签

jsp的常用标签有以下三个

  • <jsp:include>标签
  • <jsp:forward>标签
  • <jsp:param>标签

常见的属性

所有的动作要素都有两个属性:id属性和scope属性。

  • id属性:

    id属性是动作元素的唯一标识,可以在JSP页面中引用。动作元素创建的id值可以通过PageContext来调用

  • scope属性:

    该属性用于识别动作元素的生命周期。 id属性和scope属性有直接关系,scope属性定义了相关联id对象的寿命

    scope属性有四个可能的值: (a) page, (b)request, (c)session, 和 (d) application

<jsp:include>标签

<jsp:include>标签用于把另外一个资源的输出内容插入进当前JSP页面的输出内容之中,这种在JSP页面执行时的引入方式称之为动态引入
语法:

<jsp:include page="relativeURL | <%=expression%>" flush="true|false" />
  • page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得
  • flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端

<jsp:forward>标签

<jsp:forward>标签用于把请求转发给另外一个资源
语法:

<jsp:forward page="relativeURL | <%=expression%>" />
  • page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得

<jsp:param>标签

当使用<jsp:include><jsp:forward>标签引入或将请求转发给其它资源时,可以使用<jsp:param>标签向这个资源传递参数
语法1:

<jsp:include page="relativeURL | <%=expression%>">
     <jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>

语法2:

<jsp:forward page="relativeURL | <%=expression%>">
     <jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
 </jsp:include>

<jsp:param>标签的name属性用于指定参数名,value属性用于指定参数值

<jsp:include><jsp:forward>标签中可以使用多个<jsp:param>标签来传递多个参数

JSP 标准标签库(JSTL)

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

核心标签

核心标签是最常用的 JSTL标签。引用核心标签库的语法如下:

<%@ taglib prefix="c" uri="http://java./jsp/jstl/core" %>
标签 描述
<c:out> 用于在JSP中显示数据,就像<%= ... >
<c:set> 用于保存数据
<c:remove> 用于删除数据
<c:catch> 用来处理产生错误的异常状况,并且将错误信息储存起来
<c:if> 与我们在一般程序中用的if一样
<c:choose> 本身只当做<c:when>和<c:otherwise>的父标签
<c:when> <c:choose>的子标签,用来判断条件是否成立
<c:otherwise> <c:choose>的子标签,接在<c:when>标签后,当<c:when>标签判断为false时被执行
<c:import> 检索一个绝对或相对 URL,然后将其内容暴露给页面
<c:forEach> 基础迭代标签,接受多种集合类型
<c:forTokens> 根据指定的分隔符来分隔内容并迭代输出
<c:param> 用来给包含或重定向的页面传递参数
<c:redirect> 重定向至一个新的URL.
<c:url> 使用可选的查询参数来创造一个URL
格式化标签

JSTL格式化标签用来格式化并输出文本、日期、时间、数字。引用格式化标签库的语法如下:

<%@ taglib prefix="fmt" uri="http://java./jsp/jstl/fmt" %>
标签 描述
<fmt:formatNumber> 使用指定的格式或精度格式化数字
<fmt:parseNumber> 解析一个代表着数字,货币或百分比的字符串
<fmt:formatDate> 使用指定的风格或模式格式化日期和时间
<fmt:parseDate> 解析一个代表着日期或时间的字符串
<fmt:bundle> 绑定资源
<fmt:setLocale> 指定地区
<fmt:setBundle> 绑定资源
<fmt:timeZone> 指定时区
<fmt:setTimeZone> 指定时区
<fmt:message> 显示资源配置文件信息
<fmt:requestEncoding> 设置request的字符编码
SQL标签

JSTL SQL标签库提供了与关系型数据库(Oracle,MySQL,SQL Server等等)进行交互的标签。引用SQL标签库的语法如下:

<%@ taglib prefix="sql" uri="http://java./jsp/jstl/sql" %>
标签 描述
<sql:setDataSource> 指定数据源
<sql:query> 运行SQL查询语句
<sql:update> 运行SQL更新语句
<sql:param> 将SQL语句中的参数设为指定值
<sql:dateParam> 将SQL语句中的日期参数设为指定的java.util.Date 对象值
<sql:transaction> 在共享数据库连接中提供嵌套的数据库行为元素,将所有语句以一个事务的形式来运行
JSTL标签库使用步骤
  1. 在JSP页面中引入对应的 taglib

  2. 使用其中的方法

可能出现的问题

使用 JSTL 标签时,打开网页可能出现 500 错误:JSTL解析错误

这是因为我们只在Maven中添加了JSTL的依赖包,而Tomcat也需要导入

找到Maven下载的jar包,复制到Tomcat的lib文件夹中

这并不是必要的,这只是出现问题的解决方法,不导入不一定出错

常用标签

<c:if>

<head>
    <title>Title</title>
</head>
<body>
<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}"/>
</body>

<c:choose>c:when>

<body>
<%--定义一个变量score,值为85--%>
<c:set var="score" value="55"/>
<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>
</body>

<c:forEach>

<%

    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}"/> <br>
</c:forEach>
<c:forEach var="people" items="${list}" begin="1" end="3" step="1" >
    <c:out value="${people}"/> <br>
</c:forEach>

JavaBean

avaBean(咖啡豆) JavaBean是一种开发规范,可以说是一种技术

JavaBean 是特殊的 Java 类,使用 Java 语言书写,并且遵守 JavaBean API 规范

接下来给出的是 JavaBean 与其它 Java 类相比而言独一无二的特征:

  • 提供一个默认的无参构造函数
  • 需要被序列化并且实现了 Serializable 接口
  • 可能有一系列可读写属性
  • 可能有一系列的 getter 或 setter 方法

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

JavaBean 属性

一个 JavaBean 对象的属性应该是可访问的。这个属性可以是任意合法的 Java 数据类型,包括自定义 Java 类

一个 JavaBean 对象的属性可以是可读写,或只读,或只写

JavaBean 对象的属性通过 JavaBean 实现类中提供的两个方法来访问:

方法 描述
getPropertyName() 举例来说,如果属性的名称为 myName,那么这个方法的名字就要写成 getMyName() 来读取这个属性。这个方法也称为访问器。
setPropertyName() 举例来说,如果属性的名称为 myName,那么这个方法的名字就要写成 setMyName()来写入这个属性。这个方法也称为写入器。

一个只读的属性只提供 getPropertyName() 方法,一个只写的属性只提供 setPropertyName() 方法

访问JavaBean

<jsp:useBean> 标签可以在 JSP 中声明一个 JavaBean,然后使用。声明后,JavaBean 对象就成了脚本变量,可以通过脚本元素或其他自定义标签来访问

<jsp:useBean> 标签的语法格式如下:

<jsp:useBean id="beanName" class="package.class" 
                scope="page|request|session|application"/>
  • id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称。d值可任意只要不和同一 JSP 文件中其它<jsp:useBean>中 id 值一样就行了
  • class属性用于指定JavaBean的完整类名(即必须带有包名)
  • scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是 pagerequestsessionapplication等四个值中的一个,其默认值是page

访问 JavaBean 对象的属性

<jsp:useBean> 标签主体中使用<jsp:getProperty/> 标签来调用 getter 方法,使用 <jsp:setProperty/> 标签来调用 setter 方法,语法格式如下:

<jsp:useBean id="id" class="bean 编译的类" scope="bean 作用域">
   <jsp:setProperty name="bean 的 id" property="属性名"  
                    value="value"/>
   <jsp:getProperty name="bean 的 id" property="属性名"/>
   ...........
</jsp:useBean>

name属性指的是Bean的id属性。property属性指的是想要调用的getter或setter方法

ORM概念

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。

简述ORM

ORM(Object Relational Mapping)对象关系映射、使用对象操作数据库的设计思想。
在操作数据库之前,先把数据表与实体类关联起来。然后通过实体类的对象操作(增删改查)数据库表,这个就是ORM的行为!

ORM的作用

在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。

ORM的原理

Java中ORM的原理: 先说ORM的实现原理,其实,要实现JavaBean的属性到数据库表的字段的映射,任何ORM框架不外乎是读某个配置文件把JavaBean的属 性和数据库表的字段自动关联起来,当从数据库Query时,自动把字段的值塞进JavaBean的对应属性里,当做INSERT或UPDATE时,自动把 JavaBean的属性值绑定到SQL语句中。

持久化是什么?

ORM概念中提到了一个知识点:对象自动持久化到关系数据库中,那我们就来消化一下持久化
狭义的理解:持久化 仅仅指把域对象永久保存到数据库中;广义的理解:持久化 包括和数据库相关的各种操作(如CRUD)。
总结:持久化就是把数据同步保存到数据库或某些存储设备中。

为什么要持久化?

  1. 通过持久化技术可以减少访问数据库数据次数,增加应用程序执行速度;
  2. 代码重用性高,能够完成大部分数据库操作;
  3. 松散耦合,使持久化不依赖于底层数据库和上层业务逻辑实现,更换数据库时只需修改配置文件而不用修改代码。

ORM的缺点

  1. 关系-对象映射的实现是以性能为代价,方便了开发,牺牲了效率。
  2. 对于复杂的SQL有心无力。
  3. 无法完全屏蔽数据库底层细节,开发人员仍然要熟悉数据库底层操作

MVC 和三层架构

MVC不是三层架构!

三层架构是java特有的。MVC不管java,PHP,.net等都有这种设计模式

MVC 模式

MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发

  • Model(模型):模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器
    • 承载数据,并对用户提交请求进行计算的模块。其分为两类:
      • 一类称为数据承载 Bean:实体类,专门用户承载业务数据的,如 Student、User 等

      • 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理用户提交请求的

  • View(视图)
    • 视图代表模型包含的数据的可视化,为用户提供使用界面,与用户直接进行交互
  • Controller(控制器)
    • 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图,它使视图与模型分离开
    • 用于将用户请求转发给相应的 Model 进行处理,并根据 Model 的计算结果向用户提供相应响应

img

标准的MVC(Model-View-Controller)

这里写图片描述

Model(模型)

数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型(domain)或 JavaBean 组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据) 和 服务层(行为)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务

View(视图)

负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西

Controller(控制器)

接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 也就是说控制器做了个调度员的工作

  从图中我们还看到,在标准的MVC中模型能主动推数据给视图进行更新(观察者设计模式,在模型上注册视图,当模型更新时自动更新视图),但在Web开发中模型是无法主动推给视图(无法主动更新用户界面),因为在Web开发是请求-响应模型

Web MVC

Web MVC中的M(模型)-V(视图)-C(控制器)概念和标准MVC概念一样,我们再看一下Web MVC标准架构,如下图所示:

在Web MVC模式下,模型无法主动推数据给视图,如果用户想要视图更新,需要再发送一次请求(即请求-响应模型)

MVC 架构程序的工作流程

  1. 用户通过 View 页面向服务端提出请求,可以是表单请求、超链接请求、AJAX 请求等
  2. 服务端 Controller 控制器接收到请求后对请求进行解析,找到相应的 Model 对用户请求进行处理
  3. Model 处理后,将处理结果再交给 Controller
  4. Controller 在接到处理结果后,根据处理结果找到要作为向客户端发回的响应 View 页面。页面经渲染(数据填充)后,再发送给客户端

MVC开发模式

  • Model:JavaBean实现。用于封装业务数据
  • View:Jsp实现。用于显示数据
  • Controller:servlet实现。用于控制model和view

三层架构

三层架构(3-tier application)通常意义上的三层架构就是将整个业务应用划分为:

  • 表现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候他的所见所得
  • 业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理
  • 数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增添、删除、修改、更新、查找等

区分层次的目的即为了高内聚,低耦合的思想

我们的开发架构一般都是基于两种形式:

  • C/S架构。就是客户端/服务器端
  • B/S架构。就是浏览器/服务器

JAVAEE开发中,大多都是B/S架构的开发,B/S架构中,系统标准的三层架构包括:表现层,业务层,持久层

表现层(WEB层)

也就是我们常说的web层,它负责接受客户端请求,向客户端响应结果,通常客户端使用http请求web层

web层需要接受http请求,完成http响应

表现层包括:展示层(展示结果)和控制层(接受请求)

表现层依赖业务层,接受到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端

表现层的设计一般都使用MVC模型(MVC是表现层的设计模型,和其他层没有关系)

业务层(SERVICE层)

负责业务逻辑处理,和我们的开发项目需求息息相关,web层依赖业务层,但是业务层不依赖web层

业务层在业务处理是可能会依赖持久层,如果要对数据持久化需要保证事物一致性(事物应该放到业务层来控制)

持久层(DAO层)

负责数据持久化,包括数据层,即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中,通俗的讲,持久层就是和数据库交互,对数据库实现CRUD操作

三层架构

为了更好的降低各层间的耦合度,在三层架构程序设计中,采用面向抽象编程

即上层对下层的调用,是通过接口实现的

而下层对上层的真正服务提供者,是下层接口的实现类

服务标准(接口)是相同的,服务提供者(实现类)可以更换

这就实现了层间解耦合

img

MVC与三层架构的关系

img

SSM与三层架构的关系

SSM,即 SpringMVC、Spring 与 MyBatis 三个框架。它们在三层架构中所处的位置是不同的,即它们在三层架构中的功能各不相同,各司其职

  • SpringMVC:作为 View 层的实现者,完成用户的请求接收功能。SpringMVC 的 Controller作为整个应用的控制器,完成用户请求的转发及对用户的响应
  • MyBatis:作为 Dao 层的实现者,完成对数据库的增、删、改、查功能
  • Spring:以整个应用大管家的身份出现。整个应用中所有 Bean 的生命周期行为,均由Spring 来管理。即整个应用中所有对象的创建、初始化、销毁,及对象间关联关系的维护,均由 Spring 进行管理

img

早期的架构

image-20201109211440589

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

servlet-->CRUD-->数据库

弊端:程序十分臃肿,不利于维护

servlet的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码

架构:没有什么是加一层解决不了的

程序猿调用
↑
JDBC (实现该接口)
↑
Mysql Oracle SqlServer ....(不同厂商)

MVC、三层架构

image

Filter

Filter简介

Filter也称之为过滤器,它是Servlet技术中最实用的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp,Servlet,静态图片文件或静态HTML文件等进行拦截,从而实现一些特殊的功能

例如实现URL级别的权限访问控制(最常用)、过滤敏感词汇、压缩响应信息等一些高级功能

Servlet的API中提供了一个Filter接口(实际开发应用中,注意导包不要导错),开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,如下所示:

img

Filter是如何实现拦截的?

Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源(对应的URL)进行拦截后,WEB服务器每次在调用web资源之前,都会先调用一下filter的doFilter方法,因此,在Filter.doFilter()方法内编写代码一般分为三个步骤:

  1. 调用目标资源之前,让一段代码执行

  2. 是否调用目标资源(即是否让用户访问web资源)。

    • web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法。如果不调用FilterChain.doFilter()方法,即默认情况下,如下所示:

        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
      
            System.out.println("filter doFilter...");
            // 默认:对目标资源进行了拦截,目标资源没有被访问
        }
      
    • 若开发人员调用filterChain的doFilter方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问,如下所示:

        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            System.out.println("filter doFilter...");
            // 默认:对目标资源进行了拦截,目标资源没有被访问
      
            // 如果访问请求调用链下一个资源,这样就不会拦截该web资源
            chain.doFilter(request, response);
        }
      
  3. 调用目标资源之后,让一段代码执行

Filter开发入门

Filter开发分为三个步骤:

  1. 编写java类实现(implement)Filter接口,并重写(override)其doFilter方法(也可以同时重写init方法与destroy方法)。

  2. 在 web.xml 文件中使用<filter><filter-mapping>元素对编写的filter类进行注册,并设置它所能拦截的资源,代码如下所示:

        <!-- 对过滤器注册,为过滤器定义 name -->
        <filter>
            <filter-name>Filter1</filter-name>
            <filter-class>cn.itcast.filter.Filter1</filter-class>
        </filter>    
      
          <!-- 配置过滤器拦截哪个web 资源 -->
        <filter-mapping>
            <filter-name>Filter1</filter-name>
            <!-- 拦截路径 -->
            <url-pattern>/hello.jsp</url-pattern>
        </filter-mapping>
    

    其实这与在web.xml文件中配置Servlet是一个道理:

        <servlet>
          <servlet-name>HelloServlet</servlet-name>
          <servlet-class>cn.itcast.servlet.HelloServlet</servlet-class>
        </servlet>
      
       <servlet-mapping>
          <servlet-name>HelloServlet</servlet-name>
          <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    
  3. 在java类中覆盖init、doFilter、destroy方法

    Filter开发步骤与Servlet开发步骤如出一辙,下面是Servlet的开发步骤:

    1、继承HttpServlet

    2、配置web.xml Servlet虚拟路径

    3、覆盖doGet 和 doPost

    代码如下:

      public class Filter1 implements Filter {
          public Filter1() {
              System.out.println("创建了Filter1实例");
          }
      
          @Override
          public void destroy() {
              System.out.println("filter destroy...");
          }
      
          @Override
          public void doFilter(ServletRequest request, ServletResponse response,
                  FilterChain chain) throws IOException, ServletException {
              System.out.println("filter doFilter...");
          }
      
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
              System.out.println("filter init...");
          }
      
      }
    

Filter链 --- FilterChain(难点

在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。

web服务器根据多个Filter在web.xml文件中的注册顺序<mapping>,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。(详见API文档)

在Filter.doFilter()方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源

demo:

   <filter>
        <filter-name>Filter1</filter-name>
        <filter-class>cn.itcast.filter.Filter1</filter-class>
    </filter>    
                  
    <filter-mapping>
        <filter-name>Filter1</filter-name>
        <url-pattern>/hello.jsp</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>Filter2</filter-name>
        <filter-class>cn.itcast.filter.Filter2</filter-class>
        </init-param>
    </filter>
    
    <filter-mapping>
        <filter-name>Filter2</filter-name>
        <url-pattern>/hello.jsp</url-pattern>
    </filter-mapping>

Filter的生命周期

init(FilterConfig filterConfig)throws ServletException:

  • 和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法进行初始化(注:filter对象只会创建一次,init方法也只会执行一次)

    注意:在初始化(init)阶段,Servlet不一样,当第一次访问Servlet的时候web服务器才会创建Servlet的实例对象。

  • 开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。(filterConfig对象见下文)

doFilter(ServletRequest,ServletResponse,FilterChain)

  • 每次filter进行拦截都会执行(每请求一次,执行一次)
  • 在实际开发中方法中参数request和response通常转换为HttpServletRequest和HttpServletResponse类型进行操作

destroy():

  • 在Web容器卸载 Filter 对象之前被调用。

FilterConfig接口

在前文中,重写init方法时,观察到init方法需要FilterConfig对象的参数,接下来就谈谈FilterConfig这个接口。

用户在配置filter时,可以使用为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象可调用如下方法:

方法 说明
String getFilterName() 得到filter的名称
String getInitParameter(String name) 返回在部署描述中指定名称的初始化参数的值,如果不存在返回null
Enumeration getInitParameterNames() 返回过滤器的所有初始化参数的名字的枚举集合
public ServletContext getServletContext() 返回Servlet上下文对象的引用

demo:

  • 配置web.xml文件:

        <filter>
            <filter-name>Filter2</filter-name>
            <filter-class>cn.itcast.filter.Filter2</filter-class>
            <!-- 为过滤器配置初始化参数 -->
            <init-param>
                <param-name>company</param-name>
                <param-value>谷歌</param-value>
            </init-param>
        </filter>
        
        <filter-mapping>
            <filter-name>Filter2</filter-name>
            <url-pattern>/hello.jsp</url-pattern>
        </filter-mapping>
    
  • 实现Filter接口的自定义测试类:

      public class Filter2 implements Filter {
      
          @Override
          public void destroy() {
          }
      
          @Override
          public void doFilter(ServletRequest request, ServletResponse response,
                  FilterChain chain) throws IOException, ServletException {
              System.out.println("filter2 doFilter...");
              chain.doFilter(request, response);
          }
      
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
              // 功能一 :获得配置初始化参数
              String company = filterConfig.getInitParameter("company");
              System.out.println(company);
              // 功能二 :读取web资源文件
              ServletContext servletContext = filterConfig.getServletContext();
              // 读取web资源文件 必须获得绝对磁盘路径
              String filePath = servletContext.getRealPath("/WEB-INF/info.txt"); 
              try {
                  BufferedReader reader = new BufferedReader(new FileReader(filePath));
                  System.out.println(reader.readLine());
                  reader.close();
              } catch (FileNotFoundException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      
      }
    

配置Filter总结

  1. 对一个web资源可以配置多个过滤器

  2. 一个过滤器可以用来过滤多个web 资源

  3. <filter-mapping>中若想过滤Servlet时,可以通过Servlet对应的URL 或 Servlet的名字 两种方式配置:

    • Servlet对应的URL:

          <filter-mapping>
              <filter-name>Filter1</filter-name>
              <servlet-name>HelloServlet</servlet-name>
          </filter-mapping>
      
    • Servlet的名字:

          <filter-mapping>
              <filter-name>Filter1</filter-name>
            <url-pattern>/hello</url-pattern>
          </filter-mapping>
      
    • 测试用的Servlet配置如下:

          <servlet>
            <servlet-name>HelloServlet</servlet-name>
            <servlet-class>cn.itcast.servlet.HelloServlet</servlet-class>
          </servlet>
        
          <servlet-mapping>
            <servlet-name>HelloServlet</servlet-name>
            <url-pattern>/hello</url-pattern>
          </servlet-mapping>
      
  4. 关于<url-pattern>的写法Filter和Servlet相同,有三种方式,完全匹配、目录匹配、扩展名匹配,详解见:Java Web 之 Servlet

  5. <filter-mapping>中提供了一个可选的标签<dispatcher>

    • <dispatcher>标签指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一。默认REQUEST会过滤,其他三种方式都不会过滤。

      • request ---在请求时过滤
      • forward ---在转发时过滤
      • include ---在包含时过滤
      • error --在错误页面跳转时过滤
    • 用户可以设置多个 子元素用来指定 Filter 对资源的多种调用方式进行拦截。

    • demo:

          <filter-mapping>
              <filter-name>Filter1</filter-name>
              <servlet-name>HelloServlet</servlet-name>
              <!-- 在转发以及请求时进行过滤 -->
              <dispatcher>FORWARD</dispatcher>
              <dispatcher>REQUEST</dispatcher>
          </filter-mapping>
      

监听器Listener

监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。监听器其实就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法立即被执行。

监听器的相关概念事件源:

  • 被监听的对象(三个域对象 request,session,servletContext)
  • 监听器:监听事件源对象, 事件源对象的状态的变化都会触发监听器 。
  • 注册监听器:将监听器与事件源进行绑定。
  • 响应行为:监听器监听到事件源的状态变化时,所涉及的功能代码(程序员编写代码)

上述概念设计到3个名词概念:

  1. 事件源:即谁产生的事件
  2. 事件对象:即产生了什么事件
  3. 监听器:监听事件源的动作

由于事件源可以产生多个动作(即产生多个事件),而监听器中的每一个方法监听一个动作,故每个监听器中都有很多方法

JavaWeb中的监听器

概念

JavaWeb中的监听器是Servlet规范中定义的一种特殊类,它用于监听web应用程序中的ServletContext、HttpSession和 ServletRequest这三大域对象的创建、销毁事件以及监听这些域对象中的属性发生修改的事件

JavaWeb中监听器的分类

在Servlet规范中定义了多种类型的监听器(一共8个监听器),它们用于监听的事件源分别为ServletContext,HttpSession和ServletRequest这三个域对象。Servlet规范针对这三个对象上的操作,又把多种类型的监听器划分为三种类型:

  1. 域对象的生命周期监听:监听域对象自身的创建和销毁

    这个监听器需要实现相应的监听器接口:

    • ServletContextListener
    • HttpSessionListener
    • ServletRequestListener
  2. 域对象的属性监听:监听域对象中属性的增加和删除

    这个监听器需要实现的监听器接口为:

    • ServletContextAttributeListener
    • HttpSessionAttributeListener
    • ServletRequestAttributeListener
  3. 感知监听(都与HttpSession域对象有关):监听绑定到HttpSession域中的某个JavaBean对象的状态的监听器

    这个监听器需要实现的监听器接口:

    • HttpSessionBindingListener
    • HttpSessionActiveationListener
第一类:域对象的生命周期监听

事件源为:三大域

事件对象为:创建与销毁

监听器为:实现了ServletContextListener、HttpSessionListener、ServletRequestListener这三个接口的监听器

ServletContext的生命周期监听
public class AListener implements ServletContextListener{    
    //在项目启动时调用    
    public void contextInitialized(ServletContextEvent sce) {   
    }    
    //在项目关闭时调用    
    public void contextDestroyed(ServletContextEvent sce) {      
    }
}

在web.xml文件中对该监听器进行配置:

<listener>    
<listener-class>listener.AListener</listener-class>
</listener>

ServletContextListener监听器的主要作用:

  1. 初始化的工作:初始化对象;初始化数据。比如加载数据库驱动,对连接池的初始化。
  2. 加载一些初始化的配置文件;比如spring的配置文件。
  3. 任务调度(定时器Timer/TimerTask)
HttpSession的生命周期监听

代码同上述基本一致:

public class AListener implements HttpSessionListener{  
    //在会话产生时调用      
    public void sessionCreated(HttpSessionEvent sce) {      
    }   
     //在会话关闭时调用    
     public void sessionDestroyed(HttpSessionEvent sce) {      
    }
}

同样需要在web.xml文件中进行配置:

<listener>    
<listener-class>listener.AListener</listener-class>
</listener>
对各个监听器接口的方法中出现的类介绍

ServletContextEvent类:类中有一个方法getServletContext(),该方法返回ServletContext对象

HttpSessionEvent类:类中有一个方法getSession(),该方法返回一个HttpSession对象

ServletRequestEvent类:类中有两个方法

  • getServletContext(),用于返回一个ServletContext对象
  • getServletRequest(),用于返回一个ServletRequest对象
第二类:域对象的属性监听

事件源:三大域

事件对象:属性的增加与删除

监听器:实现了ServletContextAttributeListener、HttpSessionAttributeListener、ServletRequestAttributeListener接口的监听器

ServletContext的属性监听
public class AListener implements ServletContextAttributeListener{    

    //给ServletContext对象添加属性时调用    
    public void attributeAdded(ServletcontextAttribute scab){      
    }    
    //给ServletContext对象删除属性时调用   
    public void attributeRemoved(ServletContextAttributeEvent scab){    
    }    
    //给ServletContext对象替换属性值时调用    
    public void attributeReplaced(ServletContextAttributeEvent scab){    
    }
}

同样需要在web.xml文件中对AListener进行配置

HttpSession的属性监听
public class AListener implements HttpSessionAttributeListener{   
//给HttpSession对象添加属性时调用     
public void attributeAdded(HttpSessionAttribute scab){      
}    
//给HttpSession对象删除属性时调用    
public void attributeRemoved(HttpSessionAttributeEvent scab){    
}        
//给HttpSession对象替换属性值时调用    
public void attributeReplaced(HttpSessionAttributeEvent scab){    
}
}

同样需要在web.xml中对AListener进行配置

ServletRequest的属性监听
public class AListener implements ServletRequestAttributeListener{    
//给ServletRequest对象添加属性时调用    
public void attributeAdded(ServletRequestAttribute scab){      
}    
//给ServletRequest对象删除属性时调用    
public void attributeRemoved(ServletRequestAttributeEvent scab){    
}    
//给ServletRequest对象替换属性值时调用    
public void attributeReplaced(ServletRequestAttributeEvent scab){    
}
}

同样需要在web.xml中对AListener进行配置

对各个监听器接口的方法中出现的类介绍

ServletContextAttributeEvent类:该类对象有三个方法

  • getSevletContext()用于返回一个ServletContext
  • getName()用于返回属性名
  • getValue()用于返回属性值

HttpSessionBindingEvent类:该类对象有两个方法

  • getName()用于获取属性名
  • getValue()用于获取属性值

ServletRequestAttributeEvent类:该类对象有两个方法

  • getName()用于获取属性名
  • getValue()用于获取属性值
第三类:感知监听器

保存在Session域中的对象可以有多种状态:

绑定session.setAttribute(“bean”,Object)到Session中,随Session对象持久化到一个存储设备中

从Session域中解除session.removeAttribute(“bean”)绑定,随Session对象从一个存储设备中恢复

Servlet 规范中定义了两个特殊的监听器接口HttpSessionBindingListenerHttpSessionActivationListener来帮助JavaBean 对象了解自己在Session域中的这些状态,实现这两个接口的类不需要 web.xml 文件中进行注册

HttpSessionBindingListener接口

实现了HttpSessionBindingListener接口的JavaBean对象可以感知自己被绑定到Session中和Session中删除的事件
当对象被绑定到HttpSession对象中时,web服务器调用该对象的void valueBound(HttpSessionBindingEvent event)方法
当对象从HttpSession对象中解除绑定时,web服务器调用该对象的void valueUnbound(HttpSessionBindingEvent event)方法

public class JavaBeanDemo1 implements HttpSessionBindingListener {      
    private String name;          
    @Override     
    public void valueBound(HttpSessionBindingEvent event) {         
    System.out.println(name+"被加到session中了");     
    }      
    @Override     
    public void valueUnbound(HttpSessionBindingEvent event) {         
    System.out.println(name+"被session踢出来了");     
    }      
    public String getName() {         
    return name;     
    }      
    public void setName(String name) {         
    this.name = name;     
    }      
    public JavaBeanDemo1(String name) {         
    this.name = name;     
    } 
}

上述的JavaBeanDemo1这个JavaBean实现了HttpSessionBindingListener接口,那么这个JavaBean对象可以感知自己被绑定到Session中和从Session中删除的这两个操作

HttpSessionActivationListener接口

实现了HttpSessionActivationListener接口的JavaBean对象可以感知自己被活化(反序列化)和钝化(序列化)的事件

当绑定到HttpSession对象中的JavaBean对象将要随HttpSession对象被钝化(序列化)之前,web服务器调用该JavaBean对象的void sessionWillPassivate(HttpSessionEvent event)方法。这样JavaBean对象就可以知道自己将要和HttpSession对象一起被序列化(钝化)到硬盘中

当绑定到HttpSession对象中的JavaBean对象将要随HttpSession对象被活化(反序列化)之后,web服务器调用该JavaBean对象的·void sessionDidActive(HttpSessionEvent event)·方法。这样JavaBean对象就可以知道自己将要和 HttpSession对象一起被反序列化(活化)回到内存中

JavaBean随着HttpSession对象一起被活化的前提是该JavaBean对象除了实现该接口外还应该实现Serialize接口

public class JavaBeanDemo2 implements HttpSessionActivationListener, Serializable {            
    private static final long serialVersionUID = 7589841135210272124L;    
    private String name; 
         
    @Override     
    public void sessionWillPassivate(HttpSessionEvent se) {                  
        System.out.println(name+"和session一起被序列化(钝化)到硬盘了,session的id是:"+se.getSession().getId());     
    }      
    @Override     
    public void sessionDidActivate(HttpSessionEvent se) {         
        System.out.println(name+"和session一起从硬盘反序列化(活化)回到内存了,session的id是:"+se.getSession().getId());     
    }     
    public String getName() {         
        return name;     
    }     
    public void setName(String name) {         
        this.name = name;     
    }      
    public JavaBeanDemo2(String name) {        
        this.name = name;     
    } 
}

session序列化(钝化)

在session上线时,会在tomcat/work/Catalina/localhost/项目名/ 下生成一个sessions.ser文件,里面存放了所有session的信息,当你正在访问某个网页时若此时服务器关闭(关闭时才生成这个文件)又打开(打开后这个文件会消失),你依旧能正常访问该网页。(故说session有重生的效果)。若想废掉session的序列化,需要在tomcat/conf/context.xml中添加

<Manager pathname=“”/>

session的钝化与活化

Tomcat 会在session一段时间内不被使用时钝化session对象,所谓钝化session,就是把session通过序列化的方法保存到硬盘文件中

当用户再使用session时,Tomcat还会把钝化的对象再活化session,所谓活化就是把硬盘文件中的session在反序列化中放回内存

当session被tomcat钝化时,sesseion中存储的对象也被钝化,当session被活化时,也会把session中存储的对象(JavaBean对象)活化

如果某个类(JavaBean对象)实现了HttpSessionActiveationListener接口后,当对象随着session被钝化和活化时,下面两个方法就会被调用:

  • public void sessionWillPassivate(HttpSessionEvent se):当对象感知被活化时调用本方法
  • public void sessionDidActivate(HttpSessionEvent se):当对象感知被钝化时调用本方法

钝化时会在tomcat/work/Catalina/localhost/项目/mysession/ 文件下生成一个后缀为.session的文件,网页中一个被钝化的session就对应一个.session文件(而上面的序列化是一个.ser文件存在所有的session),在活化时此文件也不会消失(不同于上述的.ser文件消失)。当然要看到上述效果,应该先配置tomcat钝化session的参数,在tomcat/conf/catalina/localhost 目录下,添加配置内容

JDBC

具体参考 MySQL.md

需要jar包的支持:

  • java.sql
  • javax.sql
  • mysql-conneter-java… 连接驱动(必须要导入)

导入数据库依赖

<!--mysql的驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>

JDBC 固定步骤:

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

Junit单元测试

依赖

<!--单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

使用

在方法上添加@Test注解,这个方法就可以直接运行,不需要再创建main方法,简单便利

@Test
public void test(){
    System.out.println("Hello");
}

成功的时候是绿色:

[(img-OsUubVNQ-1588757845424)(JavaWeb.assets/1568442261610.png)]

失败的时候是红色:

[(img-qv2oTEGI-1588757845425)(JavaWeb.assets/1568442289597.png)]

可能出现的问题

在父项目中添加子模块时,pom.xml中会自动添加junit依赖,同时子模块也会从父项目中继承父项目中的junit依赖,可能在使用@Test注解时没有出现,尝试把子模块中的junit依赖删除

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多