javaweb笔记

带有Servlet的webapp

开发步骤

第一步:在webapps目录下新建一个目录,起名crm(webapp的名字)
第二步:在webapp的根目录下新建目录:WEB-INF

注意:该名称为Servlet的规范规定,必须一致

第三步:在WEB-INF目录下新建classes

注意:这也是Servlet的规范

第四步 在WEB-INF下新建目录:lib

注意:这个不是必须的,用于导入jar包,但位置和名称需要符合规范

第五步在WEB-INF的目录下新建web.xml

web.xml是一个配置文件,,是必须的,描述了Servlet和请求路径之间的对照关系,该文件可以从其他的webapp中拷贝,没有手写的必要

第六步:编写java程序,该程序必须实现Servlet的接口

servlet接口不在JDK中,是Oracle提供的,是javaEE的规范中的一员

tomcat实现了servlet接口

在web.xml路径下注册servlet

向浏览器响应一段HTML代码

1
2
3
4
5
public void service(ServletRequest,ServletResponse response){
response setContentType("text/html;charset=UTK-8");
PrintWriter out = response.getWriter();
out.print(html代码);
}

在servlet中连接数据库,怎么做?

在Servlet中直接编写java代码即可(JDBC)

如何在集成开发环境中创建servlet程序?

  1. 新建一个empty project,并且取名

  2. 新建模块

    这个模块为普通的javase模块

    取名为servlet01

  3. 让该模块转变为javaee模块

    右键该模块,add Framework support(添加框架支持)

    添加javaee规范,选择web-application

    选择该框架支持,idea会自动生成符合servlet规范的webapp目录情况

    idea生成的web目录即使是webapp的根目录

  4. 编写servlet程序

    class studentServlet implements Servlet(Servlet爆红,且无法导入包)

    在project Sturcture处的Modules–>Dependencies下点击加号,添加依赖

    (+号中的library大概率的都是tomcat自带的lib中的jar包,选中此按钮为导入全部tomcat的lib中的jar包,或者可以同样点击+号选择jars or diectories 自行选择导入jar包

    在学完maven以后,好像也是可以通过maven的坐标 版本号 score 的定义来导入jar包)

  5. 在servlet中编写业务代码(在此时连接数据库)

  6. 在WEB-INF的目录下新建子目录lib(名字固定,大小写固定)并且以此链接mysql驱动jar包

    (感觉还是可以maven导入,目前还没试过)

  7. 在web.xml文件中完成studentServlet类的注册

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?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>studentServlet</servlet-name>
    <servlet-class>javaweb.servlet.studentServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>studentServlet</servlet-name>
    <url-pattern>/servlet/student</url-pattern>
    </servlet-mapping>
    </web-app>

  8. 给定一个html页面,在html页面中编写一个超链接,用户点击这个超链接,发送请求,tomcat执行后台的StudentServlet

    该文件不能放置在WEB-INF 目录中

    student.html文件的内容是

    a超链接处添加项目名称,此处为xmm

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>student page</title>
    </head>
    <body>
    <!--此处项目名使用的为xmm-->
    <a href="/xmm/servlet/student">student list</a>
    </body>
    </html>
  9. 让idea工具关联Tomcat(即将webapp部署到tomcat之中)

    相应步骤在j大学专业课sp课程中开头已经学习过了,不再赘述

    在弹出服务器设置参数中大多数不需要改动

    在Deployment中部署webapp

    Application context为/xmm (xmm是在web,xml中取的项目名)

  10. 在浏览器中打开,本次编写的网站为…/xmm/student.html

servlet对象的生命周期

  • 什么是servlet的生命周期

    一个servlet对象的出生到死亡的整个过程

  • servlet对象是由谁来进行维护 的?

    servlet对象的创建 ,方法调用,对象的最终销毁1,javaweb程序员都是无权干涉的

    servlet的生命周期是由tomcat(web server)全权负责 的

    tomcat服务器通常我们又称为:web容器(web container)

    由web容器来管理Servlet对象

  • 注意!!我们自己new的Servlet对象只是一个普通的servlet对象,并不受web容器管理

    web容器创建的servlet对象都会被放入一个集合中(HashMap)只有放置在容器中的servlet才会受到tomcat管理,自己创建的servlet对象不在该容器之中

  • 默认情况下在服务器启动的时候,servlet对象并不会被实例化(合理的),在用户发送请求之前提前创建出的servlet是明显的浪费。

    (可以通过设置空参构造进行确认)

  • 若想实现启动服务器时,就创建servlet对象,可以在web.xml中增加以下标签

    1
    <load-on-startup>0</load-on--startup>

    中间的数字代表的是启动服务器时候创建servlet的顺序,越小优先级越高

  • 用户在发送第一次请求的时候servlet对象被实例化(使用的是无参构造)

    Aservlet对象被创建出来之后,tomcat服务器马上调用了Aservlet对象的无参构造

    无参数构造方法和init方法只会请求一次

    这说明,servlet对象是单实例,但是Servlet类不是单例模式,称为假单例(这是因为Servlet是tomcat创建的,与javaweb程序员无关)

    并且每次发出申请,service就会执行一次

  • servlet的destroy方法只会被tomcat服务器调用一次,然后servlet对象就会被销毁

    destroy方法执行的时候,实例对象还在,没有被销毁,当方法结束后servlet对象才会被销毁

Servlet

一般不建议程序员使用servlet不要去使用构造方法,因为可能导致无参构造方法消失,这个操作可能导致Servlet对象无法实例化

通常在init方法中使用初始化

使用适配器

config是tomcat创建出来并且进行传递

ServletConfig

  • servletconfig是servlet规范中的一员

servletconfig是一个接口

?是谁去实现了这个接口?

  • Tomcat服务器实现了ServletConfig接口

  • 一个Servlet对象中就有一个ServletConfig对象,这俩对象是一对一的

  • ServletConfig对象是Tomcat服务器(WEB服务器)创建的,在Servlet创建的时候同时创建ServletConfig对象

  • ServletConfig的作用

    configuration:配置,ServletConfig对象被翻译为Servlet对象的配置信息对象

    ServletConfig对象中包装的信息是web.xml文件中标签中的配置信息

    tomcat解析web.xml文件,将web.xml文件中标签中的信息自动包装到ServletConfig对象之中

    1
    2
    3
    4
    5
    public String getInitParameter(String name);//通过初始化参数的name,获取value
    public Enumeration<String> getInitParameterNames();//获取所有初始化的参数的name
    public ServletContext getServletContext();//获取ServletContext对象
    public String getServletName();
    //获取Servlet的名字

学不懂,他妈的

HTTP协议

  • 什么是协议?

    协议实际上是某些人或者某些组织提前制定好多的一套规范

    协议就是一套规范,就是一套标准,由其他人或者组织进行制定

  • 什么是HTTP协议?

    http协议是W3C制定的一种超文本传输协议。

    超文本是不仅支持传输普通字符串,

    B和S之间相互传输数据需要遵循HTTP协议

  • 什么是解耦合?

    B和S之间不相互依赖

    HTTP的请求协议(B–>S)

    HTTP的请求协议包括4部分

    • 请求行
    • 请求头
    • 空白行
    • 请求体
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    GET /servlet05/getServlet?username=jack&userpwd=123 HTTP/1.1	//请求行
    //请求头(本身没有这个换行)
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
    Connection: keep-alive
    Cookie: Idea-75fd4672=73a4fda9-20c5-4f1a-9192-a68c6d64678a
    Host: localhost:8080
    Referer: http://localhost:8080/servlet05/index.html
    Sec-Fetch-Dest: document
    Sec-Fetch-Mode: navigate
    Sec-Fetch-Site: same-origin
    Sec-Fetch-User: ?1
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.55
    //...

    `

怎么发送GET和POST请求?

到目前为止,只有一种情况可以发送POST请求:使用form表单,并且form标签中的method属性值为:method=“post”

其他所有情况一律都是get请求:

在浏览器地址栏上直接输入URL,回车,属于get请求。

在浏览器上直接点击超链接,属于get请求。

使用form表单提交数据时,没写method,默认就是get

或者使用form的时候,form标签中method属性值为:method=“get”

GET请求和POST请求怎么选择?

get请求比较适合从服务器端获取数据
post请求比较适合向服务器端传送数据
大部分的form表单提交,都是post方式
敏感信息用post请求
文件上传,一定是post请求
get请求和post请求发送的请求数据格式是统一的,只是位置不同

后端和前端发送请求需要一致

避免405错误,doGet和doPost选择其一进行重写

不建议两种方法全部重写,405错误是有意义的

#一个Servlet类的开发步骤:

  1. 编写一个Servlet类,直接对HttpServlet进行继承

  2. 重写doGet方法或者重写doPost方法,选择重写谁,由程序员决定

  3. 将Servlet类配置到web.xml文件之中

  4. 准备前端的页面(form表单),form表单中指定请求的路径即可

怎么选择web欢迎页面?

  1. 在web.xml文件下配置以下内容

    1
    2
    3
    <welcome-file-list>
    <welcome-file>Login.html</welcome-file>
    </welcome-file-list>

    Login.html设置为所设置的web欢迎页面

    web欢迎页面不需要以/开头

    如果所需要设置的web欢迎页面,是文件夹下的则所选取的file是

    文件夹名/../../Login.html

    依旧不需要以/开头,路径默认在web.app下进行寻找

  2. webapp可以设置多个欢迎页面,靠上的优先级高,找不到的往下面找。

  3. tomcat服务器已经提前配置文件好了index.xml的欢迎页面

    即默认选择index.html,index.htm,index.jsp文件

关于WEB-INF目录

在WEB-INF目录下面创建一个文件,

WEB-INF目录下的文件是受到保护

HttpServletRequest接口

是Servlet规范中的一员

该接口 的父接口 是Servletrequest

HttpServletRequest对象是tomcat服务器负责创建的,这个对象中封装了HTTP的请求协议,

  • 实际上是用户发送请求的时候,遵循了HTTP协议,将HTTP协议中的信息以及数据全部解析出来,然后tomcat服务器把这些信息封装到HttpServletRequest对象之中,传给了javaweb程序员。
  • javaweb程序员面向HttpServletRequest接口编程,调用方法就可以就可以获得请求的信息了
  • request和response对象的生命周期?
    • request对象和response对象,一个请求对象,一个响应对象,这俩对象只在当前请求 中有效(即是一次请求对应一个request,2次则对应2个)
HTTPServletRequest接口中的常用方法
  • 怎么获取前端浏览器用户提交的数据?

    • ```java
      Map<String,String[]>getParameterMap()
      //这个是获取map
      EnumertionggetParameterName()
      //这个是获取Map集合中所有的key
      String[] getParameterValues(String name)
      //这是根据key获取Map集合中的value
      String getParameter(Sring name)
      //获取value这个一维数组中的第一个元素,因为大多数value数组中只会有一个值,所以这个方法最常用
      //以上四个方法,和获取用户提交的数据有关系
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17



      - 前端提交的数据格式:username=abc&userpwd=1234&hobby=s&hobby=d

      - 采用Map集合来存储:

      ```null
      Map<String.String>
      key存储String
      value存储String
      但这种方式是不对的
      因为如果采用以上的数据结构存储会发现key重复的时候value覆盖,比如hobby=s,然后当下一条数据进入的时候hobby就会变成hobby=d,被覆盖了
      key存储String
      value存储String[]
      key使用单个,value使用数组进行存储

  • 前端表单提交数据的时候,假设提交了数字,其实是以字符串的方式进行提交的,所以服务器端获得一定是一个字符串

应用域对象

ServletContext(Servlet上下文对象)是应用域对象

什么时候会考虑使用该应用域对象?

  • 第一:所有用户共享数据
  • 第二:这个共享的数据量很小
  • 第三:这个共享的数据很少进行修改操作
  • 在以上3个条件都满足的时候,使用应用域对象,可以大大提升我们的程序的运行效率。
  • 实际上向应用域中绑定数据,就相当于把数据放在了缓存之中。

已经见过的缓存技术?

  • 字符串常量池
  • 整数型常量池
  • 数据库连接池
  • 线程池
  • 后期还会学习,redis、mongoDB…

ServletContext当中有三个操作域的方法:

1
2
3
4
5
6
7
void setAttribute(String name,Object obj);//向域中绑定数据
Object getAttribute(String name);//从域当中根据name获取数据
void removeAttribute(String name);//将域中绑定的数据进行删除

//以上的操作都是类似Map集合的操作
Map<String,Object>map;
map.put("name",obj);

请求域对象

请求域只在应用域对象范围小很多

一个请求对象request对象对应一个应用域对象

能不能将AServlet和BServlet?

答案是可以的,使用Servlt当中请求转发机制。

request对象的常用方法

1
2
3
4
5
//第一步
request.getRequestDispather("");
//参数填写路径,实际上就是把跳转的资源路径告知tomcat
//第二步
dispatcher.forward(request,respnse);
  • 第一步:转发

  • 第二步:调用转发器的forward方法进行跳转

  • 第一步和第二步代码可以链接在一起

    • ```java
      request.getRequestDispatcher(“/b”).forward(request,response);
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20

      - 这样就可以保证ABservlet中的requestresponse对象是同一个对象

      - 俩个Servlet怎么共享数据?

      - 将数据放在ServletContext应用域中,当然是可以的,但是占用资源过多,是不建议的
      - 可以将数据放置在request域中,然后由AServlet转发给B,并且保证AB在同一次请求当中,这样就可以做到多个Servlet共享同一份数据

      - 转发的下一个资源必须是Servlet吗?

      - 不一定,只要是TOmcat当中的合法资源,都是可以进行转发的,例如html...
      - 转发的时候,路径的写法要注意,转发的路径以/开始,不需要加上项目名.

      - request对象容易混淆的俩个方法:

      ```java
      //有传入的username=zhangsan&userpwd=123&sex=1
      String getParameter("name");

      //之前一定执行过:request.setAttriibute("name",new Object());

    这俩方法的区别是

    • 第一个方法从表格表单中获取用户提交的数据

    • 第二个方法是获得请求域中绑定的数据

      还有一个常见的方法是

      1
      2
      request.getRemoteAddr();
      //获取客户端IP地址

HttpServletRequest接口的其他方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 获取客户端的IP地址
String remoteAddr = request.getRemoteAddr();

// 设置请求体的字符集(处理POST请求的乱码问题,不能解决get请求的乱码问题)
request.setCharacterEncoding("UTF-8");

// Tomcat9和9以前,解决响应乱码
response.setContentType("text/html;charset=UTF-8");

// get请求乱码怎么解决
// 方案:修改CATALINA_HOME/conf/server.xml配置文件
<Connector URIEncoding="UTF-8" />
// 注意:从Tomcat8之后,URIEncoding的默认值就是UTF-8,所以GET请求也没有乱码问题了。

// 获取应用的根路径
String contextPath = request.getContextPath();

// 获取请求方式
String method = request.getMethod();

// 获取请求的URI
String uri = request.getRequestURI(); // /aaa/testRequest

// 获取servlet path
String servletPath = request.getServletPath(); // /testRequest

莫名其妙上课学会的新方法!!!

1
2
3
4
5
String newStr = new String(
userName1.getBytes("ISO-8859-1"),
"UTF-8"
);
//也能解决request传来的String中文乱码错误

使用纯Servlet做一个单表的CRUD操作(OA)

  • 使用纯粹的Servlet完成单表的增删改查

  • 实现步骤

    • 第一步:准备一张数据库表

    • 第二步准备一套HTML页面(项目原型)

      • 欢迎页面:index.html
      • 列表页面:list.html(以列表页面为核心,展开其他页面)
      • 新增页面:add.html
      • 修改页面:edit.html
      • 详情页面:detail.html
    • 第三步:确定需要哪些功能?

      只要这个操作链接了数据库,该操作就叫做功能

      • 查看部门列表

      • 新增部门

      • 查看部门信息

      • 删除部门

      • 跳转到修改页面

      • 修改部门

    • 第四步:在idea中搭建开发环境

      • 创建一个webapp(导入各种奇怪的jar包)

      • 向webapp中添加连接数据库的jar包(mysql驱动)

        • 必须在WEB-INF目录下新建lib目录,然后将mysql的驱动jar包拷贝到这个目录下,该目录必须叫做lib(全部小写)
      • JDBC的工具类(未来会使用mybatis)

    • 第五步:实现查看部门列表功能

      • 实现功能最好从前端一步步往后端写,或者从后端一步步往前端写,切忌想到什么写什么

        即写代码的过程最好是程序执行的过程,这样可以尽量避免出现错误

      • 假设从前端开始:

      • 第一:先修改前端超链接,用户从这里开始操作

        1
        2
        <a href="/oa/dept/list">查看部门列表</a>
        //从项目名开始写
      • 第二:编写web.xml文件

        1
        2
        3
        4
        5
        6
        7
        8
        9
        <servlet>
        <servlet-name>list</servlet-name>
        <servlet-class>top.xgfm737.oa.DeptListServlet</servlet-class>
        </servlet>
        <servlet-mapping>
        <servlet-name>list</servlet-name>
        <!--web.xml文件的路径也是/开头,但是不需要加项目名-->
        <url-pattern>/dept/list</url-pattern>
        </servlet-mapping>
      • 第三:编写DeptListServlet类继承HttpServlet类,然后重写doGet方法。

        注:因为超链接是get请求(我才知道,我是fw)

      • 第四:在DeptListServlet类的doget方法中连接数据库,查询部门,并且动态的进行输出

    • 第六步:实现详情功能(查看部门详情)

      • 建议从前端往后端一步步进行实现

      • 找到所点击的详情在哪,连接数据库然后执行代码

      • 使用

        1
        String contextPath=request.getContextPath();

        获取根路径

        向服务器提交数据的格式:url?name=value&name=value

      • 配置web.xml文件

      • ```xml

        detail top.xgfm737.oa.web.action.DeptDetailServlet detail /dept/detail
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35



        - 编写一个DeptDetailServlet类,重写doget方法

        - 获取部门编号等动态输出

        - 第七步:删除部门

        - 使用js脚本加入到deptListServlet之中
        - 再发送相对应的get请求,启动DeptDeleteServlet,然后删除相应数据,在成功后跳转回主界面,如果失败,则跳转至error页面

        - 第八步:新增部门

        - 在新增部门添加超链接至add.html,并在该页面中通过form表单的post传递数据给DeptAddServlet页面,然后成功实现跳转回到主界面,如果失败则跳转至error页面

        注意,第七步第八步建议开始事务步骤,通过回滚事务来避免数据的错误

        - 第九步:修改部门

        - 在修改页面获取到想要修改的部门的信息,在文本框中输出,然后修改
        - 在表单修改后的信息通过post请求发送到DeptModifyServlet处
        - 然后在DeptModifyServlet中运行,方法类似增加部门,只是修改sql语句
        - 然后成功和失败的跳转

        # 重定向和请求分派(转发)

        - 代码!

        - 转发

        ```java
        request.getRequestDispatcher("/dept/list").forward(request,response);
        //转发是一次请求,
        //无论转发多少次,显示的都是第一次的Servlet的地址
    • 重定向

      1
      response.sendTedirect(“oa/dept/list”);

      发送两次请求,每次重定向都会重新发送请求

    • 转发是由WEB服务器来控制的,A资源跳转到B资源,这些跳转动作是Tomcat 服务器内部完成的

    • 重定向是由浏览器完成的,具体跳转到哪个资源,是浏览器说了算的

  • 转发和重定向应该如何选择?什么时候使用转发,什么时候使用重定向?

注解

在开发中一般使用注解和web.xml双开发

比如@WebServlet,WebServlet注解中有哪些属性?

  • name属性:使用指定的Servlet的名字,等同于<servlet -name>
  • urlpatterns属性:用来指定Servlet的映射路径。可以指定多个路径
  • loadonstartup属性:用来指定在服务器启动阶段是否加载该Servlet,等用于

JSP

JSP实际上就是一个Servlet,index.jsp访问时候,会自动翻译生成index.jsp.java,该类继承HttpJspBase,而HttpJspBase类继承的是HttpServlet,所以说jsp实际上就是一个Servlet类

而JSP的生命周期与Servlet的生命周期完全相同,没有任何区别。

jsp和Servlet都是单例(假单例)

  • JSP第一次访问的时候是比较慢的,因为需要先编译JSP文件
  • 执行顺序为:
    • jsp文件翻译为java源文件
    • java源文件编译生成class字节码文件
    • 通过class去创建servlet对象
    • 调用servlet中的init方法
    • 最后调用servlet对象中的service方法
  • JSP是什么
    • jsp是java程序
    • jsp是JavaServer Page的缩写
    • Servlet是JavaEE的13个规范
    • 所以JSP也是一套规范
  • 对JSP进行错误调试的时候,还是要直接打开JSP文件对应的java文件,检查java代码

JSP基础语法

  • <%java语句%>

    • 是在方法体中写代码

    • service方法中编写的代码是有顺序的,是逐行执行的(从上至下进行运作)

  • <%–JSP的注释,并且不会被翻译到翻译到java源代码之中–%>

  • <%! 声明 %>

    • 可以是全是变量,也可以是静态方法等等
  • <%=输出在浏览器上的数据%>

    • 该语句等同于out.print();

JSP和Servlet的区别

jsp和Serlvet本质是一致的

但是jsp是用于展示数据

Servlet是用于收集数据

使用JSP优化OA

  • 优化list页面

    • 使用jsp展示前端页面
  • 在servlet中使用JDBC获取结果集RS

    • 编写JavaBean,编写相应的方法
  • 在servlet中将rs存入所编写的JavaBean(Dept)集合(Depts)中

    • 使用request.setAttribute(String name,Object obj);存入该集合
  • 然后在servlet中转发到jsp页面

    • 在jsp页面中用getAttribute(String name);方法,并且用集合类型接住
  • 然后在jsp中通过while循环输出

  • 优化add

  • 优化!!!

当前的OA存在的问题

任何用户都可以访问,这是不安全的

所以我们要添加一个用户登录

  • 步骤1 :数据库中添加一个用户数据表
  • 步骤2:实现登录页面

session对象(会话)

什么是会话?

  • 从打开浏览器到关闭,生成一次会话

一个用户拥有一个独立的session对象,用cookie进行标记

request<session<application

  • session具有超时机制,超过多少时间session没有被访问,那么该session将会被删除
  • session对象是存储在服务器端的,一个session包括多个请求。

session的实现原理

在web服务器中有一个session列表,存储相对于的map集合,在map集合中存储的key是sessionID

  • 第一次发送请求的时候,服务器会创建一个新的session对象,同时给session对象生成一个id,然后存储到web服务器中。
  • 第二次发送请求和在session未销毁的之前发送请求的时候,会将浏览器缓存中的sessionid自动发送给服务器,服务器获取到sessionid,然后在session列表中寻找相对应的session对象

一次会话包涵完整的session对象的生命周期。

session对象的销毁是依靠超时机制和手动销毁的

  • 因为tomcat无法知道你是否关闭浏览器(因为HTTP协议是无状态协议)

JSESSIONID=xxxxxxxxxx 是以cookie的形似存储在浏览器之中的,浏览器只要关闭,这个cookie就会关闭,内存就会消失,会话等同消失。

cookie禁用

如果禁用cookie(即服务器拒收cookie),每次访问(发送请求)的时候都会生成一个全新的session和与之对应的cookie,之前的cookie和session都会消失,然后因为会话超时消失。

session默认超时时间为30分钟

如果cookie禁用,session机制还能使用?

可以,需要使用URL重写机制

  • 该机制会严重提高开发者的成本,给开发带来了很大的难度,极大的成本,每个超链接后面都要加上url路径

    1
    <a>/test/session;jsessionid=XXXXXXXXXXXX</a>

用户登录功能有效化

将用户信息加入到session中

1
2
3
4
5
6
7
HttpSession session =request.getSession(false);
//参数使用false的话,未找到session不会去创建session
//如果之前并没有创建过session对象,参数最好填写true,不然在setAttribute处容易导致空指针异常
//(登录成功后放入session)
session.setAttribute("username",username);

//在deptServlet之中添加一个if判断,session中有登录信息就允许跳转,否则一律跳转至login页面

在退出登录的servlet下添加销毁session对象的代码

1
session.invalidate();

Cookie

session的实现原理中,每个session对象都关联一个sessionid

JSESSIONID的键值对数据其实就是cookie对象

对于ssesion关联的cookie来说,这个cookie是被保存在浏览器的运行内存中的

只要浏览器不关闭,用户再次发送请求的时候,会自动将运行内存中的cookie发送给服务器

cookie最终保存在浏览器客户端上

  • 可以保存在运行内存中(浏览器关闭后cooki就消失了)
  • 也可以保存在硬盘文件中(永久保存)

cookie有啥用呢?

  • cookie何session机制其实都是为了保存会话的状态
  • cookie是将会话的状态保存在浏览器上

如果cookie清除,那么所有的记录也会跟着消失

  • 登录前的数据存储在cookie上,而登录后的各种数据存储在数据库中

十天内免登录:

该功能也是需要cookie来进行实现的,并且同时选择十天内免登录,登录成功后,浏览器客户端会保存一个cookie,这个cookie保存快来用户名和密码,并且该cookie是保存在硬盘文件当中的,十天有效,在十天内用户再次访问网站的时候,浏览器自动提交cookie给服务器,然后验证,登录

cookie和session机制都是http协议的机制,并不是Java的机制

java的servlet,对cookie提供了哪些支持?

提供了javax.servlet.http.Cookie

cookie在HTTP协议中是这么规定 :当浏览器发送请求的时候,会自动携带该path下的cookie

  • cookie的有效时间

    cookie。setMaxAge(60*60);设置cookie在一小时后关闭

  • 没有设置有效时间,则默认存储在浏览器的运行内存之中,浏览器关闭则cookie消失

  • 只要cookie的有效时间>0,这个cookie一定会存储到硬盘文件当中

  • cookie的有效时间为0,表示该cookie被删除,主要用在删除浏览器中的同名cookie

  • cookie的有效期<0,那么该cookie不会被存储在硬盘文件当中,存储在运行内存当中,和不使用setMaxAge是一个效果

    1
    2
    3
    4
    Cookie cookie = new Cookie("productid","13572468");		//创建cookie
    cookie.setMaxAge(60*60); //设置cookie的有效时间(秒)
    cookie.setPath(“/servlet13”); //手动设置cookie的path
    response.addCookie(cookie); //向浏览器发送cookie
1
2
3
4
5
6
7
Cookie[] cookies = request.getCookies(); // 这个方法可能返回null
if(cookies != null){
for(Cookie cookie : cookies){
String name = cookie.getName();
String value = cookie.getValue();
}
}

JSP九大内置对象

JSP为了方便使用,在加载时就创建九大内置对象

九大内置对象:request、response、session、application、page、pageContext、out、config、exception

pageContext<request<session<application

以上四个作用域都有:setAttribute,getAttribute,removeAttribute方法

以上四个作用域的使用原则是尽量使用小的域

  1. request对象(请求作用域)
  2. session对象(会话作用域)
  3. application对象(应用作用域)
  4. pageContext对象(页面作用域)
  5. response对象
  6. out对象
  7. page对象(其实就是this,当前的servlet)
  8. config对象
  9. exception对象

JSP四大作用域

四大作用域:pageContext、request、session、application

JSP的指令

指令的作用是指导当前jsp翻译引擎工作

JSP指令

  • include指令:包含指令
  • taglib指令:引入标签库指令,与JSTL标签库息息相关
  • page指令:

指令的使用语法

<%@指令名 属性名=属性值 属性名=属性值 属性名=属性值 ……%>

关于page指令的常用属性

1
2
3
<%page session="true|false"%>
//true or false表示是否启用JSP的内置对象session
//该指令的默认值是true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@page contentType="text/json" pageEncoding="UTF-8"%>
contentType属性是用来设置响应的内容类型,内容也可以是“text/html"
pageEncoding用力啊设置响应时采用的字符集
//说实话感觉都是默认的属性,没必要进行修改
<%@page contentType="text/json;charset=UTF-8"%>
//这样就是同时设置,将2个page指令合并,效果是一致的
<%@page import=""%>
//import导包,和java中的import导包效果一模一样
<%@page errorpage="/error.jsp"%>
//出现错误之后,跳转至error.jsp页面,设置错误跳转页面
<%@page isErrorPage="true"%>
//这个是errorpage是否的判定。与errorpage配套使用
//同时这也是表明启用JSP的九大内置对象
<%
exception.printStackTrace();
%>
//exception是jsp的九大内置对象,通过这个语句打印异常栈信息,输出到后台控制台,也是与errorpage和isErrorPage配套使用的

EL表达式

EL表达式的作用

  • Expression Language(表达式语言)

  • EL表达式可以替代JSP中的java代码,让JSP文件中的程序看起来更加整洁,美观

  • JSP代码中夹杂着各种java代码导致JSP文件很混乱(后来通过前后端分离解决)

  • EL表达式可以算是JSP语法的一部分,EL表达式归属于JSP

  • EL表达式出现在JSP中主要是:

    • 从某个域中取出数据

      四个域

      • pageContext

      • session

      • request

      • application

    • 将取出的数据转换成字符串

    • 将字符串输出到浏览器

  • EL表达式十分简便,基本语法格式:

    ${}

    举例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <%
    request.setAttribute("username","zhangsan");
    %>

    //该输出语句可以替换为下面的EL表达式
    <%=requst.getAttribute("username")%>


    //与之等效的EL表达式是这样的
    ${username}
  • EL表达式的使用

    数据需要存储到4大范围之一

    然后使用EL表达式获取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //首先需要编写User类
    <%
    User user =new user();
    user.setUsername("123");
    user.setPassword("123");
    user.setAge(123);
    session.setAttribute("userObj",user);
    %>
    //{}中写存储到域对象中的name,并且千万不要使用双引号,这会导致直接输出该字符串
    ${userObj}
    //输出该实体对象的地址

    ${userObj.name}
    //输出user的name:123
    //该输出需要有相互对应的get方法

    ${userObj["name"]}
    //这样写与上一个等效
    //[]里面如果没有双引号会将其看做变量,如果是带双引号的,则回去userObj的数据中寻找name属性

    ${abc}
    //输出存储到域对象中name为abc的数据
    ${"abc"}
    //输出abc
  • 如果四个作用域中有重名的存储数据,根据应用域优先级从小到大取出数据

  • 如果不想逆优先级取出数据可以使用如下代码

    1
    2
    3
    4
    ${pageScope.data}
    ${requestScope.data}
    ${sessionScope.data}
    ${applicationScope.data}

    实际开发时,存储的name尽量不要重复,极容易导致获取的数据出现问题,所以xxxScope都是可以省略的

  • EL表达式如果从四个域中都找不到name对应的数据,会什么也不显示,这样比较友好

  • ${user[“”]}

    这个中间的中括号可以有效的避免user的名字中间带.号的时候,避免识别错误

    数据实体对象带点

    比如${user.a.abc}

    这样是无法取值的

  • 掌握EL表达式,怎么从Map集合中取数据:

  • 掌握EL表达式,从数组中获取数据:

    1
    2
    3
    4
    5
    String[] user={....};
    request.setAttribute("nameArray",user);

    ${nameArray[index]}
    //并且此处的index不会触发数组索引越界异常,十分高效
  • EL表达式严禁{}中有多个.号,这会导致读不出数据

  • EL表达式中的pageContext对象是隐式的对象,pageContext

  • 并且request.getContextPath

    可以在EL表达式中写pageContext.request.contextPath

关于EL表达式的运算符

  1. 算术运算符

    +-*/%

  2. 关系运算符

    == != > >= < <= eq

  3. 逻辑运算符

    ! && || not and or

  4. 取值运算符

    [] .

  5. empty运算符

算数运算符

  • +号只能加做为运算符,不能作为字符串拼接

  • 如果是强行加上字符串,会报500错,数字格式化异常

关系运算符

  • 只能出现在el表达式

  • EL表达式中其他的隐式对象:

      • pageContext
      • param
      • paramValues
      • initParam
  • empty运算符的结果是Boolean类型的

JSTL标签库

  • 什么JSTL标签库?

    • Java Standard Tag Lib(java标准标签库)
    • JSTL标签库通常和EL表达式一起使用,目的是让JSP中的java代码消失
  • 使用JSTL标签库的步骤

    1. 引入JSTL标签库对应的jar包

    2. 在idea中如何引入

      新建lib目录然后add as lib

      一定是要和mysql数据库jar包一样

      或者可以使用maven导入

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
      </dependency>

      <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
      </dependency>
    3. 使用tablib指令引入标签库

      JSTL提供了很多种标签,你要引入哪个标签需要自己选择,重点掌握核心标签库!

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

      //prefix= 这里写什么都可以,一般都写c
      别的有的用fmt,sql 一般都是首字母这样的进行取名
    4. 在需要使用标签的位置使用。表面使用的是标签,底层实际上还是java代码

    5. JSTL标签的原理

      1
      2
      3
      4
      5
      6
      <%@tablib prefix="c" uri="http://java.su.com/jsp/jstl/core"%>
      //以上uri后面的路径实际上指向了一个xxxt.tld文件
      //tld文件实际上是一个xml配置文件
      //在tld文件中描述了标签和java类之间的关系
      //在以上核心标签库对应的tld文件是c.tld文件
      //存在jar包的META-INF目录下
  • core举例

    • if标签
    1
    2
    3
    <c:if test="${status==1}">
    启动
    </c>

    test中添加if判断,如果test判断为正,输出C标签中的东西

    • foreach标签

      相当于for循环

    1
    2
    3
    4
    5
    6
    <c:forEach items="${brands}" var ="brand">
    ${brand.id}<br>
    ${brand.brandName}<br>
    ${brand.companyName}<br>
    ${brand.description}<br>
    </c>
    • items:被遍历的容器
    • var:被遍历的临时变量
    • begin:开始数
    • end:结束数
    • step:步长

Filter过滤器

各种servlet都是执行自己的业务这些servlet执行前都要判断用户是否要登录,如果用户登录了,可以继续操作,如果没有登录,需要用户登录,而这步骤很多时候都重复的,显然代码没有得到重复利用

这时候就需要用到过滤器了

我们可以使用过滤器来添加过滤代码,这个过滤代码可以添加到Servlet执行前,或者之后进行过滤。Servlet和Filter实际上也都是java程序

过滤器和用户的请求路径相关,目的也就是提升复用性

目标Servlet是否执行,取决于两个条件:

第一:过滤器是否编写chian.doFilter(request,response);

第二:用户请求路径是否和Servlet的请求路径一致

chain.doFilter(requset,response);这行代码的作用是:

  • 执行下一个过滤器,如果下面没有过滤器,那么就执行最终的Servlet
  • 注意:Filter的优先级,天生就比Servlet的优先级高
  • /a.do对应一个Filter也会对应一个Servlet,那么一定是先执行Filter,再执行Servlet

关于Filter的配置路径:

  • /a.do、/b.do、/dept/save 这些配置方式都是精确匹配
  • /*匹配所有路径
  • *.do 匹配后缀,不需要以/开始
  • /dept/* 匹配前缀

在web.xml文件中进行配置的时候,Filter的执行顺序是什么?

  • 依靠filter -mapping标签的配置位置,越靠上优先级越高

过滤器的调用顺序,遵循栈数据结构

1
2
3
4
5
6
7
8
<filter>
<filter-name>filter2</filter-name>
<filter-class>com.bjpowernode.javaweb.servlet.Filter2</filter-class>
</filter>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>

使用webFilter的时候,filter的执行顺序是什么样的呢?

  • 执行顺序是比较Filter的类名
  • 比如:FilterA和FilterB,则先执行FilterA
  • 比如:Filter1和Filter2,则先执行Filter1

Filter的生命周期

  • 和Servlet对象的生命周期一致,唯一的区别就是Filter会在tomcat启动时候实例化

Filter过滤器这里有一个设计模式:

  • 责任链设计模式
  • 优点可以在程序运行阶段,动态的修改web.xml文件,动态的组合程序的调用程序

在编译阶段已经完全确定了调用关系。

  • 如果你想改变他们的调用顺序,必须修改以下java源代码。
  • java代码修改,需要重新编译,项目需要重新测试,项目需要重新发布。这是一个繁琐的过程。
  • 显然,这种设计违背了:OCP原则。(开闭原则)
  • 开闭原则:对扩展开放,对修改关闭。
  • 对项目扩展我没有意见,但是你扩展的过程中如果修改了我之前写好的代码,这就是你的不对了。
  • 最好要达到的效果是,可以扩展,但是最好别改我之前写好的代码。

责任链设计模式:

  • 在程序运行阶段,动态的调整程序的运行顺序,过滤器一般配置在xml中,因为这个能够更好地进行改动

Listener监听器

  • 什么是监听器?

    监听器是Servlet规范中的一员。就像Filter一样。Filter也是Servlet规范中的一员。

    在Servlet中,所有的监听器接口都是以“Listener”结尾。

  • 监听器有什么用?

    监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。

    特殊的时刻如果想执行这段代码,你需要想到使用对应的监听器。

  • Servlet规范中提供了哪些监听器?

    jakarta.servlet包下:

    ServletContextListener

    ServletContextAttributeListener

    ServletRequestListener

    ServletRequestAttributeListener

  • jakarta.servlet.http包下:

    HttpSessionListener

    HttpSessionAttributeListener

    • 该监听器需要使用@WebListener注解进行标注。

    • 该监听器监听的是什么?是session域中数据的变化。只要数据变化,则执行相应的方法。主要监测点在session域对象上。

    HttpSessionBindingListener

    • 该监听器不需要使用@WebListener进行标注。

    • 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。

    • 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。

    HttpSessionIdListener

    • session的id发生改变的时候,监听器中的唯一一个方法就会被调用。
  • HttpSessionActivationListener

    • 监听session对象的钝化和活化的。
    • 钝化:session对象从内存存储到硬盘文件。
    • 活化:从硬盘文件把session恢复到内存。

实现一个监听器的步骤:以ServletContextListener接口,并且实现其中的方法

  • 第一步:编写一个类实现ServletContextListener接口,并且实现里面的方法

    • ```java
      void contextInitialized(ServletContextEvent event)
      void contextDestoryed(ServletContextEvent event)
      1
      2
      3
      4
      5
      6
      7

      - 第二步:在web.xml文件中对ServletContextListener进行配置,如下:

      - ```xml
      <listener>
      <listener-class>xgfm737.javaweb.MyServletContextListener</listener-class>
      </listener>
  • 思考一个业务场景:

    编写一个功能,记录该网站实时的在线用户的个数

    我们可以通过服务器端有没有分配session对象,因为一个session对象就代表了一个用户,那个就可以通过HttpSessionListener来实时监测用户个数

    方法:在user类中增加HttpSessionBindingListener 监听器,监听User实体对象是否创建,并且在重写的监听方法中增加aplication存入取出在线人数的变化,然后在list.jsp中通过EL表达式或者是get,<%=%>输出在线人数

完结完结咯


javaweb笔记
http://example.com/2023/03/22/javaweb/
作者
星光浮梦
发布于
2023年3月22日
许可协议