JavaWeb

1.JAVASE与JAVAEE区别

  • JAVASE:指Java类在单独执行,在单独处理业务时需要遵守语法规则。

                    比如 继承,访问权限,接口实现,控制语句,标识符命名。。。

  • JAVAEE: Java在进行商业开发时遵守开发规则在商业开发过程中,往往需要Java类与不同服务器进行沟通来解决当前业务

            由于在商业开发过程中,Java需要与13种不同服务器进行沟通,因此SUN公司

            根据13种服务器特征指定13套接口,这13套接口统称为JAVAEE规范。

            SUN公司相当于【汽车制造工厂】负责提供接口(Engine),但是不负责提供接口

            中实现类。接口中实现类由不同服务器厂商来提供。服务器厂商将完成接口实现类

            以JAR包形式提供。Java程序员通过jar包得到接口中实现类,从而实现与指定服务器
            之间交流。

2.互联网通信模型

B/S通信模型:(以后主要走的方向是这个。)

  • B:browser,浏览器
    • 浏览器安装在客户端计算机软件
    • 可以向任意服务器发送请求,索要资源文件
    • 可以将服务器返回的【二进制数据】解析为【文字,数字,图片,视频,命令】
    • Browser支持哪些语言:HTML CSS JavaScript
  • S: server software 服务器软件
    • 服务器软件专门安装在服务端计算机上
    • 可以接收任意浏览器发送请求
    • 自动的在服务端计算机上定位被访问的资源文件
    • 自动的将定位的资源文件内容以二进制形式发送回发起请求浏览器上
    • S是服务器端Server,Server端的语言很多:C C++ Java python.....(我们主要是使用Java语言完成服务器端的开发)

 适用场景: 既适用于个人娱乐市场,又广泛适用于企业日常活动

优缺点:

  • 优点:

    1.不会增加用户获得服务的成本
    2.几乎不需要更新浏览器

    升级方便,只升级服务器端代码即可。维护成本低。

  • 缺点:

      1.几乎无法有效对服务端计算机资源文件进行保护
      2.服务端计算机工作压力异常巨大-----》【B/S通信下高并发解决方案】

        速度慢、体验不好、界面不炫酷


        企业内部的解决方案都是采用B/S架构的系统,因为企业内部办公需要的一些系统
        不需要炫酷,不需要特别好的用户体验,只要能做数据的增删改查即可。并且企业
        内部更注重维护的成本。

        B/S架构的系统有哪些代表?
            京东
            百度
            天猫
            ....

    C/S架构

C/S通信模型:

  • C,client software;客户端软件

                 1)客户端软件专门安装在客户端计算机上
                 2)帮助客户端计算机向指定服务端计算机发送请求,索要资源文件
                 3)帮助客户端计算机将服务端计算机发送回来【二进制数据】解析
                    为【文字,数字,图片,视频,命令】

  • S,server software;服务器软件

                 1)服务器软件专门安装在服务端计算机上
                 2)服务器软件用于接收来自于特定的客户端软件发送请求
                 3)服务器软件在接收到请求之后自动的在服务端计算机上定位被访问的资源文件
                 4)服务器软件自动的将定位的文件内容解析为【二进制数据】通过网络发送回
                   发起请求的客户端软件上

 适用场景:C/S通信模型普遍用于个人娱乐市场,比如【微信,淘宝/京东,视频(优酷/B站),大型网络游戏(魔兽/英雄联盟)】
                           企业办公领域相对应用较少

  • 优缺点:
  • 优点:

      1.安全性较高
      2.有效降低服务端计算机工作压力

   速度快,体验好,界面炫酷。(娱乐型的系统多数是C/S架构的。)

  • 缺点:

      1.增加客户获得服务的成本
      2.更新较为繁琐
      缺点:升级麻烦,维护成本较高。

3.共享资源文件:

  • 什么是共享资源文件:

            可以通过网络进行传输的文件,都被称为共享资源文件所有的文件内容都可以通过网络传输,所有文件都是共享资源文件

  • Http服务器下对于共享资源文件分类

        1)静态资源文件
        2)动态资源文件

  • 静态资源文件:

             1) 如果文件内容是固定,这种文件可以被称为【静态资源文件】(文档,图片,视频)
             2) 如果文件存放不是内容而是命令,这些命令只能在浏览器编译与执行,这种文件可以被称为【静态资源文件】(.html,.css,.js)

  • 动态资源文件:

                如果文件存放命令,并且命令不能在浏览器编译与执行;只能在服务端计算机编译执行,这样的文件可以被称为【动态资源文件】
        (.class)因为.class文件的执行需要jvm,而浏览器中没有jvm文件。

  • 静态资源文件与动态资源文件调用区别

           静态文件被索要时,Http服务器直接通过【输出流】将静态文件中内容或则命令以【二进制形式】推送给发起请求浏览器

          动态文件被索要时,Http服务器需要创建当前class文件的实例对象,通过实例对象
          调用对应的方法处理用户请求,通过【输出流】将运行结果以【二进制形式】推送给发起请求浏览器 

          class Student{
             public int add(int num1,int num2){
                int sum = num1 + num2;
            return sum;
             }
          }
         Http服务器(自动)
          Student stu = new Student();
          int 结果 = stu.add(10,20);
          out.print(结果)

4.开发人员在互联网通信流程担负职责:

  • 控制浏览器行为
  • 开发动态资源文件来解决用户请求

5.Http网络协议包

  • 网络协议包:
    • 在网络中传递信息都是以【二进制】形式存在的。
    • 接收方【浏览器/服务器】在接收信息后,要做第一件事就是将【二进制数据】进行编译【文字,图片,视频,命令】
    • 传递信息数据量往往比较巨大,导致接收方很难在一组连续二进制得到对应数据比如 浏览器发送一个请求: http://192.168.100.2:8080/index.html
                     这个请求信息以二进制形式发送 01010101010110101010101101010
                     Http服务器很难从二进制数据得到相关信息
    • 网络协议包一组有规律二进制数据,在这组数据存在了固定空间每一个空间专门存放特定信息,这样接收方在接收网络协议包之后
                就可以到固定空间得到对应信息,网络协议包出现极大降低了
                接收方对接收二进制数据编译难度

          【0000(ip地址)0000(端口号)0000(资源文件名)0000】

  • 常见网络协议:
    • FTP网络协议包
    • Http网络协议包
  • Http网络协议包:

                在基于B/S结构下互联网通信过程中,所有在网络中传递信息都是保存在Http网络协议包

        分类:
              Http请求协议包

              Http响应协议包

  • Http请求协议包与Http响应协议包介绍:

                 1.Http请求协议包:
                           在浏览器准备发送请求时,负责创建一个Http请求协议包
                   浏览器将请求信息以二进制形式保存在Http请求协议包各个空间
                   由浏览器负责将Http请求协议包推送到指定服务端计算机

                 2.Http响应协议包:
                           Http服务器在定位到被访问的资源文件之后,
                   负责创建一个Http响应协议包Http服务器将定位文件内容或则文件命令以
                   二进制形式写入到Http响应协议包各个空间由Http服务器负责将Http响应协议包推送回
                   发起请求的浏览器上。

  • Http请求协议包内部空间:【背】

     1.按照自上而下划分,分为4个空间

     2.空间划分:

                请求行:[
                    url:请求地址(http://192.168.100.2:8080/index.html)
                    method:请求方式(POST/GET)
                 ]

                请求头:[
        
                      请求参数信息【GET】
                 ]

                空白行:[
                        没有任何内容,起到隔离作用
         
                 ]

                 请求体:[
          
                     请求参数信息【POST】
                  ]
  • Http响应协议包内部结构 【背】

          1.按照自上而下划分,分为4个空间

      2.空间划分:

                 状态行:[
                          Http状态码
             
                      ]

                     响应头:[
                          content-type: 指定浏览器采用对应编译器
                                对响应体二进制数据进行解析
             
                      ]

                     空白行:[
                          没有任何内容,起到隔离作用
             
                      ]
                     响应体:[
                         可能被访问静态资源文件内容
                         可能被访问的静态资源文件命令
                         可能被访问的动态资源文件运行结果
                          *****都是以二进制形式***
                      ]

6.MVC开发规则:

一。介绍:
       1.MVC开发规则制定了互联网通信开发过程中必须出现角色有哪些

       2.MVC开发规则制定了互联网通信开发过程中必须出现角色担负职责

       3.MVC开发规则制定了互联网通信开发过程中必须出现角色的出场顺序


二。 角色

      DAO对象: DAO对象提供某张表文件的操作细节,降低对表文件操作难度。
                            避免反复开发表文件操作的代码提高代码复用性


      Service对象:服务对象,提供【业务】的具体解决方案
                           service对象一个方法指定一个业务的解决方案
                           避免业务开发重复性开发行为,提供复用性
                           网站每一个业务都有一个独立标准解决方案


三。 业务:
        浏览器向Http服务器发送请求

        用户向网站发送请求

        举个栗子: 张三用户发送请求:要求在服务端实现将张三账户3000元钱转给李四账户

           业务处理方案:

            1. 判断"张三"是否是当前系统中用户

            2. 判断"李四"是否是当前系统中用户

            3. 读取"张三账户余额",判断余额是否充足

            4. 读取"李四账户余额",背账

            5. 更新"张三账户余额 - 3000"

            6. 更新 "李四账户余额 + 3000"

四。业务特征:

          1.真实业务场景中,一个业务往往包含多个分支任务。因此解决业务开发工作量往往比较巨大

          2.真实业务场景中,只有所有分支任务都能顺利成功解决,才可以认为当前业务处理成功


五。解决业务开发困扰:

          1. 一个业务可能在网站的多个地方重复出现,如果不做【封装】,增加开发难度,进行业务解决代码重复性开发

          2.【百人有百味】,不同程序员面对同一个业务时,给出解决方案往往有偏差,导致最终解决数据会有偏差

六。MVC开发规则------互联网通信开发过程中必须出现角色有哪些

          一次互联网开发过程,必须出现角色有三个:

      C, contorller object ; 控制层对象   (servlet对象)

      M, model  object ;      业务模型对象 (Service 对象)

      V, view   object;       视图层对象    (jsp  or  HttpServletResponse)


七。MVC开发规则------互联网通信开发过程中必须出现角色担负职责

         C(servlet对象):
             1)【可以】调用【请求对象】读取【请求包】参数信息

             2)【必须】调用【Service对象】处理业务        

             3)【必须】调用【 视图层对象】将结果写入到响应体

         M(service对象):

              1)处理业务中所有分支任务
              2)根据分支任务执行情况判断业务是否处理成功
              3)必须通过return将处理结果返回给【控制层对象】

         V(jsp/HttpServletResponse)
                    
               1)[禁止参与业务处理]
               2)唯一任务将处理结果写入到响应体

七。互联网通信开发过程中必须出现角色的出场顺序

 

                                                 发送请求                        ------->DeptDao  
        请求调用顺序:  浏览器------------>Servlet------>Service
                                                                                         -------->EmpDao

                                        分支任务结果
                         DeptDao-------------->                                                                            tomcat
    响应顺序                     分支任务结果   Service------>Servlet------>View---->响应体---------->浏览器
                         EmpDao---------------->

在客户端与数据库端之间需要经过Web应用服务器端(Tomcat)来进行协调。

要将程序解耦,采用MVC框架。

  • Model(模型) - 主要表示业务逻辑(service、repository、entity)其中repository负责操作数据库,entity是将数据库映射为Java。
  • View(视图) - jsp、Html、APP客户端等
  • Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开(Servlet)。
  • 请求进入JavaWeb应用后,Controller接收该请求,进行业务逻辑处理,最终将处理的结果再返回给用户

IDEA中配置Tomcat

https://baijiahao.baidu.com/s?id=1664552036548041365&wfr=spider&for=pc

这个javaweb工程取什么名字都可以(可以是中文名称),但是在deployment中需要给其取一个英文别名。

我电脑上经常会出现端口被占用的情况,所以要先进任务管理器,然后将

将它关掉。

建立完成后内部结构如图:

注意静态资源文件要放到web文件夹下面!

WEB-INF里面存放的是各种配置文件

因此在完成上述配置以后要新建一个目录lib(放在WEB-LIB下面)。

同时需要将JDBC加入其中,其方式如下:

7.Tomcat文件夹含义:

安装完成Tomcat后,对其中的文件夹含义的汇总:

bin -存放可执行二进制文件,其中包括各个平台下启动和停止服务的脚本文件等。如setup.bat(bat用于windows系统)setup.sh(sh用于linux系统)

conf -存放各种服务器的配置文件(这里用来配置端口,通过IP找到电脑后,通过端口来区分服务)

lib -外部引入的jar包文件

logs -存放日志,表示服务运行情况

temp -运行时的临时文件

webapps -存放允许客户端访问的资源(java程序)

work -存放Tomcat将JSP转化后的Servlet文件

在编写好代码后,会被压缩成一个artifacts包,最终外部的访问也是访问这个包

8.URL

在WWW上,每一信息资源都有统一的且在网上唯一的地址,该地址就叫URL(Uniform Resource Locator,统一资源定位符),它是WWW的统一资源定位标志,就是指网络地址。

eg: http://localhost:8080/mytomcat001-war-exploded/ 这里面localhost是本地IP,8080是Tomcat端口号,mytomcat001-war-exploded是Tomcat下的某一个服务(工程名)

9.Servlet介绍

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

它主要可以用来创建并返回基于客户请求的动态HTML页面以及与数据库进行通信。

Servlet规范介绍:

  • servlet规范来自于JAVAEE规范中的一种
  • 作用:
    • 在Servlet规范中,指定【动态资源文件】开发步骤
    • 在Servlet规范中,指定Http服务器调用动态资源文件规则
    • 在Servlet规范中,指定Http服务器管理动态资源文件实例对象规则

Servlet这一组接口在javax.servlet包中(javax包与java包,javax包相当于2.0版本)

自定义一个类,并且实现Servlet接口,这个类就具备了接受客户端请求以及做出相应的功能。

10.Servlet接口实现类:

  • Servlet接口来自于Servlet规范下一个接口,这个接口存在Http服务器提供jar包
  • Tomcat服务器下lib文件有一个servlet-api.jar存放Servlet接口(javax.servlet.Servlet接口)
  • Servlet规范中认为,Http服务器能调用的【动态资源文件】必须是一个Servlet接口实现类
           例子:
                   class Student{
                 //不是动态资源文件,Tomcat无权调用
               }

               class Teacher implements Servlet{
                 //合法动态资源文件,Tomcat有权利调用

                 Servlet obj = new Teacher();
                 obj.doGet()
               }

11.Servlet接口实现类开发步骤

  • 第一步:创建一个Java类继承与HttpServlet父类,使之成为一个Servlet接口实现类
  • 第二步:重写HttpServlet父类两个方法。doGet或则doPost

                          get
              浏览器------》oneServlet.doGet()  (浏览器通过GET请求调用了自定义的servlet中的doGet方法)
                           post
               浏览器------》oneServlet.doPost()

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 *  子类----》父类----A接口
*  此时 子类也是A接口实现类
*
 *  抽象类作用:降低接口实现类对接口实现过程难度
*            将接口中不需要使用抽象方法教给抽象类进行完成
*            这样接口实现类只需要对接口需要方法进行重写
*            也就是说Servlet中有一些没有用的方法在通过多次继承已经被实现了(抽象类中可以有抽象方法和实例方法),
*            最后只有一个service方法是我们所需要的,交给我们自己写。
 *           在GenericServlet中,只有service被留了下来,仍然是抽象方法 
 *           public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
*   servlet接口: 5个方法)
*                init
 *                getServletConfig
 *                getServletInfo
 *                destory-----------四个方法对于Servlet接口实现类没用
*
 *                service()----有用
*
 *  Tomcat根据Servlet规范调用Servlet接口实现类规则:
 *               1.Tomcat有权创建Servlet接口实现类实例对象
*               Servlet oneServlet = new  OneServlet();
 *               2.Tomcat根据实例对象调用service方法处理当前请求(在HttpServlet中service方法已经被重写了,
 *                   可以区分进来的是GET请求还是POST请求,如果是GET请求,则将调用doGet方法,
 *                   如果是POST请求,则将调用doPost方法,所以我们需要重写这两个方法。)
*               oneServlet.service(),此时service方法中 this=====oneServlet
 *                extends                        extends                      implements
 *    oneServlet---------->(abstract)HttpServlet----->(abstract)GenericServlet----------> servlet接口
*                                                     init
 *                                                     destory
 *                                                     getServletInfo
 *                                                     getServletConfig
 *
 *  通过父类决定在何种情况下调用子类中方法-------【设计模式】---模板设计模式
*
 *  HttpServlet:  service(){   //在HttpServlet中service方法已经被重写了
 *
 *                      if(请求方式 == GET){
 *                          this.doGet
 *                      }else if(请求方式 == POST){
 *                          this.doPost
 *                      }
 *
 *                  }
 *                  doGet
 *                  doPost
 *
 *  OneServlet:   doGet  doPost   // 我们需要在自己定义的类中重写这两个方法
 *
 *   Servlet oneServlet = new  OneServlet();
 *   oneServlet.service()*/
public class OneServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("OneServlet类针对浏览器发送GET请求方式处理");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("OneServlet类针对浏览器发送POST请求方式处理");
    }
}
  • 第三步:将Servlet接口实现类信息【注册】到Tomcat服务器

浏览器不能直接访问Servlet文件,只能通过映射的方式来间接访问Servlet,映射需要开发者手动配置。可以用基于注解的方式来映射。

基于XML文件的配置方式:

                   【网站】--->【web】---》【WEB-INF】--->web.xml

                <!--将Servlet接口实现类类路径地址交给Tomcat-->
                <servlet>
                   <servlet-name>mm</servlet-name> <!--声明一个变量存储servlet接口实现类类路径(自定义的随便取名)-->
                   <servlet-class>com.bjpowernode.controller.OneServlet</servlet-class><!--声明servlet接口实现类类路径-->
                </servlet>

                <!--为了降低用户访问Servlet接口实现类难度,需要设置简短请求别名-->
                <!--servlet-mapping是映射,将com.bjpowernode.controller.OneServlet映射成了/one-->
                <servlet-mapping> 
                                <servlet-name>mm</servlet-name>
                <url-pattern>/one</url-pattern> <!--设置简短请求别名,别名在书写时必须以"/"为开头-->
                </servlet-mapping>

                如果现在浏览器向Tomcat索要OneServlet时地址

                http://localhost:8080/myWeb/one(之前应该是http://localhost:8080/myWeb/com/bjpowernode/controller/OneServlet.class)

通过注解方式:

在src里面的MyServlet(自己写的)import下面写入@WebServlet("/demo1");demo1是自己设定的,可以随便改。

快速新建Servlet:

如果选中这个框,则采用的是注解的方式

否则将采用XML文件进行配置

在configurations中,将这两个地方设置成如下形式,则每次以调试模式开启时,对动态文件进行修改后,tomcat都会进行自动更新

12.Servlet的层次结构:

Servlet ->GenericServlet(屏蔽了Servlet中其他不怎么需要的四种方法) ->HttpServlet(对Service进行分发(分成doGet等))

其中Servlet是最顶层了一般不用,一般就是用HttpServlet,HttpServlet继承了GenericServlet,而GenericServlet又继承了Servlet。

HTTP请求常用的四种:GET / POST / PUT /DELETE

目前主要用到的就是HttpServet的doGet 和 doPost两个函数。通过地址栏去访问就是Get请求

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

doGet方法提交表单的时候会在url后边显示提交的内容,所以不安全。而且doGet方法只能提交256个字符(1024字节),而doPost没有限制,因为get方式数据的传输载体是URL(提交方式能form,也能任意的URL链接),而POST是HTTP头键值对(只能以form方式提交) , Post适合发送大量的数据。

get生成方式有四种:1)直接在URL地址栏中输入URL(在现有的url中加上?然后可以直接传值)。2)网页中的超链接<a>标签。3)form中method为get。4)form中method为空时,默认是get提交。

post目前只知道有一种:form中method属性为post。

13.Servlet(最顶层)的生命周期

  • 网站中所有的Servlet接口实现类的实例对象,只能由Http服务器负责额创建。开发人员不能手动创建Servlet接口实现类的实例对象
  • 在默认的情况下,Http服务器接收到对于当前Servlet接口实现类第一次请求时自动创建这个Servlet接口实现类的实例对象

              在手动配置情况下,要求Http服务器在启动时自动创建某个Servlet接口实现类的实例对象

              <servlet>
                   <servlet-name>mm</servlet-name> <!--声明一个变量存储servlet接口实现类类路径-->
                   <servlet-class>com.bjpowernode.controller.OneServlet</servlet-class>
                   <load-on-startup>30<load-on-startup><!--填写一个大于0的整数即可,随便写-->
              </servlet>
                       
  • 在Http服务器运行期间,一个Servlet接口实现类只能被创建出一个实例对象         
  • 在Http服务器关闭时刻,自动将网站中所有的Servlet对象进行销毁

其方法:Init、 service、 destroy、 无参构造函数 

14.ServletConfig

15.初试HttpServlet:

package com.southwind.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//这里就是注解
@WebServlet("/test")
public class TestServlet extends HttpServlet {
    //这里是重写doGet 和 doPost
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Get");

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Post");
    }
}
  • response.getWriter()返回的是PrintWriter,这是一个打印输出流。(这个是响应流,与IO流不同,如果不手动关闭响应流,tomcat服务器会自动帮忙关闭)
  • response.getWriter().print(),不仅可以打印输出文本格式的(包括html标签),还可以将一个对象以默认的编码方式转换为二进制字节输出
  • response.getWriter().writer(),只能打印输出文本格式的(包括html标签),不可以打印对象。

16 HttpServletResponse接口

Http响应协议包:

  • 介绍:
    • HttpServletResponse接口来自于Servlet规范中,在Tomcat中存在servlet-api.jar
    • HttpServletResponse接口实现类由Http服务器负责提供
    • HttpServletResponse接口负责将doGet/doPost方法执行结果写入到【响应体】交给浏览器(这是从服务器回传给浏览器的过程)
    • 开发人员习惯于将HttpServletResponse接口修饰的对象称为【响应对象】
  • 主要功能:
    •  将执行结果以二进制形式写入到【响应体】
    • 设置响应头中[content-type]属性值,从而控制浏览器使用对应编译器将响应体二进制数据编译为【文字,图片,视频,命令】
    •  设置响应头中【location】属性,将一个请求地址赋值给location.从而控制浏览器向指定服务器发送请求
public class OneServlet extends HttpServlet {
 
   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         String result ="Hello World"; //执行结果

         //--------响应对象将结果写入到响应体--------------start

            //1.通过响应对象,向Tomcat索要输出流
            PrintWriter out = response.getWriter();
            //2.通过输出流,将执行结果以二进制形式写入到响应体(这个输出流并不用关闭,因为这个输出流不是我们创建的输出流,这是输出流是我们借的输出流)
            out.write(result);

        //--------响应对象将结果写入到响应体--------------start
    }//doGet执行完毕
     //Tomcat将响应包推送给浏览器
}

在写入到响应体以后,通过浏览器访问该Servlet映射,就可以得到响应体中的内容,所以该代码结果是在浏览器上显示Hello World。

问题:1:以下代码中浏览器接收到数据是2 ,不是50

public class TwoServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

          int money = 50; //执行结果

          PrintWriter out = response.getWriter();
          //out.write(97);
          out.print(money);

    }
}

问题原因:
out.writer方法可以将【字符】,【字符串】,【ASCII码】写入到响应体
       【ASCII码】:  a -------------- 97
                                2--------------- 50
问题解决方案:  实际开发过程中,都是通过out.print()将真实数据写入到响应体

问题2: Java<br/>Mysql<br/>HTML<br/>
             浏览器在接收到响应结果时,将<br/>作为
             文字内容在窗口展示出来,没有将<br/>
             当做HTML标签命令来执行(未设置响应头前)

public class ThreeServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String result="Java<br/>Mysql<br/>HTML<br/>"; //既有文字信息又有HTML标签命令
        String result2="红烧排骨<br/>梅菜肘子<br/>糖醋里脊";

        //设置响应头content-type
        response.setContentType("text/html;charset=utf-8");
        //向Tomcat索要输出流
        PrintWriter out = response.getWriter();
        //通过输出流将结果写入到响应体
        out.print(result);
        out.print(result2);
    }
}

*  问题原因:
*           浏览器在接收到响应包之后,根据【响应头中content-type】
*           属性的值,来采用对应【编译器】对【响应体中二进制内容】进行编译处理
*
*           在默认的情况下,content-type属性的值“text” content-type="text"
*           此时浏览器将会采用【文本编译器】对响应体二进制数据进行解析
*
*  解决方案:
*           一定要在得到输出流之前,通过响应对象对响应头中content-type属性进行
*           一次重新赋值用于指定浏览器采用正确编译器

浏览器中查看响应头如下:

public class FourServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

              String result ="http://www.baidu.com?userName=mike";

              //通过响应对象,将地址赋值给响应头中location属性
              response.sendRedirect(result);//[响应头  location="http://www.baidu.com"]
    }

}

    *  浏览器在接收到响应包之后,如果
    *  发现响应头中存在location属性
    *  自动通过地址栏向location指定网站发送请求
    *  sendRedirect方法远程控制浏览器请求行为【请求地址,请求方式,请求参数】

17 HttpServletRequest接口

Http请求协议包

  • 介绍:
    • HttpServletRequest接口来自于Servlet规范中,在Tomcat中存在servlet-api.jar
    • HttpServletRequest接口实现类由Http服务器负责提供
    • HttpServletRequest接口负责在doGet/doPost方法运行时读取Http请求协议包中信息
    • 开发人员习惯于将HttpServletRequest接口修饰的对象称为【请求对象】
  • 作用:
    • 可以读取Http请求协议包中【请求行】信息
    • 可以读取保存在Http请求协议包中【请求头】或则【请求体】中请求参数信息
    • 可以代替浏览器向Http服务器申请资源文件调用
public class OneServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1.通过请求对象,读取【请求行】中【url】信息
         String url = request.getRequestURL().toString();
        //2.通过请求对象,读取【请求行】中【method】信息
         String method = request.getMethod();

        //3.通过请求对象,读取【请求行】中uri信息
        /*
        * URI:资源文件精准定位地址,在请求行并没有URI这个属性。
        *      实际上URL中截取一个字符串,这个字符串格式"/网站名/资源文件名"
        *      URI用于让Http服务器对被访问的资源文件进行定位
        */
        String uri =  request.getRequestURI();// substring

        System.out.println("URL "+url);
        System.out.println("method "+method);
        System.out.println("URI "+uri);

    }
}

输出:

public class TwoServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1.通过请求对象获得【请求头】中【所有请求参数名】
        Enumeration paramNames =request.getParameterNames(); //将所有请求参数名称保存到一个枚举对象进行返回
        while(paramNames.hasMoreElements()){
              String paramName = (String)paramNames.nextElement();
              //2.通过请求对象读取指定的请求参数的值
              String value = request.getParameter(paramName);
              System.out.println("请求参数名 "+paramName+" 请求参数值 "+value);
        }
    }
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
      <center>
          <a href="/myWeb/two?userName=mike&password=123">通过超链接访问TwoServlet别携带请求参数</a>

      </center>
</body>
</html>

/myWeb/two 这个就是URI

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
        <center>
            <form action="/myWeb/three" method="get">
                请求参数:<input type="text" name="userName"/><br/>
                <input type="submit" value="get方式访问ThreeServlet">
            </form>

            <form action="/myWeb/three" method="Post">
                请求参数:<input type="text" name="userName"/><br/>
                <input type="submit" value="post方式访问ThreeServlet">
            </form>

        </center>
</body>
</html>

 

public class ThreeServlet extends HttpServlet {

   /*
   *  问题:
   *      以GET方式发送中文参数内容“老杨是正经人”时,得到正常结果
   *      以POST方式发送中文参数内容"老崔是个男人",得到【乱码】“è?????????????·???”
   *
   *  原因:
   *
   *      浏览器以GET方式发送请求,请求参数保存在【请求头】,在Http请求协议包到达Http服务器之后,第一件事就是进行解码
   *      请求头二进制内容由Tomcat负责解码,Tomcat9.0默认使用【utf-8】字符集,可以解释一切国家文字
   *
   *      浏览器以POST方式发送请求,请求参数保存在【请求体】,在Http请求协议包到达Http服务器之后,第一件事就是进行解码
   *      请求体二进制内容由当前请求对象(request)负责解码。request默认使用[ISO-8859-1]字符集,一个东欧语系字符集
   *      此时如果请求体参数内容是中文,将无法解码只能得到乱码
   *
   *  解决方案:
   *
   *      在Post请求方式下,在读取请求体内容之前,应该通知请求对象使用utf-8字符集对请求体内容进行一次重新解码
   *
   *
   */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //通知请求对象,使用utf-8字符集对请求体二进制内容进行一次重写解码
        request.setCharacterEncoding("utf-8");
        //通过请求对象,读取【请求体】参数信息
        String value = request.getParameter("userName");
        System.out.println("从请求体得到参数值 "+value);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         //通过请求对象,读取【请求头】参数信息
         String userName = request.getParameter("userName");
         System.out.println("从请求头得到参数值 "+userName);
    }
}

18 Http状态码

            1.介绍:
                1)由三位数字组成的一个符号。
                2)Http服务器在推送响应包之前,根据本次请求处理情况
                  将Http状态码写入到响应包中【状态行】上

                3)如果Http服务器针对本次请求,返回了对应的资源文件。
                       通过Http状态码通知浏览器应该如何处理这个结果

              如果Http服务器针对本次请求,无法返回对应的资源文件
              通过Http状态码向浏览器解释不能提供服务的原因

            2.分类:
                1)组成  100---599;分为5个大类
                2)1XX :
                     最有特征 100; 通知浏览器本次返回的资源文件
                     并不是一个独立的资源文件,需要浏览器在接收
                     响应包之后,继续向Http服务器所要依赖的其他资源文件

                    3) 2XX:
                     最有特征200,通知浏览器本次返回的资源文件是一个
                     完整独立资源文件,浏览器在接收到之后不需要所要
                     其他关联文件

 

                    4)3xx:
                     最有特征302,通知浏览器本次返回的不是一个资源文件内容
                 而是一个资源文件地址,需要浏览器根据这个地址自动发起
                 请求来索要这个资源文件

                 response.sendRedirect("资源文件地址")写入到响应头中location
                 而这个行为导致Tomcat将302状态码写入到状态行

public class OneServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          String address = "http://www.baidu.com";
          response.sendRedirect(address); //写入到响应头 location
    }
     // Tomcat在推送响应包之前,看到响应体是空,但是响应头location却存放了一个地址。
    //此时Tomcat将302状态码写入到状态行
    //在浏览器接收到响应包之后,因为302状态码,浏览器不会读取响应体内容,自动根据响应头
    //中location的地址发起第二次请求
}

                 

                  5)4XX:

                             404: 通知浏览器,由于在服务端没有定位到被访问的资源文件因此无法提供帮助

                             405:通知浏览器,在服务端已经定位到被访问的资源文件(Servlet)
                                      但是这个Servlet对于浏览器采用的请求方式不能处理

//只能针对浏览器发送POST请求方式进行处理,无法对GET方式进行处理
public class OneServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("OneServlet  doPost method is run....");
    }

}

                    6)5xx:

                         500:通知浏览器,在服务端已经定位到被访问的资源文件(Servlet)
                                 这个Servlet可以接收浏览器采用请求方式,但是Servlet在处理请求期间,由于Java异常导致处理失败

public class OneServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         Map map = new HashMap();
         //int num =(int)map.get("key1");//抛出NullPointerException  状态码500
         Integer num =(Integer)map.get("key1");
         System.out.println("heheh.......");
        //int a = null; // null值不可能赋值给int
         Integer b = null; //所有高级引用类型都可以赋值给null
    }
}

19 多个Servlet之间调用规则:

1.前提条件:
                      某些来自于浏览器发送请求,往往需要服务端中多个Servlet协同处理。
              但是浏览器一次只能访问一个Servlet,导致用户需要手动通过浏览器
              发起多次请求才能得到服务。
              这样增加用户获得服务难度,导致用户放弃访问当前网站【98k,AKM】

2.提高用户使用感受规则:

      无论本次请求涉及到多少个Servlet,用户只需要【手动】通知浏览器发起一次请求即可

3.多个Servlet之间调用规则:

1)重定向解决方案

             1.工作原理: 用户第一次通过【手动方式】通知浏览器访问OneServlet。
                      OneServlet工作完毕后,将TwoServlet地址写入到响应头
                      location属性中,导致Tomcat将302状态码写入到状态行。

              在浏览器接收到响应包之后,会读取到302状态。此时浏览器
              自动根据响应头中location属性地址发起第二次请求,访问
              TwoServlet去完成请求中剩余任务。

             2.实现命令:

                      response.sendRedirect("请求地址")
              将地址写入到响应包中响应头中location属性

public class OneServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("OneServlet 负责 洗韭菜");

        //重定向解决方案:
        response.sendRedirect("/myWeb/two");// [地址格式: /网站名/资源文件名]
        //response.sendRedirect("http://www.baidu.com");
    }
}

             3.特征:
         
                      1)请求地址:
                 既可以把当前网站内部的资源文件地址发送给浏览器 (/网站名/资源文件名)
                 也可以把其他网站资源文件地址发送给浏览器(http://ip地址:端口号/网站名/资源文件名)

                          2)请求次数

                 浏览器至少发送两次请求,但是只有第一次请求是用户手动发送。
                 后续请求都是浏览器自动发送的。
 
                          3) 请求方式:
                 重定向解决方案中,通过地址栏通知浏览器发起下一次请求,因此
                 通过重定向解决方案调用的资源文件接收的请求方式一定是【GET】

             4.缺点:
                     重定向解决方案需要在浏览器与服务器之间进行多次往返,大量时间
             消耗在往返次数上,增加用户等待服务时间f

2)请求转发解决方案

             1.原理:   用户第一次通过手动方式要求浏览器访问OneServlet。
                             OneServlet工作完毕后,通过当前的请求对象代替浏览器
                            向Tomcat发送请求,申请调用TwoServlet。
                            Tomcat在接收到这个请求之后,自动调用TwoServlet来完成剩余任务

             2.实现命令: 请求对象代替浏览器向Tomcat发送请求

                                 //1.通过当前请求对象生成资源文件申请报告对象
               RequestDispatcher  report = request.getRequestDispatcher("/资源文件名");一定要以"/"为开头
                                 //2.将报告对象发送给Tomcat
               report.forward(当前请求对象,当前响应对象)

public class OneServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("OneServlet 实施麻醉。。。。。");
        //请求转发方案:
        //1.通过当前请求对象生成资源文件申请报告对象
        RequestDispatcher report = request.getRequestDispatcher("/two");
        //2.将报告对象发送给Tomcat
        report.forward(request, response);
        //也可以合在一起
        //request.getRequestDispatcher("/two").forward(request, response);
    }
}

            3.优点:
          
                     1)无论本次请求涉及到多少个Servlet,用户只需要手动通过浏览器发送一次请求

             2) Servlet之间调用发生在服务端计算机上,节省服务端与浏览器之间往返次数
                增加处理服务速度

             4.特征:

                    1)请求次数

                         在请求转发过程中,浏览器只发送一次请求

                        2)请求地址
                        只能向Tomcat服务器申请调用当前网站下(当前http服务器)资源文件地址
                        request.getRequestDispathcer("/资源文件名") ****不要写网站名****

                        3)请求方式

                        在请求转发过程中,浏览器只发送一个了个Http请求协议包。
                        参与本次请求的所有Servlet共享同一个请求协议包,因此
                        这些Servlet接收的请求方式与浏览器发送的请求方式保持一致

20 多个Servlet之间数据共享实现方案:

1.数据共享:OneServlet工作完毕后,将产生数据交给TwoServlet来使用

2.Servlet规范中提供四种数据共享方案

  • ServletContext接口

                  1.介绍:
                              1)来自于Servlet规范中一个接口。在Tomcat中存在servlet-api.jar
                 在Tomcat中负责提供这个接口实现类

                             2)如果两个Servlet来自于同一个网站。彼此之间通过网站的ServletContext
                 实例对象实现数据共享

                              3)开发人员习惯于将ServletContext对象称为【全局作用域对象】


                  2.工作原理:

                  每一个网站都存在一个全局作用域对象。
              这个全局作用域对象【相当于】一个Map。
              在这个网站中OneServlet可以将一个数据
              存入到全局作用域对象,当前网站中其他
              Servlet此时都可以从全局作用域对象得到
              这个数据进行使用。

                  3.全局作用域对象生命周期:

                 1)在Http服务器启动过程中,自动为当前网站在内存中创建一个全局作用域对象
                2)在Http服务器运行期间时,一个网站只有一个全局作用域对象

               3)在Http服务器运行期间,全局作用域对象一直处于存活状态

               4)在Http服务器准备关闭时,负责将当前网站中全局作用域对象进行销毁处理
                           *****全局作用域对象生命周期贯穿网站整个运行期间***

                  4.命令实现: 【同一个网站】OneServlet将数据共享给TwoServlet

                 OneServlet{
                 
                    public void doGet(HttpServletRequest request,HttpServletResponse response){
                     
                           //1.通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
                           ServletContext application = request.getServletContext();  //全局作用域对象一般都叫application
                           //2.将数据添加到全局作用域对象作为【共享数据】
                           application.setAttribute("key1",数据);
                    
                    }
                 
                 }

                 TwoServlet{
                 
                   public void doGet(HttpServletRequest request,HttpServletResponse response){
                   
                            //1.通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
                            ServletContext application = request.getServletContext();
                            //2.从全局作用域对象得到指定关键字对应数据
                            Object 数据 =  application.getAttribute("key1");  //从map里面得到的类型Object,之后可以做强转
                   
                   }
                 
                 }
  • Cookie类

1.介绍:

          1)Cookie来自于Servlet规范中一个工具类,存在于Tomcat提供servlet-api.jar中

           2)如果两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,此时
             借助于Cookie对象进行数据共享

          3) Cookie存放当前用户的私人数据,在共享数据过程中提高服务质量

           4) 在现实生活场景中,Cookie相当于用户在服务端得到【会员卡】

2.原理:
                用户通过浏览器第一次向MyWeb网站发送请求申请OneServlet。
            OneServlet在运行期间创建一个Cookie存储与当前用户相关数据
            OneServlet工作完毕后,【将Cookie写入到响应头】交还给当前浏览器。
            浏览器收到响应响应包之后,将cookie存储在浏览器的缓存
            一段时间之后,用户通过【同一个浏览器】再次向【myWeb网站】发送请求申请TwoServlet时,
            【浏览器需要无条件的将myWeb网站之前推送过来的Cookie,写入到请求头】发送过去
            此时TwoServlet在运行时,就可以通过读取请求头中cookie中信息,得到OneServlet提供的共享数据

           3.实现命令:  同一个网站 OneServlet 与  TwoServlet 借助于Cookie实现数据共享

OneServlet{
                public void doGet(HttpServletRequest request,HttpServletResponse resp){
                
                    //1.创建一个cookie对象,保存共享数据(当前用户数据)
                    Cookie card = new Cookie("key1","abc");
                    Cookie card1= new Cookie("key2","efg");
                    ****cookie相当于一个map
                    ****一个cookie中只能存放一个键值对
                    ****这个键值对的key与value只能是String
                    ****键值对中key不能是中文
                    //2.【发卡】将cookie写入到响应头,交给浏览器
                    resp.addCookie(card);
                    resp.addCookie(card1)
                }
            
            }

            浏览器/用户      <---------响应包 【200】
                                             【cookie: key1=abc; key2=eft】
                                             【】
                                             【处理结果】

                        浏览器向myWeb网站发送请求访问TwoServlet---->请求包 【url:/myWeb/two method:get】
                                                                         【
                                                                           请求参数:xxxx
                                                                           Cookie   key1=abc;key2=efg
                                                                         】
                                                                         【】
                                                                         【】
                         TwoServlet{
             
                public void doGet(HttpServletRequest request,HttpServletResponse resp){
                
                             //1.调用请求对象从请求头得到浏览器返回的Cookie
                             Cookie  cookieArray[] = request.getCookies();
                            //2.循环遍历数据得到每一个cookie的key 与 value
                             for(Cookie card:cookieArray){
                                  String key =   card.getName(); //读取key  "key1"
                                  Strign value = card.getValue();//读取value "abc"
                             
                              }
                }
             }
              
            

创建Cookie

Cookie cookie = new Cookie("name","tom"); 
response.addCookie(cookie);

读取 Cookie

Cookie[] cookies = request.getCookies(); 
for (Cookie cookie:cookies){    
    out.write(cookie.getName()+":"+cookie.getValue()+"<br/>"); 
}

4.Cookie销毁时机:

                    1.在默认情况下,Cookie对象存放在浏览器的缓存中。
              因此只要浏览器关闭,Cookie对象就被销毁掉

                        2.在手动设置情况下,可以要求浏览器将接收的Cookie
              存放在客户端计算机上硬盘上,同时需要指定Cookie
              在硬盘上存活时间。在存活时间范围内,关闭浏览器
              关闭客户端计算机,关闭服务器,都不会导致Cookie
              被销毁。在存活时间到达时,Cookie自动从硬盘上被
              删除

                           cookie.setMaxAge(60); //cookie在硬盘上存活1分钟

Cook应用实例(订餐会员卡)

public class OneServlet extends HttpServlet {


    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String userName,money;
        //1.调用请求对象读取【请求头】参数信息
        userName = request.getParameter("userName");
        money    = request.getParameter("money");
        //2.开卡
        Cookie card1 = new Cookie("userName", userName);
        Cookie card2 = new Cookie("money",money);
        //3.发卡,将Cookie写入到响应头交给浏览器
        response.addCookie(card1);
        response.addCookie(card2);
        //4.通知Tomcat将【点餐页面】内容写入到响应体交给浏览器(请求转发)
        request.getRequestDispatcher("/index_2.html").forward(request, response);

    }
}
package com.dong.controller;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "TwoServlet")
public class TwoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int jiaozi_money = 30;
        int gaifan_money = 15;
        int miantiao_money = 20;
        int money = 0, xiaofei = 0, balance = 0;
        String food, userName = null;
        Cookie cookieArray[] = null;
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        Cookie newCard = null;
        //1.读取请求头参数信息,得到用户点餐食物类型
        food = request.getParameter("food");
        //2.读取请求中Cookie
        cookieArray = request.getCookies();
        //3.刷卡消费
        for (Cookie card : cookieArray) {
            String key = card.getName();
            String value = card.getValue();
            if ("userName".equals(key)) {
                userName = value;
            } else if ("money".equals(key)) {
                money = Integer.valueOf(value);
                if ("饺子".equals(food)) {
                    if (jiaozi_money > money) {
                        out.print("用户 " + userName + " 余额不足,请充值");
                    } else {
                        newCard = new Cookie("money", (money - jiaozi_money) + "");
                        xiaofei = jiaozi_money;
                        balance = money - jiaozi_money;
                    }
                } else if ("面条".equals(food)) {
                    if (miantiao_money > money) {
                        out.print("用户 " + userName + " 余额不足,请充值");
                    } else {
                        newCard = new Cookie("money", (money - miantiao_money) + "");
                        xiaofei = miantiao_money;
                        balance = money - miantiao_money;
                    }
                } else if ("盖饭".equals(food)) {
                    if (gaifan_money > money) {
                        out.print("用户 " + userName + " 余额不足,请充值");
                    } else {
                        newCard = new Cookie("money", (money - gaifan_money) + "");// 10+"abc"="10abc"
                        xiaofei = gaifan_money;
                        balance = money - gaifan_money;
                    }
                }

            }


        }
        //4.将用户会员卡返还给用户
        response.addCookie(newCard);
        //5.将消费记录写入到响应
        out.print("用户 " + userName + "本次消费 " + xiaofei + " 余额 :" + balance);
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
        <center>
            <font style="color:red;font-size:40px">新会员申请开卡</font>
            <form action="/myWeb/user/one">
                <table border="2">
                    <tr>
                        <td>用户名</td>
                        <td><input type="text" name="userName"/></td>
                    </tr>
                    <tr>
                        <td>预存金额</td>
                        <td><input type="text" name="money"/></td>
                    </tr>
                    <tr>
                        <td><input type="submit" value="申请开卡"/></td>
                        <td><input type="reset" /></td>
                    </tr>
                </table>
            </form>

        </center>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
           <center>
               <font style="color:red;font-size:40px">点餐页面</font>
               <form action="/myWeb/user/two">
                   食物类型:<input type="radio" name="food" value="饺子"/>饺子(30元)
                   <input type="radio" name="food" value="面条"/>面条(20元)
                   <input type="radio" name="food" value="盖饭"/>盖饭(15元)<br/>
                   <input type="submit" value="划卡消费">
               </form>

           </center>
</body>
</html>
  • HttpSession接口

其中 invalidate() 常用来作为退出操作

1.介绍:
                 1)HttpSession接口来自于Servlet规范下一个接口。存在于Tomcat中servlet-api.jar
                        其实现类由Http服务器提供。Tomcat提供实现类存在于servlet-api.jar

                  2)如果两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,此时
                        借助于HttpSession对象进行数据共享

                  3)开发人员习惯于将HttpSession接口修饰对象称为【会话作用域对象】

2.HttpSession 与  Cookie 区别:【面试题】

                  1)存储位置:  一个在天上,一个在地下

                            Cookie:存放在客户端计算机(浏览器内存/硬盘)
                            HttpSession:存放在服务端计算机内存

                      2)数据类型:
                             Cookie对象存储共享数据类型只能是String
                             HttpSession对象可以存储任意类型的共享数据Object

                      3) 数据数量:
                             一个Cookie对象只能存储一个共享数据
                             HttpSession使用map集合存储共享数据,所以可以存储任意数量共享数据

                      4)参照物:
                             Cookie相当于客户在服务端【会员卡】

                            HttpSession相当于客户在服务端【私人保险柜】


               3.命令实现:   同一个网站(myWeb)下OneServlet将数据传递给TwoServlet

                 OneServlet{
                  
                             public void doGet(HttpServletRequest request,HttpServletResponse response){
                 
                                        //1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
                                         HttpSession   session = request.getSession();
                                        //2.将数据添加到用户私人储物柜
                                        session.setAttribute("key1",共享数据);
                             }
                  }

                  浏览器访问/myWeb中TwoServlet

                  TwoServlet{
                  
                    public void doGet(HttpServletRequest request,HttpServletResponse response){
                       //1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
                        HttpSession   session = request.getSession();
                        //2.从会话作用域对象得到OneServlet提供的共享数据
                        Object 共享数据 = session.getAttribute("key1");
                    }
                  
                  }
                 

4.Http服务器如何将用户与HttpSession关联起来

通过Cookie

Servlet中Session的id属性:

浏览器中接收到的响应包:

再次请求时,浏览器中的请求头中将携带有该Cookie值
5.getSession()  与  getSession(false)

                 1)getSession(): 如果当前用户在服务端已经拥有了自己的私人储物柜.
                              要求tomcat将这个私人储物柜进行返回
                              如果当前用户在服务端尚未拥有自己的私人储物柜
                              要求tocmat为当前用户创建一个全新的私人储物柜

                    2)getSession(false):如果当前用户在服务端已经拥有了自己的私人储物柜.
                                 要求tomcat将这个私人储物柜进行返回
                                 如果当前用户在服务端尚未拥有自己的私人储物柜
                                 此时Tomcat将返回null

如果用户身份已经合法,则用getSession(),如果用户身份没有验证,则用getSession(false

6.HttpSession销毁时机:

              1.用户与HttpSession关联时使用的Cookie只能存放在浏览器缓存中.
              2.在浏览器关闭时,意味着用户与他的HttpSession关系被切断
              3.由于Tomcat无法检测浏览器何时关闭,因此在浏览器关闭时并不会
                导致Tomcat将浏览器关联的HttpSession进行销毁
              4.为了解决这个问题,Tomcat为每一个HttpSession对象设置【空闲时间】
                这个空闲时间默认30分钟,如果当前HttpSession对象空闲时间达到30分钟
                此时Tomcat认为用户已经放弃了自己的HttpSession,此时Tomcat就会销毁
                掉这个HttpSession

7.HttpSession空闲时间手动设置

            在当前网站/web/WEB-INF/web.xml

                <session-config>
                       <session-timeout>5</session-timeout> <!--当前网站中每一个session最大空闲时间5分钟-->
                </session-config>

Session实例:在网页中点击超链接,之后将数据存储到HttpSession中,然后将数据读出来(模拟购物车)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
          <table border="2" align="center">
              <tr>
                  <td>商品名称</td>
                  <td>商品单价</td>
                  <td>供货商</td>
                  <td>放入购物车</td>
              </tr>

              <tr>
                  <td>华为笔记本电脑pro13</td>
                  <td>7000</td>
                  <td>华为</td>
                  <td><a href="/HttpSession/one?goodsName=华为笔记本电脑pro13">放入购物车</a></td>
              </tr>
              <tr>
                  <td>榴莲</td>
                  <td>300</td>
                  <td>泰国</td>
                  <td><a href="/HttpSession/one?goodsName=榴莲">放入购物车</a></td>
              </tr>
              <tr>
                  <td>男士内裤</td>
                  <td>1000</td>
                  <td>老崔</td>
                  <td><a href="/HttpSession/one?goodsName=男士内裤">放入购物车</a></td>
              </tr>
              <tr align="center">
                  <td colspan="4">
                      <a href="/HttpSession/two">查看我的购物车</a>
                  </td>
              </tr>
          </table>
</body>
</html>
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

public class OneServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String goodsName;
        //1.调用请求对象,读取请求头参数,得到用户选择商品名
        goodsName = request.getParameter("goodsName");
        //2.调用请求对象,向Tomcat索要当前用户在服务端的私人储物柜
        HttpSession session = request.getSession();
        //session.setMaxInactiveInterval(5);
        //3.将用户选购商品添加到当前用户私人储物柜
        Integer  goodsNum = (Integer)session.getAttribute(goodsName);
        if(goodsNum == null){
            session.setAttribute(goodsName, 1);
        }else{
            session.setAttribute(goodsName, goodsNum+1);
        }
    }
}
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Enumeration;

public class TwoServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1.调用请求对象,向Tomcat索要当前用户在服务端私人储物柜
        HttpSession session = request.getSession();

        //2.将session中所有的key读取出来,存放一个枚举对象
        Enumeration goodsNames =session.getAttributeNames();
        while(goodsNames.hasMoreElements()){
            String goodsName =(String) goodsNames.nextElement();
            int goodsNum = (int)session.getAttribute(goodsName);
            System.out.println("商品名称 "+goodsName+" 商品数量 "+goodsNum);
        }
    }
}

Session与Cookie实例

使用Session重写页面登录过程:

因为request只是一次请求,当我登录以后,在没有关闭浏览器的情况下,新开一个窗口就没有作用了(request的作用域比session少很多),所以我们可以使用session,这样可以让生命周期更长一些。

同时刚刚check中的代码应该写在servlet中,因为它全是java代码。

login.jsp中没有太多的变化:

//这里有一个地址的问题,我使用了相对地址和绝对地址好像都不是很理想,然后我才知道,它这个的选择,就完全看我地址栏怎么输入
//我地址栏输入/my可以进入servlet界面,那我这里跳转只需要写/my就可以了
<form action="/my" method="post">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="password"name="password"><br/>
    <input type="submit"value="登录"/>
<form/>

MyServlet中:

//这个要与上面的对应起来
@WebServlet("/my")
public class MyServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进来了");
        String username=req.getParameter("username");
        //首先要从req中获得session
        HttpSession session = req.getSession();
        session.setAttribute("username",username);
        //将username写入了session中,由于session也是从req中拿出来的,所转发也只是转发req
        req.getRequestDispatcher("/welcome.jsp").forward(req,resp);
    }
}

welcome.jsp中

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>Welcome</h1>
<%
    //要用session来接收
    String name =(String)session.getAttribute("username");
%>
<%=name%><a href="/logout">退出登录</a>
</body>
</html>

loginout.java:

@WebServlet("/logout")
public class loginout extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //这里在登陆了以后释放了登录信息        
        HttpSession session = req.getSession();
        session.invalidate();
        resp.sendRedirect("/login.jsp");
    }
}

使用Cookie:

Cookielogin.jsp中没有什么更改的

Cookielogin.java

@WebServlet("/Cookie")
public class CookieLogin extends HttpServlet {
    String username="login";
    String password="123";
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("username");
        String pw = req.getParameter("password");
        if(name.equals(username)&&pw.equals(password)){
            //创建一个新的Cookie对象
            Cookie cookie = new Cookie("username",name);
            //把它装到相应里面可以发给客户端
            //注意Cookie是写到response里面的
            resp.addCookie(cookie);
            resp.sendRedirect("/welcome.jsp");
        }
        else{
            resp.sendRedirect("/Cookielogin.jsp");
        }
    }
}

welcome.jsp:

<%
    String name;
    //String name =(String)session.getAttribute("username");
    //读取cookies,浏览器因为本来就含有一些Cookie所以我们要把它跟我自己要的那个区分开
    Cookie[] cookies = request.getCookies();
    for(Cookie cookie:cookies){
        if (cookie.getName().equals("username")){
            name=cookie.getValue();
            out.write(name);
        }
    }

%>
<a href="/logout">退出登录</a>

loginout.java

@WebServlet("/logout")
public class loginout extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //接收cookies
        Cookie[] cookies = req.getCookies();
        for(Cookie cookie:cookies){
            if(cookie.getName().equals("username")){
                //因为是退出,所以要把它的生命周期设为0
                cookie.setMaxAge(0);
                //别忘了不管做了什么操作,这里都要加到resp里面相应回去
                resp.addCookie(cookie);
                resp.sendRedirect("/Cookielogin.jsp");
            }
        }
    }
}
  • HttpServletRequest接口

 1.介绍:

                 1) 在同一个网站中,如果两个Servlet之间通过【请求转发】方式进行调用,
                         彼此之间共享同一个请求协议包。而一个请求协议包只对应一个请求对象
                         因此servlet之间共享同一个请求对象,此时可以利用这个请求对象在两个
                         Servlet之间实现数据共享

                  2) 在请求对象实现Servlet之间数据共享功能时,开发人员将请求对象称为【请求作用域对象】


2.命令实现: OneServlet通过请求转发申请调用TwoServlet时,需要给TwoServlet提供共享数据

                 OneServlet{
                 
                   public void doGet(HttpServletRequest req,HttpServletResponse response){
                   
                          //1.将数据添加到【请求作用域对象】中attribute属性
                          req.setAttribute("key1",数据); //数据类型可以任意类型Object
                          //2.向Tomcat申请调用TwoServlet
                          req.getRequestDispatcher("/two").forward(req,response);
                   }
                 
                 }
                 TwoServlet{
                    public void doGet(HttpServletRequest req,HttpServletResponse response){
                                            
                        //从当前请求对象得到OneServlet写入到共享数据
                        Object 数据 = req.getAttribute("key1");
                    }
                 
                 
                 }

实例:

public class OneServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         //1.将数据添加到请求作用域对象,作为共享数据
          request.setAttribute("key1", "hello World");
         //2.代替浏览器,向Tomcat索要TwoServlet来完成剩余任务
          request.getRequestDispatcher("/two").forward(request, response);
    }
}
public class TwoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         // 从同一个请求作用域对象得到OneServlet写入到共享数据
         String value =(String)request.getAttribute("key1");
         System.out.println("TwoServlet得到共享数据 "+value);
    }
}

21 Servlet规范扩展-----------------监听器接口


       1.介绍:

              1)一组来自于Servlet规范下接口,共有8个接口。在Tomcat存在servlet-api.jar包

              2)监听器接口需要由开发人员亲自实现,Http服务器提供jar包并没有对应的实现类

              3)监听器接口用于监控【作用域对象生命周期变化时刻】以及【作用域对象共享数据变化时刻】

        2.作用域对象:

           1)在Servlet规范中,认为在服务端内存中可以在某些条件下为两个Servlet之间提供
              数据共享方案的对象,被称为【作用域对象】

               2)Servlet规范下作用域对象:(Cookie不是,因为其是在客户端的)
                       ServletContext:   全局作用域对象
                       HttpSession   :    会话作用域对象
                       HttpServletRequest:请求作用域对象

        3.监听器接口实现类开发规范:三步

            1)根据监听的实际情况,选择对应监听器接口进行实现

            2)重写监听器接口声明【监听事件处理方法】

            3)在web.xml文件将监听器接口实现类注册到Http服务器

        4.ServletContextListener接口:

            1)作用:通过这个接口合法的检测全局作用域对象被初始化时刻以及被销毁时刻

            2)监听事件处理方法:

                    public void contextInitlized() :在全局作用域对象被Http服务器初始化被调用

                    public void contextDestory():      在全局作用域对象被Http服务器销毁时候触发调用

<?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">
    
         <!--将监听器接口实现类注册到Tomcat-->
         <listener>
             <listener-class>com.bjpoewrnode.listener.OneListener</listener-class>
         </listener>
</web-app>     
package com.dongdong.Listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class OneListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("生成了");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("销毁了");
    }
}

5.ServletContextAttributeListener接口:

                 1)作用:通过这个接口合法的检测全局作用域对象共享数据变化时刻

                 2)监听事件处理方法:

                         public void contextAdd():在全局作用域对象添加共享数据
                         public void contextReplaced():在全局作用域对象更新共享数据
                         public void contextRemove():在全局作用域对象删除共享数据

6.全局作用域对象共享数据变化时刻

               ServletContext application = request.getServletContext();

                 application.setAttribute("key1",100); //新增共享数据

                 application.setAttribute("key1",200); //更新共享数据
                 
                 application.removeAttribute("key1");  //删除共享数据

package com.bjpowernode.listener;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

/**
 * 2020/4/29
 */
public class OneListener implements ServletContextAttributeListener {

    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        System.out.println("新增共享数据");
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        System.out.println("删除共享数据");
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        System.out.println("更新共享数据");
    }
}

 

public class OneServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        ServletContext application = request.getServletContext();

        application.setAttribute("key1",100); //新增共享数据

        application.setAttribute("key1",200); //更新共享数据

        application.removeAttribute("key1");  //删除共享数据
    }
}

 

<?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">
      <!--注册监听器接口实现类-->
      <listener>
          <listener-class>com.bjpowernode.listener.OneListener</listener-class>
      </listener>
    <servlet>
        <servlet-name>OneServlet</servlet-name>
        <servlet-class>com.bjpowernode.controller.OneServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>OneServlet</servlet-name>
        <url-pattern>/one</url-pattern>
    </servlet-mapping>
</web-app>

监听器的应用实例:

这一段代码是写在exam项目里的,也就是《JavaWeb练习项目

//用户注册
//JDBC规范中,Connection创建与销毁最浪费时间
package com.dong.Listener;

import com.dong.util.JdbcUtil;

import javax.security.sasl.SaslClient;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class OneListener implements ServletContextListener {
    //在Tomcat启动时,预先创建20个Connection,在userDao.add方法执行时,
    //将事先创建好的connection交给add方法
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        JdbcUtil util = new JdbcUtil();
        Map map = new HashMap();
        for(int i = 1;i<20;i++){
            Connection con = util.getCon();
            System.out.println("在Http服务器启动时,创建Connection" + con);
            map.put(con,true);//key是con的地址,true表示这个通道处于空闲状态,false通道正在被使用
        }
        //为了在http服务器运行期间,一直可以使用20个Connection,将Connection保存到全局作用域对象
        ServletContext applicaion = sce.getServletContext();
        applicaion.setAttribute("key1",map);
    }
    //map被销毁
    //在http服务器关闭时刻,将全局作用域对象20个Connection销毁(服务器关闭了,这20个全局作用域对象自动被销毁了,不需要手动写)
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContext applicaion = sce.getServletContext();
        Map map = (Map) applicaion.getAttribute("key1");
        Iterator it = map.keySet().iterator();
        while(it.hasNext()){
            Connection con = (Connection) it.next();
            if(con!= null){
                System.out.println("我"+con+"被销毁了");
            }
        }
    }
}

22.Filter接口(过滤器接口)

       1.介绍:
           1)来自于Servlet规范下接口,在Tomcat中存在于servlet-api.jar包

           2)Filter接口实现类由开发人员负责提供,Http服务器不负责提供

           3)  Filter接口在Http服务器调用资源文件之前,对Http服务器进行拦截

       2.具体作用:

                1)拦截Http服务器,帮助Http服务器检测当前请求合法性

/**
 *  http://localhost:8080/myWeb/mm.jpg?age=89
 */
public class OneFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

           //1.通过拦截请求对象得到请求包参数信息,从而得到来访用户的真实年龄
           String age= servletRequest.getParameter("age");
           //2.根据年龄,帮助Http服务器判断本次请求合法性
           if(Integer.valueOf(age) < 70){ //合法请求
               //将拦截请求对象和响应对象交还给Tomcat,由Tomcat继续调用资源文件
               filterChain.doFilter(servletRequest, servletResponse);//放行
           }else{
               //过滤器代替Http服务器拒绝本次请求
               servletResponse.setContentType("text/html;charset=utf-8");
               PrintWriter out = servletResponse.getWriter();
               out.print("<center><font style='color:red;font-size:40px'>大爷,珍爱生命啊!</font></center>");
           }
    }
}

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">

          <!--将过滤器类文件路径交给Tomcat-->
          <filter>
              <filter-name>oneFilter</filter-name>
              <filter-class>com.bjpowernode.filter.OneFilter</filter-class>
          </filter>
          <!--通知Tomcat在调用何种资源文件时需要被当前过滤器拦截-->
          <filter-mapping>
              <filter-name>oneFilter</filter-name>
              <url-pattern>/mm.jpg</url-pattern>
          </filter-mapping>
</web-app>

                2)拦截Http服务器,对当前请求进行增强操作

实例:将所有通往Servlet的请求都拦下,然后设置utf-8格式

public class OneServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

          //直接从请求体读取请求参数
          String userName = request.getParameter("userName");
         System.out.println("OneServelt 从请求体得到参数 "+userName);
    }
}
public class TwoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //直接从请求体读取请求参数
        String userName = request.getParameter("userName");
        System.out.println("TwoServelt 从请求体得到参数 "+userName);
    }

}
public class OneFilter implements Filter {

     //通知拦截的请求对象,使用UTF-8字符集对当前请求体信息进行一次重新编辑
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");//增强
        filterChain.doFilter(servletRequest, servletResponse);
    }
}
<?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>OneServlet</servlet-name>
        <servlet-class>com.bjpowernode.controller.OneServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>TwoServlet</servlet-name>
        <servlet-class>com.bjpowernode.controller.TwoServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>OneServlet</servlet-name>
        <url-pattern>/one</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>TwoServlet</servlet-name>
        <url-pattern>/two</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>oneFilter</filter-name>
        <filter-class>com.bjpowernode.filter.OneFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>oneFilter</filter-name>
        <url-pattern>/*</url-pattern><!--通知tomcat在调用所有资源文件之前都需要调用OneFilter进行拦截-->
    </filter-mapping>
</web-app>

       3.Filter接口实现类开发步骤:三步

                 1)创建一个Java类实现Filter接口

                 2)重写Filter接口中doFilter方法                

                 3)web.xml将过滤器接口实现类注册到Http服务器

       4.Filter拦截地址格式

                 1) 命令格式:

                    <filter-mapping>
                    <filter-name>oneFilter</filter-name>
                    <url-pattern>拦截地址</url-pattern>
                     </filter-mapping>


                 2) 命令作用:
                       拦截地址通知Tomcat在调用何种资源文件之前需要调用OneFilter过滤进行拦截
                 3)要求Tomcat在调用某一个具体文件之前,来调用OneFilter拦截
                       <url-pattern>/img/mm.jpg</url-pattern>

                 4)要求Tomcat在调用某一个文件夹下所有的资源文件之前,来调用OneFilter拦截(此处是img文件夹)
                        <url-pattern>/img/*</url-pattern>

                 5)要求Tomcat在调用任意文件夹下某种类型文件之前,来调用OneFilter拦截
                        <url-pattern>*.jpg</url-pattern>

                 6)要求Tomcat在调用网站中任意文件时,来调用OneFilter拦截
                        <url-pattern>/*</url-pattern>

实例——过滤器防止用户恶意登录行为:

public class TwoFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        //以下代码会导致用于卡死在user_Add.html中
        //索要当前用户在服务端HttpSession(request.getSession(false)如果当前用户有Session,则返回的session)
        //如果当前用户没有session,则认为是非法用户,返回的是null

        //1.拦截后,通过请求对象向Tomcat索要当前用户的HttpSession

        //2.判断来访用户身份合法性


//        if(session == null){
//            request.getRequestDispatcher("/user_Add.html").forward(servletRequest,servletResponse);
//            return;
//        }
//        //3.放行
//        filterChain.doFilter(servletRequest,servletResponse);

        //1.调用请求对象读取请求包中的URI,了解用户的资源文件是谁
        String uri = request.getRequestURI();//[/网站名/资源文件名  eg:/myWeb/login.html or /myWeb/login or ...]

        //2.如果本次请求资源文件与登录相关[login.html 或 LoginServlet]此时应该无条件放行
        if(uri!=null&&uri.indexOf("login")!=-1 || "/myWeb/".equals(uri)){
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }
        //如果本次请求访问的是其他资源文件,需要得到用户在服务端HttpSession
        HttpSession session = request.getSession(false);
        if(session != null){
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }
        //拒绝请求
        request.getRequestDispatcher("/user_Add.html").forward(servletRequest,servletResponse);

    }

}
<?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>UserAddServlet</servlet-name>
        <servlet-class>com.dong.controller.UserAddServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UserAddServlet</servlet-name>
        <url-pattern>/user/add</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>UserFindServlet</servlet-name>
        <servlet-class>com.dong.controller.UserFindServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UserFindServlet</servlet-name>
        <url-pattern>/user/find</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>UserDeleteServlet</servlet-name>
        <servlet-class>com.dong.controller.UserDeleteServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UserDeleteServlet</servlet-name>
        <url-pattern>/user/delete</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.dong.controller.LoginServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/user/login</url-pattern>
    </servlet-mapping>
    <!--监听器-->
    <listener>
        <listener-class>com.dong.Listener.OneListener</listener-class>
    </listener>
    <!--过滤器-->
    <filter>
        <filter-name>TwoFilter</filter-name>
        <filter-class>com.dong.Filter.TwoFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>TwoFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


</web-app>

23.JSP

  1. JSP规范制定了如何开发JSP文件代替响应对象将处理结果写入到响应体的开发流程
  2. JSP规范制定了Http服务器应该如何调用管理JSP文件

响应对象存在弊端

  1. 适合将数据量较少的处理结果写入到响应体
  2. 如果处理结果数量过多,使用响应对象增加开发难度

JSP文件优势

  1. JSP文件在互联网通信过程,是响应对象替代品.
  2. 降低将处理结果写入到响应体的开发工作量降低处理结果维护难度
  3. 在JSP文件开发时,可以直接将处理结果写入到JSP文件不需要手写out.print命令,在Http服务器调用JSP文件时,根据JSP规范要求自动的将JSP文件书写的所有内容通过输出流写入到响应体

JSP文件作用

代替响应对象 将Servlet中doGet/doPost的执行结果写入到响应体

JSP文件中主要开发步骤

将作用域存放的处理结果读取出来并写入到响应体

<%
    String value = (String)request.getAttribute("key");
%>
<%=value%>
  1.  第一步:从指定的作用域对象读取处理结果
  2.  第二步: 将得到数据进行类型强转
  3.  第三步: 将转换后的数据写入到响应体

在JSP文件中如何书写Java命令

在jsp文件中,只有书写在执行标记中内容才会被当做java命令

单纯从开发角度看,JSP就是在HTML中嵌入Java程序,具体嵌入方式有:

1.JSP脚本执行Java逻辑代码

<% Java代码 %>   Java代码要输出只能输出在控制台,只有html的标签才能写在页面上

这个里面不能定义方法(一般就是调用方法)

2.JSP声明:定义Java方法

<%!
    声明Java方法
%>

这种方式里面一般不调用方法

3.JSP表达式 把Java对象直接输出到HTML页面中

<%= Java变量 %>

注意这里面一般就是输出变量的值的

<%!  public String test(){  return "HelloWorld"; } %>//这里是声明
<% String str = test(); %> //这里是调用
<%=str%>//这里是显示在HTML中
  • 执行标记
    • 命令格式
         <% int a  =10;  %> 声明局部变量

         <% boolean flag = 30 >= 40; %>  Java中表达式(数学表达式,关系表达式,逻辑表达式)

        <%
            if(判断条件){

            }else{

             }
            while(){
            }
        %>  书写控制语句

命令作用:

通知Http服务器将JSP文件中Java命令与其他普通执行结果进行区分

  • 输出标记

命令格式

<%=java的变量名%>

<%=java的表达式%>

eg:

<%
   int num1 =100;
   int num2 =200;
%>

<!--在JSP文件,通过输出标记,通知JSP将Java变量的值写入到响应体-->
变量num1的值:<%=num1%><br/>
变量num2的值:<%=num2%><br/>
<!--执行标记还可以通知Jsp将运算结果写入到响应体-->
num1 + num2 = <%=num1+num2%>

命令作用

通知Tomcat将输出标记中【变量的值】或则输出标记中【表达式运算结果】写入到响应体

如何将Servlet中doGet/doPost方法执行结果交给JSP文件输出

  1. JSP文件被访问时,并不是JSP文件在执行。而是对应的Servlet在执行
  2. 自定义Serlvet接口实现类与JSP文件之间调用关系,等同于两个Servlet之间调用关系
  3. 自定义Servelt接口实现类与JSP文件之间可以通过重定向方案或则请求转发方案进行调用考虑到运行效率问题,一般采用【请求转发】
  4. 自定义Servlet接口实现类可以通过请求作用域对象将共享数据交给JSP来输出到响应体

在jsp中导包:

<%@ page import="com.bjpowernode.entity.Student" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>

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

<%
   //创建一个Student类型对象
    Student stu = new Student(10,"mike");
    List list= new ArrayList();
%>

学员编号:<%=stu.getSid()%><br/>
学员姓名:<%=stu.getSname()%>

if在jsp中的运用:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    int age =15;
%>
<%
    if(age >=18){
%>
      <font style="color:red;font-size:45px">热烈欢迎</font>
<%
    }else{
%>
       <font style="color:red;font-size:45px">谢绝入内</font>
<%
    }
%>
<!--在jsp中将等价于:-->
<!--
   {
          int age =15;
          if(age>=18){
            out.print(<font style="color:red;font-size:45px">热烈欢迎</font>)
          }else{
            out.print(<font style="color:red;font-size:45px">热烈欢迎</font>)
         }
    }
-->

for在jsp中的运用:

<%@ page import="com.bjpowernode.entity.Student" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<!--制造数据-->
<%
       Student stu1 = new Student(10,"mike");
       Student stu2 = new Student(20,"allen");
       Student stu3 = new Student(30,"smith");
       List<Student> list = new ArrayList();
       list.add(stu1);
       list.add(stu2);
       list.add(stu3);
%>

<!--数据输出-->
<table border="2" align="center">
    <tr>
        <td>学员编号</td>
        <td>学员姓名</td>
    </tr>

    <%
       for(Student stu:list){
    %>
        <tr>
            <td><%=stu.getSid()%></td>
            <td><%=stu.getSname()%></td>
        </tr>
    <%
       }
    %>

</table>

在Jsp中写java程序:

<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
//这里我创建了一个User类,在com.southwind.servlet这个包下面,
//User里面有两个变量,
//name和age,然后有他们的getter和setter(可以自动生成的,在Generate里面)
//还有一个有参构造函数来赋值name和age
<%@ page import="com.southwind.servlet.User" %>
<%--
  Created by IntelliJ IDEA.
  com.southwind.servlet.User: DonghuiTan
  Date: 2020/2/17
  Time: 20:31
  To change this template use File | Settings | File Templates.
--%>
//允许中文
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <h1>Index</h1>
//这个是没有用User类,直接现场定义的
    <%
      List<String>names=new ArrayList<>();
      names.add("张三");
      names.add("李四");
      names.add("王五");
      List<Integer>age=new ArrayList<>();
      age.add(22);
      age.add(21);
      age.add(23);
    %>

//这个是调用了User类
    <%
      List<User>list = new ArrayList<>();
      list.add(new User("张三",21));
      list.add(new User("李四",22));
      list.add(new User("王五",23));
    %>
<table border="1px">
    <tr>
      <td>姓名</td>
      <td>年龄</td>
    </tr>
//通过这种方式可以实现html文本的循环
    <%
      for(int i=0;i<names.size();++i){
    %>

    <tr>
      <td>
        <%=list.get(i).getName()%>
      </td>
      <td>
        <%=list.get(i).getAge()%>
      </td>
    </tr>
    <%
      }
    %>

  </table>
  </body>
</html>

值得注意的是,这样写显然是不合适的,关于逻辑的书写,显然不应该写在jsp里面,jsp里面更多的还是主要用来显示。

JSP内置对象9个

JSP最终会转成Servlet,而在Servlet中已经定义好了这些对象,不需要自己定义(可以通过查看jsp转换过去的java文件看到其定义)

<!--
   JSP文件内置对象: request
             类型:HttpServletRequest
             作用: 在JSP文件运行时读取请求包信息
                  与Servlet在请求转发过程中实现数据共享

  浏览器: http://localhost:8080/myWeb/request.jsp?userName=allen&password=123
-->

<%
   //在JSP文件执行时,借助于内置request对象读取请求包参数信息
    String userName = request.getParameter("userName");
    String password =request.getParameter("password");
%>

来访用户姓名:<%=userName%><br/>
来访用户密码:<%=password%>

session_1.jsp 与 session_2.jsp为同一个用户/浏览器提供服务。 因此可以使用当前用户在服务端的私人储物柜进行数据共享

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!--
    JSP文件内置对象:session
              类型:HttpSession
              作用:JSP文件在运行时,可以session指向当前用户私人储物柜,添加
                   共享数据,或则读取共享数据
-->

<!--将共享数据添加到当前用户私人储物柜-->
<%
   // HttpSession session = request.getSession();
   session.setAttribute("key1", 200);
%>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
     Integer value=(Integer) session.getAttribute("key1");
%>
session_2.jsp从当前用户session中读取数据:<%=value%>

application:

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

<!--
       ServletContext application;全局作用域对象
       同一个网站中Servlet与JSP,都可以通过当前网站的全局作用域对象实现数据共享
       JSP文件内置对象 : application
-->

<%
    application.setAttribute("key1", "hello world");
%>

public class OneServlet extends HttpServlet {
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

          ServletContext application = request.getServletContext();
          String value = (String)application.getAttribute("key1");
          System.out.println("value = "+value);
    }
}

Servlet 与   JSP 分工:

    Servlet: 负责处理业务并得到【处理结果】--------------------大厨

    JSP:     不负责业务处理,主要任务将Servlet中【处理结果】写入到响应体----传菜员

Servlet 与  JSP 之间调用关系

   Servlet工作完毕后,一般通过请求转发方式 向Tomcat申请调用JSP

Servlet  与 JSP 之间如何实现数据共享

   Servlet将处理结果添加到【请求作用域对象】

   JSP文件在运行时从【请求作用域对象】得到处理结果

public class OneServlet extends HttpServlet {

    //处理业务,得到处理结果-----查询学员信息
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

             Student s1 = new Student(10,"mike");
             Student s2 = new Student(20,"allen");
             List stuList = new ArrayList();
             stuList.add(s1);
             stuList.add(s2);

             //将处理结果添加到请求作用域对象
             request.setAttribute("key", stuList);

             //通过请求转发方案,向Tomcat申请调用user_show.jsp
             //同时将request与response通过tomcat交给user_show.jsp使用
             request.getRequestDispatcher("/user_show.jsp").forward(request, response);
    }
}
Student实体类
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%
   //从请求作用域对象得到OneServlet添加进去的集合
     List<Student> stuList = (List)request.getAttribute("key");
%>

<!--将处理结果写入到响应体-->
<table border="2" align="center">
    <tr>
        <td>用户编号</td>
        <td>用户姓名</td>
    </tr>
    <%
       for(Student stu:stuList){
    %>
        <tr>
            <td><%=stu.getSid()%></td>
            <td><%=stu.getSname()%></td>
        </tr>
    <%
       }
    %>

</table>

Http服务器调用JSP文件步骤:【2019年北京地区常考面试题】

    1.Http服务器将JSP文件内容【编辑】为一个Servlet接口实现类(.java) 可以在下面的地址中看到这个java文件
    2.Http服务器将Servlet接口实现类【编译】为class文件(.class)
    3.Http服务器负责创建这个class的实例对象,这个实例对象就是Servlet实例对象
    4.Http服务器通过Servlet实例对象调用_jspService方法,将jsp文件内容写入到响应体

Http服务器【编辑】与【编译】JSP文件位置:

    标准答案:我在【work】下看到这个证据

    C:\Users\[登录windows系统用户角色名]\.IntelliJIdea2018.3\system\tomcat\[网站工作空间]\work\Catalina\localhost\【网站别名】\org\apache\jsp

jsp编辑成Servlet接口实现类时的方法:

HttpJspBase

Tomcat将JSP文件编辑后生成的Java文件,继承与HttpJspBase。通过Apache官方文档查看

其身份是HttpServlet的子类。所以Tomcat将JSP编辑为的文件为一个Servlet接口实现类

_jspService方法

作用:

当一个JSP文件被访问时,Tomcat调用的并不是当前JSP文件。而是JSP文件对应的Servlet接口实现类中_jspService方法。_jspService方法负责将开发人员在JSP文件书写的所有内容写入到响应体中

内部结构:

  1. 判断当前请求方式。Jsp文件可以接收的请求方式有POST,GET,HEAD
  2. 声明局部变量。这些局部变量都可以在JSP文件开发时直接使用
  3. 输出部分。这部分执行时将JSP文件内容通过输出流写入到响应体

24.request常用方法:

1. String getParameter(String key)  获取客户端(如页面)传来的参数

传参咋地址栏最后面加上: eg: id = 1000&name = zhangsan(多个之间用&)

收到的只能是String,所以在接收到传参以后可以对其进行转换:

Integer idint = Integer.parseInt(id);

2.void setAttribute(String key,Object value) 通过键值对的形式保存数据

req.setAttribute("list",list);

后面的那个list是我之前定义的一个列表。

3. Object getAttribute(String key)通过key取出value

在内部传递与接收数据需要用 getAttribute 与setAttribute

4.RequestDispatcher  getRequestDispatcher(String Path)

返回一个RequestDispatcher对象,该对象forword方法用于请求转发

req.getRequestDispatcher("index.jsp").forward(req,resp);

这个是要传输数据的转发到另外一个jsp中(不只是可以传jsp,html与其他的servlet等都可以传,里面是包含数据的)

5. String[] getParameterValues() 获取客户端传来的多个同名参数

6.void setCharacterEncording(String charset)指定每个请求的编码,常用的就是设置成utf-8

req.setCharacterEncoding("UTF-8");

一般一整套流程如下:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //首先设置一下编码,要不然getParameter取出来的可能是乱码
    req.setCharacterEncoding("UTF-8");
    //获取username的值
    String idstr=(String)req.getParameter("id");
    //id应该是整形的所以把它转型
    Integer id = Integer.parseInt(idstr);
    //得先把值setAttribute到req里面
    req.setAttribute("number",id);
    //然后才能转发,这里转发的req里面是含有刚刚setAttribute的键值对的
    //然后带着数据跳转到了index.jsp中
    req.getRequestDispatcher("index.jsp").forward(req,resp);
}
<h1>index.jsp</h1>
<%   //getAttribute获得的是Object,可以直接向下转型成Integer
    Integer id = (Integer)request.getAttribute("number");
%>
<%=id%>

在地址栏中输入

http://localhost:8080/test.jsp?name=zhangsan&name=lisi&name=东东

同时传了三个name

<% //指定编码方式
    request.setCharacterEncoding("UTF-8");
    //这样可以同时收到很多的name,并把它们打包成了一个String数组
    String[] names=request.getParameterValues("name");
%>//这样可以把String数组直接全部输出
<%=Arrays.toString(names)%>

其页面上的输出

小实例:在一个界面中注册,在后台进行验证以后,如果正确跳转到新的页面,如果错误则重新回到注册页面:

login.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
    <body>
        <form action="/check.jsp"method="post">
            用户名:<input type="text" name="username"/><br/>
            密码:<input type="password"name="password"><br/>
            //这里注意得用submit啊,要不然跳转不了的
            <input type="submit"value="登录"/>
        <form/>
    </body>
</html>

check.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%
        String username=(String)request.getParameter("username");
        String password = (String)request.getParameter("password");
        if(password.equals("123123")&&username.equals("login")){
            request.setAttribute("name",username);
            //getRequestDispatcher是转发,其中包含了数据的,比如这里发送的request是包含了获得的username的
            request.getRequestDispatcher("welcome.jsp").forward(request,response);
        }
        else{
            //sendRedirect是重定向,其中不包含数据,只是页面的跳转
            response.sendRedirect("login.jsp");
        }
    %>
</body>
</html>

welcome.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>Welcome</h1>
<%
    String name =(String)request.getAttribute("name");
%>
<%=name%>
</body>
</html>

25.response常用方法:

1.sendRedirect(String Path)重定向,页面之间的跳转。

26.JSP内置对象作用域

小实例:网站访问量统计:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%   //application作用域比较大,因为是可以存放在Tomcat里面的,切换请求、以及浏览器并不会对它有影响
        Integer count = (Integer)application.getAttribute("count");
        if(count==null){
            count=1;
            application.setAttribute("count",count);
        }
        else{
            count = (Integer)application.getAttribute("count");
            count++;
            application.setAttribute("count",count);
        }
    %>
您是第<%=count%>位访问本页面的顾客
</body>
</html>

27.EL表达式

EL工具包介绍:

  1. 由Java技术开发一个jar包
  2. 作用降低JSP文件开发时Java命令开发强度
  3. Tomcat服务器本身自带了EL工具包(Tomcat安装地址/lib/el-api.jar)
public class OneServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 1.分别将共享数据添加到作用域对象

        ServletContext application = request.getServletContext();
        HttpSession session = request.getSession();

        application.setAttribute("sid", 10);
        session.setAttribute("sname", "mike");
        request.setAttribute("home", "新起屯");

        //2.通过请求转发方式,向Tomcat申请调用index_1.jsp,由index_1.jsp负责将
        // 作用域对象共享数据读取并写入到响应体,交给浏览器
        request.getRequestDispatcher("/index_1.jsp").forward(request, response);
    }
}

传统采用jsp开发步骤:

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

<%
     Integer sid =(Integer)  application.getAttribute("sid");
     String  sname= (String)session.getAttribute("sname");
     String  home=  (String)request.getAttribute("home");
%>
学员ID:<%=sid%><br/>
学员姓名:<%=sname%><br/>
学员地址:<%=home%>

采用EL表达式:

学员ID:  ${applicationScope.sid}<br/>
学员姓名: ${sessionScope.sname}<br/>
学员地址:${requestScope.home}

EL表达式

     1.命令格式:${作用域对象别名.共享数据}

     2.命令作用:
          1)EL表达式是EL工具包提供一种特殊命令格式【表达式命令格式】
          2)EL表达式在JSP文件上使用
          3)负责在JSP文件上从作用域对象读取指定的共享数据并输出到响应体

EL表达式——作用域对象别名

     1.JSP文件可以使用的作用域对象

        1) ServletContext     application:  全局作用域对象

        2) HttpSession        session:      会话作用域对象

        3) HttpServletRequest    request:      请求作用域对象

        4) PageContext        pageContext: 当前页作用域对象,这是JSP文件独有的作用域对象。Servlet中不存在
                                        在当前页作用域对象存放的共享数据仅能在当前JSP文件中使用,不能
                                        共享给其他Servlet或则其他JSP文件
                                        真实开发过程,主要用于JSTL标签与JSP文件之间数据共享
                                                  数据
                                         JSTL------->pageContext---->JSP


    2.EL表达式提供作用域对象别名
    
              JSP                       EL表达式

          application               ${applicationScope.共享数据名}

          session                     ${sessionScope.共享数据名}

          request                     ${requestScope.共享数据名}

          pageContext            ${pageScope.共享数据名}

EL表达式将引用对象属性写入到响应体

public class Student {

    private Integer sid;
    private String  sname;

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public Student() {
    }

    public Student(Integer sid, String sname) {
        this.sid = sid;
        this.sname = sname;
    }
}
<!--传统写法-->
<%
    Student stu= (Student)request.getAttribute("key");
%>
学员编号:<%=stu.getSid()%><br/>
学员姓名:<%=stu.getSname()%>

<hr/>
学员编号:${requestScope.key.sid}<br/><!--sid来自于Student类属性名,大小写完全一致-->
学员姓名:${requestScope.key.sname}

     1.命令格式: ${作用域对象别名.共享数据名.属性名}

     2.命令作用: 从作用域对象读取指定共享数据关联的引用对象的属性值。
                  并自动将属性的结果写入到响应体

     3.属性名: 一定要去引用类型属性名完全一致(大小写)

     4.EL表达式没有提供遍历集合方法,因此无法从作用域对象读取集合内容输出

EL表达式简化版

     1.命令格式: ${共享数据名}

     2.命令作用: EL表达式允许开发人员开发时省略作用域对象别名

     3.工作原理:
                  EL表达式简化版由于没有指定作用域对象,所以在执行时采用【猜】算法

          首先到【pageContext】定位共享数据,如果存在直接读取输出并结束执行
          如果在【pageContext】没有定位成功,到【request】定位共享数据,如果存在直接读取输出并结束执行
          如果在【request】没有定位成功,到【session】定位共享数据,如果存在直接读取输出并结束执行
          如果在【session】没有定位成功,到【application】定位共享数据,如果存在直接读取输出并结束执行
          如果在【application】没有定位成功,返回null

          pageContext--->request--->session--->application

     4.存在隐患:

                  容易降低程序执行速度【南辕北辙】
                  容易导致数据定位错误

     5.应用场景:
                   设计目的,就是简化从pageContext读取共享数据并输出难度

     6.EL表达式简化版尽管存在很多隐患,但是在实际开发过程中,开发人员为了节省时间,一般都使用
       简化版,拒绝使用标准版


EL表达式-----支持运算表达式

     1.前提:  在JSP文件有时需要将读取共享数据进行一番运算之后,将运算结果写入到响应体

     2.运算表达式:
      
          1) 数学运算

          2) 关系运算:  >    >=   ==    <   <=  !=

                                gt   ge    eq   lt  le   !=(与上面的符号是等效的)

          3)逻辑运算:  &&   ||    !

<!--传统Java命令方式实现关系运算输出-->
<%
     String age =  (String)request.getAttribute("age");
     if(Integer.valueOf(age)>=18){
%>
          欢迎光临<br/>
<%
     }else{
%>
         谢绝入内<br/>
<%
     }
%>

EL表达式输出关系运算: ${age ge 18?"欢迎光临":"谢绝入内"}

EL表达式提供内置对象    (用得少)

      1.命令格式: ${param.请求参数名}

      2.命令作用: 通过请求对象读取当前请求包中请求参数内容
                            并将请求参数内容写入到响应体

      3.代替命令:  index.jsp

           发送请求:  Http://localhost:8080/myWeb/index.jsp?userName=mike&password=123
           <%
             String userName =   request.getParameter("userName");
             String password =   request.getParameter("password");
           %>
           <%=userName%>
           <%=password%>

代替:

来访者姓名:${param.userName}<br/>
来访者密码:${param.password}

      1.命令格式:${paramValues.请求参数名[下标]}

      2.命令作用: 如果浏览器发送的请求参数是[一个请求参数关联多个值]
                          此时可以通过paramVaues读取请求参数下指定位置的值
                          并写入到响应体

      3.代替命令: http://localhost:8080/myWeb/index_2.jsp?pageNo=1&pageNo=2&pageNo=3
                  此时pageNo请求参数在请求包以数组形式存在(这里面只有一个请求参数,关联三个值)
          pageNo:[1,2,3]

          <%
              String  array[]= request.getParameterValues("pageNo");
          %>
          第一个值:<%=array[0]%>
          第二个值:<%=array[1]%>

代替:

第一个部门编号:${paramValues.deptNo[0]}<br/>
第二个部门编号:${paramValues.deptNo[1]}<br/>
第三个部门编号:${paramValues.deptNo[2]}<br/>

EL表达式常见异常:

 javax.el.PropertyNotFoundException:在对象中没有找到指定属性

注意要用setAttribute,在类里面必须要写好getter函数

3.${user.id}调用user的getId方法,它是跟方法绑定的也就是说,user类里面必须要有getId这个函数,然后把函数里面的值给提取出来,比如getId里面放的是num,那输出的也就是num而不是id。

4.${user.id = 3}这个调用了setId方法,但是很少用

5.EL执行表达式:

eg:${num>num2} 输出的true或者false

小实例:

<%
    User user = new User("张三",18);
    request.setAttribute("user",user);
%>
${user}

这样输出的是:

这是因为EL表达式相当于还调用了toString()这个函数,其原始定义为:

因此才会这样输出。

因此我们如果想要直接用${user}来输出user中所有的内容,可以在User中重载toString(),idea可以帮我们直接重载这个不需要我们自己写,不要太方便!

然后就输出:

如果要取出来其中一个,可以

${user.id}

它相当于:((User) request.getAttribute("user")).getId();

所以要有EL表达式getId这个方法,没有会报错.

28.JSTL

JSP标准标签库,JSP为开发者提供的一系列标签,使用这些标签,可以完成一些逻辑处理,比如循环遍历集合,让代码更加简洁,不在出现JSP脚本穿插的情况。

实际开发中,EL与JSTL一般结合起来使用,JSTL侧重于逻辑处理,EL负责展示数据。

JSTL的使用

1.需要导入jar包(两个: jstl.jar standard.jar)存放的位置 web/WEB-INF

2.在JSP页面开始的地方导入JSTL标签库:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>(注意这里的c与下面的c是同一个c,这个c是一个别称,可以取别的名字的)

3.常用标签:set out remove catch:

set:向域对象中添加数据,var是key,value是给的值,scope可以表示是那个作用域

<c:set var="name" value="tom" scope="request"></c:set>
${requestScope.name}

也可以这样对对象的参数进行修改

<%
    User user = new User("张三",16);
    //注意写了一个新的对象一定要setAttribute要不然是用不了EL的
    request.setAttribute("user",user);
%>
//这里target后面千万别写成了"user",得是EL表达式才能把东西取出来,
//property指定修改的是user中的哪个值,value指定的是修改的值是什么
<c:set target="${user}" property="name" value="李四"></c:set>
${user.name}

out标签:输出域对象中的数据, 就是比EL表达式多了一个default属性

<c:out value="${name}" default="未定义"></c:out>

remove标签:删除域对象中的数据

<c:remove var="asdf" scope="page"></c:remove>

catch标签:捕获异常,但是使用的少

if和choose标签:

<c:set var="num" value="1"></c:set>
<c:set var="num2" value="2"></c:set>
<c:if test="${num>num2}">ok</c:if>
<c:if test="${num<num2}">fail</c:if>  (这里面没有else这种写法)
<hr/>
<c:choose>
//这个里面when相当于if otherwise相当于else
        <c:when test="${num>num2}">ok</c:when>
        <c:otherwise>fail</c:otherwise>
</c:choose>

foreach标签

<c:forEach items="${list}" var="str" begin="1" end="6" step="2" varStatus="sta">
    ${sta.count}-${str}
</c:forEach>

foreach属性:类比于 for(int x:y)

items -> y;
var -> x;
begin -> 遍历的起始位置;
end -> 遍历的结束位置;
step -> 步长;
varStatus -> 另外一个参数,比如可以用来做排序
eg : varStratus = "sta";
${sta.count} 可以作为序号,从1开始

格式化常用标签:

首先需要在前面包含:

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

一个小实例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--使用jstl库需要加这句话,prefix说明以后调用的jstl标签库的时候,
只需要使用c来表示就可以这个标签库就可以了--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
   // List<User>list = (List<User>)request.getAttribute("list");
%>
<%--${requestScope.list[0].age}--%>
<table>
    <tr>
        <td>名称</td>
        <td>年龄</td>
    </tr>
//这里用了jstl,可以不用像之前一样用<% %>有一堆这个     
//items中存放的是要取出来的东西(相当于
// List<User>list = (List<User>)request.getAttribute("list");)     
//然后var里面存放的是要代替items中的东西,
//去遍历的 for(User user:list)差不多就是这个list和这个user的意思
    <c:forEach items="${list}" var="user">
        <tr>
            <td>${user.name}</td>
            <td>${user.age}</td>
        </tr>
    </c:forEach>
</table>

</body>
</html>

20.过滤器(Filter)

在客户端与服务器(Servlet)之间放上一个过滤器

其配置方式有两种:

基于注解的配置方式:

@WebFilter("/test") 
public class WordFilter implements Filter {}

实际开发中Filter的使用场景:

1.统一处理中文乱码

package com.southwind.filter;
import javax.servlet.*; 
import java.io.IOException;
public class CharacterFilter implements Filter {
    @Override    
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException 
{        
    servletRequest.setCharacterEncoding("UTF-8");        
    filterChain.doFilter(servletRequest,servletResponse);    
  }
}

注意:doFilter方法中处理完业务逻辑之后,必须添加filterChain.doFilter(servletRequest.servletRsponse),这句代码主要是让相应向后传递,如果没有,那相应就会一直停留在过滤器中。

2.屏蔽敏感词

WordFilter

@WebFilter("/test")
public class WordFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //首先把页面输入的东西给截取出来
        String name = servletRequest.getParameter("value");
        //调用String的replaceAll就可以全部替换了
        name = name.replaceAll("敏感词","***");
        //替换完成以后一定要把它重新写进去,然后我在接收端在getAttribute
        servletRequest.setAttribute("name",name);
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

TestServlet

@WebServlet("/test")
public class TestServet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //因为是从过滤器获得,所以是getAttribute而不是getParameters
        String value = (String)req.getAttribute("name");
        System.out.println(value);
    }
}

3.控制资源的访问权限:

eg:想要进入download.jsp必须先登录(session中有值)否则就跳转到登录页面

login.jsp

<form action="/login" method="post">
   姓名 <input type="text" name="name"/><br/>
    密码<input type="password" name="password"><br/>
    <input type = "submit" value="提交"/><br/>
</form>

LoginServlet:

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = (HttpSession)req.getSession();
        String password = req.getParameter("password");
        String name = req.getParameter("name");
        if(name.equals("张三")&&password.equals("123")){
            //如果登入了就把名字写入session中,因为这里用的是张三,是中文,所以我们还需要代用前面的CharacterFilter
            session.setAttribute("name",name);
            resp.sendRedirect("download.jsp");
        }
        else{
            resp.sendRedirect("login.jsp");
        }


    }
}

DownloadFilter:

@WebFilter("/download.jsp")
public class DownloadFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在filter中使用因为它直接关联的是Servlet,
        //而我们平常用的都是HttpServlet所以需要向下转型(因为要获得session)
        HttpServletRequest requset = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        HttpSession session = requset.getSession();
        //在LoginServlet中会登录的信息写入session,所以这里调用session
        //看它是否是空的,如果不是空的则说明登陆了
       String name=(String) session.getAttribute("name");
        if(name==null){
            response.sendRedirect("login.jsp");
        }
        else{
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
}

download.jsp

<body>
<a href="">资源一</a>
<a href="">资源二</a>
<a href="">资源三</a>
</body>

21.实现增删查改(没有用数据库,是直接在作用域里面来回传递信息)

User.java:

package com.dong;

public class User {
    private Integer id;
    private String name;
    private Double score;

    public User(Integer id, String name, Double score) {
        this.id = id;
        this.name = name;
        this.score = score;
    }

    public void setId(Integer id) {
        this.id = id;
    }

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

    public void setScore(Double score) {
        this.score = score;
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Double getScore() {
        return score;
    }

}

login.jsp:(登录添加页面)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/user" method="post">
    <table border="1px">
        <tr>
            <td>编号</td>
            <td><input type="text" name="id"></td>
        </tr>
        <tr>
            <td>姓名</td>
            <td><input type="text" name="name"></td>
        </tr>
        <tr>
            <td>分数</td>
            <td><input type="text" name="score"></td>
        </tr>
        <tr>
            <input type="submit" value="提交"/>
        </tr>
    </table>
</form>
</body>
</html>

UserServlet:

package com.dong;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    //要用map,这样子可以通过id来对里面的值进行修改
   public static Map<Integer,User>map=new HashMap<>();
  //初始化的话可以使用静态代码块(这个要声明成public static)或者默认构造函数(都是只调用一次的)
//    static {
//        map.add(1,new User(1,"张三",79.5));
//        map.add(2,new User(2,"李四",89.5));
//        map.add(3,new User(3,"王五",99.5));
//    }
    public UserServlet() {
        map.put(1,new User(1,"张三",79.5));
        map.put(2,new User(2,"李四",89.5));
        map.put(3,new User(3,"王五",99.5));
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {      
        //如果id相同,那在map里面就是直接覆盖了,这就是修改
        //如果id不同,那就是加了一个所以是增加
        //下面这句话可以防止乱码
        req.setCharacterEncoding("UTF-8");
        String idStr =req.getParameter("id");
        String name=req.getParameter("name");
        String scoreStr = req.getParameter("score");
        Integer id =Integer.parseInt(idStr);
        Double score = Double.parseDouble(scoreStr);
        //map.put是输入key值,然后输出里面的values
        map.put(id,new User(id,name,score));
        //下面这句话就是进入doGet中,然后被赋值长了findAll,所以就将所有的东西显示了
        resp.sendRedirect("/user");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过在超链接后面加入值,就可以区分这个超链接是在干嘛
        String method = req.getParameter("method");
        String idStr;
        Integer id;
        if(method==null){
            method = "findAll";
        }
        switch (method){
            //查
            case "findAll":
                req.setAttribute("map",map.values());
                req.getRequestDispatcher("index.jsp").forward(req,resp);
                break;
            //删
            case "delete":
                idStr = req.getParameter("id");
                id = Integer.parseInt(idStr);
                //这里因为用的是map,所以可以通过它的key直接删掉它的values
                map.remove(id);
                //这个相应又调回了doGet,然后进入了findAll,然后显示了
                resp.sendRedirect("/user");
                break;
            //改
            case "findById":
                idStr = req.getParameter("id");
                id = Integer.parseInt(idStr);
                //map.get("key")可以得到key对应的值
                req.setAttribute("user",map.get(id));
                req.getRequestDispatcher("update.jsp").forward(req,resp);
                break;
        }

    }
}

index.jsp(显示页面)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
        <table>
          <tr>
              <td>编号</td>
              <td>姓名</td>
              <td>成绩</td>
              <td>操作</td>
          </tr>
                <c:forEach items="${map}" var="user">
                    <tr>
                        <td>${user.id}</td>
                        <td>${user.name}</td>
                        <td>${user.score}</td>
                        <td>
                            //注意这里超链接是传了值回去的
                            <a href="/user?method=delete&id=${user.id}">删除</a>
                            <a href="/user?method=findById&id=${user.id}">修改</a>
                        </td>
                    </tr>

                </c:forEach>
        </table>
  </body>
</html>

update.jsp(修改操作页面)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/user" method="post">
    <table border="1px">
        <tr>
            <td>编号</td>
            //readonly就是这一行只能读不能修改
            <td><input type="text" name="id" readonly value="${user.id}"></td>
        </tr>
        <tr>
            <td>姓名</td>
            <td><input type="text" name="name" value="${user.name}"></td>
        </tr>
        <tr>
            <td>分数</td>
            <td><input type="text" name="score" value="${user.score}"></td>
        </tr>
        <tr>
            //点击修改以后,因为用的是map所以直接新内容直接覆盖了旧内容
            <input type="submit" value="修改"/>
        </tr>
    </table>
</form>
</body>
</html>

22.文件上传下载

在doPost中:通过输出输出流来使得文件上传下载,但是这样做有一个问题,没办法换行,并且文件中会带有web中的一些信息

InputStream inputStream = req.getInputStream();
        //读取字节流
        //        int temp = 0 ;
        //当没有了以后字节流会返回负一,要不就会返回0-255之间的值
//        while((temp=inputStream.read())!=-1){
//            System.out.println(temp);
//        }
        Reader reader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(reader);

        //注意这里的路径是绝对路径而且是打包以后out路径的绝对路径
        //所以加文件只能存到out文件夹里面
        //它这里会自动生成一个copy.txt
        String path = req.getServletContext().getRealPath("file/copy.txt");
        System.out.println(path);
        OutputStream outputStream = new FileOutputStream(path);
        Writer writer = new OutputStreamWriter(outputStream);
        BufferedWriter bufferedWriter = new BufferedWriter(writer);

        String str = "";
        //带缓存的输入输出流可以很好地读取并写入一整行的值
        while ((str=bufferedReader.readLine())!=null){
            bufferedWriter.write(str);
        }
        bufferedReader.close();
        bufferedWriter.close();
        reader.close();
        writer.close();
        inputStream.close();
        outputStream.close();

所以我们引入了另外一个外部的库fileupload

配置好以后,可以将上面的代码修改成以下的形式

try {
    //首先创建对象,这个我不是很懂,不过应该都是这样的
    DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
    ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
    //这个使得文件名可以是中文
    servletFileUpload.setHeaderEncoding("UTF-8");
    //这个是将页面发来的请求写道FileItem中去
   List<FileItem>list = servletFileUpload.parseRequest(req);
    //因为可以有很多个请求,所以它需要遍历
    for(FileItem fileItem :list){
        //如果是true则表明它是一个框(type为"text"类型等)
       if(fileItem.isFormField()){
            //获取框中name属性的值
            String name =fileItem.getFieldName();
            //获取key值,并且支持UTF-8可以输入中文
            String value = fileItem.getString("UTF-8");
            System.out.println(name+":"+value);
        }
        else{   //这个else表示有文件传输
             //获取文件名,这个文件名需要包括前面的servletFileUpload.setHeaderEncoding("UTF-8")这样才不是乱码
            String fileName = fileItem.getName();
            System.out.println(fileName);
            //下面这是是因为我在Ie浏览器汇中传输,它getName获得的是绝对地址而不是单纯的名字,所以我可以直接通过这个来把名字取出来
            int index=fileName.lastIndexOf("\\");
            if(index!=-1) {
                fileName=fileName.substring(index+1);
            }
            long size = fileItem.getSize();
            System.out.println(fileName+":"+size+"Byte");
            //输入字节,这里不能用reader或者bufferedreader
            //因为比如输入的是图片,用这个会对其结构产生影响,只能以字节读取
           InputStream inputStream = fileItem.getInputStream();
            //这里可以保留文件本来的名字
            String path = req.getServletContext().getRealPath("file/"+fileName);
            OutputStream outputStream = new FileOutputStream(path);
            int temp = 0;
            //以字节写入
            while ((temp= inputStream.read())!=-1){
                outputStream.write(temp);
            }
            //记得要关掉
            outputStream.close();
            inputStream.close();
            System.out.println("上传成功");
        }

    }
} catch (FileUploadException e) {
    e.printStackTrace();
}

文件的下载:

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String type = req.getParameter("name");
    String filename = "";
    switch (type){
        case "txt":
            filename="test.txt";
            break;
        case "jpg":
            filename="1.jpg";
            break;
    }
    //设置相应方式
    resp.setContentType("application/x-msdownload");

    //设置下载之后的文件名(除了filename其他都是写死的)
    resp.setHeader("Content-Disposition","attachment;filename="+filename);
    //获取输出流
    OutputStream outputStream =resp.getOutputStream();
    //获取要被下载的文件的地址(在工程中只需要写文件名,不需要写绝对地址)
    //文件要先被存入web里面,然后会被out打包(如果没有被打包可以在build中选择rebuild project),然后才能调用
    String path = req.getServletContext().getRealPath("file/"+filename);
    InputStream inputStream = new FileInputStream(path);
    int temp = 0;
    //按照字节读入并输出
    while ((temp= inputStream.read())!=-1){
        outputStream.write(temp);
    }
    inputStream.close();
    outputStream.close();
}
<body>
<a href="/download?name=jpg">1.png</a>
<a href="/download?name=txt">text.txt</a>
</body>

23.JDBC

JDBC的使用:

1. 加载数据库驱动,java程序和数据库之间的桥梁。(JDBC API与JDBC Driver Manager由Java官方已经写好的,但是JDBC驱动是厂家写的,所以要额外导入)

2.获取Connection,java程序与数据库的一次连接

3.创建Statement对象,由Connection产生,执行SQL语句

4.如果需要接收返回值,创建ResultSet对象,保存Statement执行之后所查询到的结果。

5.断开连接,释放资源

要使用首先记得要导入jar包,然后配置一下idea的database

最基本的增删查改:

package com.dong.test;

import java.sql.*;
import java.util.Date;

public class Test {
    public static void main(String[] args) {
        try {
            //加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //获取连接
            //这里要注意,test1是我本地的数据库,可以选的,但是得本地有
            String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
            String user = "root";
            String password = "tanhuidong";
            Connection connection = DriverManager.getConnection(url,user,password);
            //这里写了一个语句
            //String sql = "insert into student(id,name,score,birthday) values(2,'李四',88,'2019-01-01')";
            //String sql = "update student set name ='李四'";
            //String sql = "delete from student";
            //Statement statement = connection.createStatement();
            //增删改都是executeUpdate
            //int result = statement.executeUpdate(sql);
            //System.out.println(result);
            //查不一样
            String sql = "select * from student";
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            while(resultSet.next()){
                //这个是是通过标识来获取,比如我现在是通过id来获取id的值
                Integer id =resultSet.getInt("id");
                //这个是通过下标去获取
                String name = resultSet.getString(2);
                Double score = resultSet.getDouble(3);
                Date date = resultSet.getDate(4);
                System.out.println(id+"-"+name+"-"+score+"-"+date);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e){
            e.printStackTrace();
        }

    }
}

PreparedStatement

PreparedStatement是Statement的子类,提供了SQL占位符的功能。

使用preparestatement实现该功能:

package com.dong.test;

import java.sql.*;
import java.util.Date;

public class Test {
    public static void main(String[] args) {
        try {
            //加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //获取连接
            //这里要注意,test1是我本地的数据库,可以选的,但是得本地有
            String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
            String user = "root";
            String password = "tanhuidong";
            Connection connection = DriverManager.getConnection(url,user,password);
            //or '1' = '1'是一种恶意注入,如果用statement,那么用户都为1,也就是肯定能被读出
            //使用preparedStatemrnt可以避免这种情况
//            String name = "李四 or '1' = '1'";
//            String password1 = "000 or '1' = '1'";
            String name = "李四";
            String password1 = "000";
            //这个是使用statement
            //String sql = "select * from student where name ='"+name+"'and password = '" + password1+"'";
            //yong ?来占位,然后可以通过setString来添加,这样可以避免拼接
            String sql = "select * from student where name = ? and password = ?";
            //这个与statement不同,它是先写入sql再去executeQuery的,而statement相反
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            //这样可以避免sql语句被拼接
            preparedStatement.setString(1,name);
            preparedStatement.setString(2,password1);
            ResultSet resultSet =preparedStatement.executeQuery();
//            Statement statement = connection.createStatement();
//            ResultSet resultSet = statement.executeQuery(sql);
            //如果返回了结果说明不是空的,也就是登录成功了
            if(resultSet.next()){
                System.out.println("登录成功");
            }else{
                System.out.println("登录失败");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e){
            e.printStackTrace();
        }

    }
}

24 数据库连接池

数据库连接池实现:

使用:

1.导入jar包

2.在实际开发中,C3P0的配置信息定义在xml文件夹中,jaca程序只需要加载配置文件即可完成数据库连接池的初始化才做

其配置文件名必须是c3p0-config.xml

初始化ComboPooledDataSource时,传入的参数必须是c3p0-config.xml中named-config标签的name属性值。

xml:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!--这个xml文件名不能随便定义,配置文件名必须使用默认的-->
    <named-config name="testc3p0">

        <!-- 指定连接数据源的基本属性 -->
        <property name="user">root</property>
        <property name="password">这里是密码</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        //标红的地方是要改的,我这里是有一个hospital的数据库在mysql里面
        <!-- xml里面不能直接写&,得用&amp;来替代它-->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/hospital?useUnicode=true&amp;characterEncoding=UTF-8</property>

        <!-- 若数据库中连接数不足时, 一次向数据库服务器申请多少个连接 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化数据库连接池时连接的数量 -->
        <property name="initialPoolSize">20</property>
        <!-- 数据库连接池中的最小的数据库连接数 -->
        <property name="minPoolSize">2</property>
        <!-- 数据库连接池中的最大的数据库连接数 -->
        <property name="maxPoolSize">40</property>

    </named-config>

</c3p0-config>

java:

package com.dong.utils;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCTools {
    private static DataSource dataSource;
    static {
        dataSource = new ComboPooledDataSource("testc3p0");
    }
    public static Connection getConnection(){
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    public static void release(Connection connection, Statement statement, ResultSet resultSet){
        try {
            if (connection!= null){
                connection.close();
            }
            if (statement!=null){
                statement.close();
            }
            if(resultSet!=null){
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

调用:

Connection connection = JDBCTools.getConnection();

这里讲一个小的报错的问题:

这里如果student在try前面没有赋初值(=null),在return student的时候是会报错的,这是因为它的赋值被写到了try - catch里面,如果在student被赋值之前有语句报错,那student就没有办法赋值,但是最后结尾有return student,因此它会报错。

 

25 Servlet实现数据库的增删查改(在mywebzonghe里面)

初始显示见面index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <h1>学生管理系统</h1>
  <table>
    <tr>
      <th>编号</th>
      <th>姓名</th>
      <th>成绩</th>
      <th>注册日期</th>
      <th>操作</th>
    </tr>
    <c:forEach items="${list}" var="student" >
      <tr>
        <td>${student.id}</td>
        <td>${student.name}</td>
        <td>${student.score}</td>
        <td>${student.birthday}</td>
        <td>
          <a href="/student?method=delete&id=${student.id}">删除</a>
          <a href="/student?method=findById&id=${student.id}">修改</a>
        </td>
      </tr>
    </c:forEach>
  </table>
  </body>
</html>

相当于main的主要的servlet:

package com.dong.test.servlet;

import com.dong.test.entity.Student;
import com.dong.test.repository.StudentRepository;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;

@WebServlet("/student")
public class StudentServlet extends HttpServlet {
    StudentRepository studentRepository = new StudentRepository();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getParameter("method");
        if(method==null){
            method = "findAll";
        }
        switch (method){
            case "findAll":
                //返回视图+数据
                List<Student>list = studentRepository.findAll();
                req.setAttribute("list",list);
                req.getRequestDispatcher("index.jsp").forward(req,resp);
                break;
            case "delete":
                String idStr = req.getParameter("id");
                Integer id = Integer.parseInt(idStr);
                studentRepository.deleteById(id);
                resp.sendRedirect("/student");
                break;
            case "findById":
                idStr = req.getParameter("id");
                id = Integer.parseInt(idStr);
                req.setAttribute("student",studentRepository.findById(id));
                req.getRequestDispatcher("update.jsp").forward(req,resp);
        }


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getParameter("method");
        switch (method){
            case "add":
                String name = req.getParameter("name");
                String idStr =req.getParameter("id");
                Integer id = Integer.parseInt(idStr);
                String scoreStr = req.getParameter("score");
                Double score = Double.parseDouble(scoreStr);
                studentRepository.add(id,name,score);
                break;
            case "update":
                idStr = req.getParameter("id");
                id = Integer.parseInt(idStr);
                name = req.getParameter("name");
                scoreStr = req.getParameter("score");
                score = Double.parseDouble(scoreStr);
                studentRepository.update(id,name,score);
                break;
        }
        resp.sendRedirect("/student");

    }
}

对数据库进行操作的java代码:

package com.dong.test.repository;

import com.dong.test.entity.Student;
import com.dong.test.util.JDBCTools;

import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class StudentRepository {
    public List<Student> findAll(){
       List<Student>list = new ArrayList<>();
        Connection connection= null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCTools.getConnection();
            String sql = "select * from student";
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            Student student = null;
            while(resultSet.next()){
                Integer id = resultSet.getInt(1);
                String name = resultSet.getString(2);
                Double score = resultSet.getDouble(3);
                Date date = resultSet.getDate(4);
                //上面把数据已经取出来了,然后下面直接把他们封装起来就可以了
//                Student student = new Student(id,name,score,date);
                student = new Student(id,name,score,date);
                list.add(student);
            }
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            JDBCTools.release(connection,preparedStatement,resultSet);
        }
        return list;
    }

    public void add(Integer id , String name , Double score ){
        List<Student>list = new ArrayList<>();
        Connection connection= null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCTools.getConnection();
            String sql = "insert into student(id,name,score,birthday) values (?,?,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,id);
            preparedStatement.setString(2,name);
            preparedStatement.setDouble(3,score);
            preparedStatement.setDate(4,new java.sql.Date(1));
            preparedStatement.executeUpdate();
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            //没有参数就传输null啊,然后再release里面判断不是null就不关
                JDBCTools.release(connection,preparedStatement,null);

        }
    }
    public void deleteById(Integer id){
       Connection connection = null;
       PreparedStatement preparedStatement = null;
        try {
            connection = JDBCTools.getConnection();
            String sql = "delete from student where id = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,id);
            preparedStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCTools.release(connection,preparedStatement,null);
        }


    }
    public Student findById(Integer id){
        Connection connection = null;
        PreparedStatement preparedStatement =null;
        ResultSet resultSet = null;
        Student student = null;
        try {
            connection = JDBCTools.getConnection();
            String sql = "select * from student where id = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,id);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                Integer id1 = resultSet.getInt(1);
                String name = resultSet.getString(2);
                Double score = resultSet.getDouble(3);
                Date date = resultSet.getDate(4);
                //上面把数据已经取出来了,然后下面直接把他们封装起来就可以了
//                Student student = new Student(id,name,score,date);
                student = new Student(id1,name,score,date);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCTools.release(connection,preparedStatement,resultSet);
        }
        return student;
    }
    public void update(Integer id, String name,Double score){
        Connection connection= null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCTools.getConnection();
            String sql = "update student set name = ?,score = ? where  id = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,name);
            preparedStatement.setDouble(2,score);
            preparedStatement.setInt(3,id);
            preparedStatement.executeUpdate();
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            //没有参数就传输null啊,然后再release里面判断不是null就不关
            JDBCTools.release(connection,preparedStatement,null);

        }
    }
}

增加元素的jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/student" method="post">
        编号:<input type="text" name = "id"/></br>
        姓名:<input type="text" name = "name"/></br>
        成绩:<input type="text" name = "score"/></br>
        <input type = "hidden" name="method" value="add"/>
        <input type="submit" value="提交">
    </form>
</body>
</html>

更新元素的jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/student" method="post">
        编号:<input type="text" name = "id" value="${student.id}" readonly/></br>
        姓名:<input type="text" name = "name" value="${student.name}" /></br>
        成绩:<input type="text" name = "score" value="${student.score}"/></br>
<%--
        这个是表单传输一些参数的方式和<a href="/student?method=update'>是一样的
        同时这个input标签是隐藏起来的用户是看不到的
  --%>
        <input type="hidden" name="method" value="update" />
        <input type="submit" value="修改" />
    </form>
</body>
</html>

26 DBUtils

DBUtils可以帮助开发者将数据集映射成java对象(首先要现在程序里面定义好对象实体,然后才能与数据库中的数据进行对应)

其对应关系完全取决于表里的结构以及实体的名字,比如有一个Student的表,表里面有(name、id、address),我可以用一个User实体(java写好的一个User类)去映射,User类里面有(name,id),那最后就可以返回name与id的数据,但是address将返回null。

1.导入jar包(commons-dbutils-1.7.jar)

queryRunner.query(connection,sql,这第三个参数就是ResultHandler)

public static Student findByDBUtils(Integer id){
  Connection connection = null;
  Student student = null;
  try {
    connection = dataSource.getConnection();

    //BeanHandler将查询结果封装成一个实体类
    //String sql = "select * from student where id = 30";
    //QueryRunner queryRunner = new QueryRunner();
    //student = queryRunner.query(connection,sql,new BeanHandler<>(Student.class));

     //String sql = "select * from student where id = ?";
    //QueryRunner queryRunner = new QueryRunner();
    //queryRunner.query除了在前三个参数以后,还定义了可变长参数,前面的?接在前三个参数后面传入
    //student = queryRunner.query(connection,sql,new BeanHandler<>(Student.class),id);

    //BeanListHandler将查询的结果集封装成一个List
    //String sql = "select * from student where id = 30";
    //QueryRunner queryRunner = new QueryRunner();
    //List<Student> list= queryRunner.query(connection,sql,new BeanListHandler<>(Student.class));

    //MapHandler将结果集封装成一个Map,这个Map里面,通过key可以获取到value(eg: map.get("id") 输出30)
    //(所以只能输入出一整条语句,比如这里,只能输出一个student的信息)
    //String sql = "select * from student where id = ?";
    //QueryRunner queryRunner = new QueryRunner();
    //下面这个Object是直接用常见类型的父类,这样可以保证常见类型都能过输出
    //Map<String,Object> map = queryRunner.query(connection,sql,new MapHandler(),id);
    //System.out.println(map.get("id"));

    //MapListHandler用list接收Map集合
    String sql = "select * from student";
    QueryRunner queryRunner = new QueryRunner();
    List<Map<String,Object>> list = queryRunner.query(connection,sql,new MapListHandler());
    for (Map<String,Object> map:list){
      System.out.println(map);    
    }  
 } catch (SQLException e) {
    e.printStackTrace();
  } finally {
    try {
      connection.close();    
    } catch (SQLException e) {
      e.printStackTrace();    
    }  
 }  return student; 
}
posted @ 2021-05-08 19:40  你是什么小东东  阅读(92)  评论(0)    收藏  举报