JavaWeb(二:Servlet与Jsp,监听器与过滤器)

JavaWeb(一:基础知识和环境搭建)icon-default.png?t=N7T8https://blog.csdn.net/xpy2428507302/article/details/140365130?spm=1001.2014.3001.5501

目录

五、Servlet

1.定义

2.创建一个HelloServlet

3.Servlet原理

4.servlet生命周期 

5.Servlet体系结构

6.Mapping问题 

7.ServletContext

(1)共享数据

​编辑

(2)获取初始化参数

(3)请求转发

(4)获取配置文件

8.HttpServletResponse

(1)简单分类

(2)常见应用

(3)重定向和转发的区别:

9.HttpServletRequest

获取前端传递的参数,请求转发

六、Cookie 和 Session

1.会话

2.实现会话的两种方式

3.Cookie

 4.Session

七、JSP

1.定义

2.原理

3.基础语法

(1)jsp表达式

(2)jsp脚本片段

(3)jsp声明

(4)jsp注释

4.jsp指令

(1)定制错误页面

(2)包含页面头部和尾部

5.九大内置对象 

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

(1)EL表达式:${}

(2)JSP标签:

(3)JSTL表达式:

 八、Filter过滤器

九、监听器​​​​​​​


五、Servlet

1.定义

Servlet 是Sun公司开发 动态web 的一门技术,负责与客户端进行通信。

Sun 在这些API中提供了一个接口叫做:Servlet

开发一个Servlet程序的步骤:

        ① 编写一个类实现Servlet接口

        ② 把开发好的Java类部署到web服务器中

Servlet:实现了Servlet接口Java程序

2.创建一个HelloServlet

① 构建一个普通的Maven项目,删掉里面的src目录

这个空的工程就是Maven主工程,以后就在这个项目里面建立Module。

关于Maven父子工程的理解:

父项目中会有:

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

子项目中会有:

<parent>
    <groupId>com.mihoyo</groupId>
    <artifactId>javaweb</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

父项目中的 jar 包,子项目可以直接使用(类似于java中的继承)

② maven环境优化

        Ⅰ. 修改web.xml为最新的。

        Ⅱ. 将Maven的结构搭建完整。(建立 java 文件夹)

③ 编写一个Servlet程序

Ⅰ.编写一个普通类

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

import java.io.*;
import jakarta.servlet.*;
import jakarta.servlet.http.*;

public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

注意:Tomcat10对servet依赖包级目录改变,maven导入依赖时应该导入:

<dependency>
    <groupId>jakarta.servlet.jsp</groupId>
    <artifactId>jakarta.servlet.jsp-api</artifactId>
    <version>3.0.0</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.0.0</version>
     <scope>provided</scope>
</dependency>

 所以,Servlet 程序中在导包时,也应导入 jakarta.servlet,而不是 javax.servlet。

 ④ 编写Servlet的映射

原因:

我们写的是 Java程序,浏览器不能直接访问 Servlet,只能通过映射的方式来间接访问 Servlet。

所有我们需要在web服务(web.xml)中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径。

<!-- 注册Servlet -->
<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.mihoyo.serlvet.HelloServlet</servlet-class>
</servlet>

<!-- Servlet的请求路径 -->
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

⑤ 配置Tomcat

⑥ 启动测试

3.Servlet原理

Servlet是由Web服务器调用,Web服务器在收到浏览器请求

4.servlet生命周期 

① 当浏览器访问 Servlet 的时候,Tomcat 容器会查询当前 Servlet 的对象是否存在:

        如果不存在,则通过反射机制动态创建对象

        如果存在,直接执行第 3 步

② 调用 init 方法完成初始化操作

③ 调用 service 方法完成业务逻辑操作

④ 当关闭 Tomcat 时,才会调用 destory 方法,释放当前对象所占用的资源。


Question:为什么在 web.xml 中配置servlet-class 的时候要给出全类名?

Tomcat 容器会根据全类名利用反射机制,创建该 servlet 的对象。


 Servlet 的生命周期方法:

① 无参构造函数:只调用一次,创建对象

② init 方法:只调用一次,初始化对象

③ service 方法:调用多次,执行业务逻辑。

④ destory 方法:只调用一次,卸载对象。

5.Servlet体系结构

Servlet -- 接口

|| 继承

GenericServlet -- 抽象类

|| 继承

HttpServlet -- 抽象类

GenericServlet:

        将Servlet接口中屏蔽了不常用的方法(做了默认空实现),只将service 方法作为抽象

        使用定义Servlet类时,可以继承GenericServlet,子类只要重写 service 方法即可

HttpServlet:

        根据请求类型进行分发处理,将 service 方法划分为 Get 和 Post 请求方法

        使用时定义类继承HttpServlet,复写 doGet/doPost 方法

6.Mapping问题 

一般情况下,一个Servlet 指定一个映射路径 。

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

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

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

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

* :通配符,表示任意字符串

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

默认的请求路径

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

指定一个后缀或者前缀等等……

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

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

<!-- 404 -->
<servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>com.mihoyo.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

7.ServletContext

Web容器在启动的时候,它会为每一个Web程序都创建一个对应的 ServletContext 对象。

它作用于整个Web应用,是一个全局对象。

(1)共享数据

在这个Servlet中保存的数据,可以在另外一个Servlet中拿到。

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取ServletContext对象
        ServletContext context = this.getServletContext();
        String username = "江江"; // 数据
        // 将一个数据保存在了ServletContext中 
        // 名字:String字符串,值:任意对象(Object)
        context.setAttribute("username", username);
    }
}
public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取ServletContext对象
        ServletContext context = this.getServletContext();
        //从ServletContext取出数据
        String username = (String) context.getAttribute("username");
        
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().println("请叫我" + username);
    }
}
    <servlet>
        <servlet-name>GetServlet</servlet-name>
        <servlet-class>com.mihoyo.serlvet.GetServlet</servlet-class>
    </servlet>
    <!-- Servlet的请求路径 -->
    <servlet-mapping>
        <servlet-name>GetServlet</servlet-name>
        <url-pattern>/get</url-pattern>
    </servlet-mapping>

测试访问结果:

直接访问 /get,结果显示为 null;

先访问/hello,再访问 /get ,就能拿到结果为 “江江”。

(2)获取初始化参数

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

(3)请求转发

public class ServletDemo extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        // 转发的请求路径
        RequestDispatcher requestDispatcher = context.getRequestDispatcher("/hello");
        // 调用forward实现请求转发
        requestDispatcher.forward(req, resp);

        //context.getRequestDispatcher("/SD").forward(req, resp);
    }
}
    <servlet>
        <servlet-name>ServletDemo</servlet-name>
        <servlet-class>com.mihoyo.serlvet.GetServlet</servlet-class>
    </servlet>
    <!-- Servlet的请求路径 -->
    <servlet-mapping>
        <servlet-name>ServletDemo</servlet-name>
        <url-pattern>/SD</url-pattern>
    </servlet-mapping>

转发和重定向的区别: 

转发之后,路径不会变,仍是:/SD,只是转发到  /hello  这个路径下的 servelt 中。

重定向后,路径也会随之发生改变,变成  /hello。

(4)获取配置文件

分别在 Java目录下 和 resources目录下新建 .properties 配置文件。

发现:都被打包到了同一个路径下:WEB-INF/classes 下,我们俗称这个路径—classpath;

注意:如果资源无法导出,是因为:该 module 下的 pom.xml 中没有在build中配置 resources。

public class PropertiesServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取ServletContext对象
        ServletContext context = this.getServletContext();
        // 获取输入流
        // 第一个 / 不能省略,代表当前 web 项目
        InputStream is = context.getResourceAsStream("/WEB-INF/classes/db.properties");
        Properties prop = new Properties();
        //将配置文件中的数据,读取到集合中来
        prop.load(is);
        String user = prop.getProperty("username");
        String pwd = prop.getProperty("password");
        resp.getWriter().println(user + pwd);
    }
}

8.HttpServletResponse

Web服务器接收到浏览器的http请求,针对这个请求,分别创建:

        一个代表请求的 HttpServletRequest 对象;

        一个代表响应的 HttpServletResponse 对象。

如果要获取浏览器请求过来的参数:找 HttpServletResuest

如果要给浏览器响应一些信息:找 HttpServletResponse

(1)简单分类

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

ServletoutputStream getoutputStream() throws IOException;
PrintWriter getwriter() throws IOException;

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

void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setBufferSize(int var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
void setStatus(int var1);

相应的状态码

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

(2)常见应用

Ⅰ. 向浏览器输出消息 --> response.getWriter()

Ⅱ. 下载文件 --> response.getOutputStream()

public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 要获取下载文件的路径
        String realPath = this.getServletContext().getRealPath("/WEB-INF/classes/dog.jpg");
        System.out.println("下载文件的路径:" + realPath);
        // 2. 下载的文件名是啥?
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
        System.out.println(fileName);
        // 3. 想办法设置让浏览器能够支持下载我们的东西,中文文件名URLEncoder.encode编码,否则有可能乱码
        resp.setHeader("Content-Disposition","attachment;"+
                " filename=" + URLEncoder.encode(fileName, "UTF-8"));
        // 4. 获取下载文件的输入流
        FileInputStream in = new FileInputStream(realPath);
        // 5. 创建缓冲区
        int len = 0;
        byte[] buffer = new byte[1024];
        // 6. 获取OutoutStream对象
        ServletOutputStream out = resp.getOutputStream();
        // 7. 将FileOutputStream流写入到缓冲区, 使用OutputStream将缓冲区中的数据输出到客户端
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
        in.close();
        out.close();
    }
}

Ⅲ. 验证码功能

public class ImageServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 如何让浏览器3秒刷新一次
        resp.setHeader("refresh", "3");
        // 在内存中创建一个图片
        BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
        // 得到图片
        Graphics2D g = (Graphics2D) image.getGraphics(); // 笔
        // 设置图片的背景颜色
        g.setColor(Color.white);
        g.fillRect(0, 0, 80, 20);
        // 给图片写数据
        g.setColor(Color.blue);
        g.setFont(new Font(null, Font.BOLD, 20));
        g.drawString(getCode(), 0, 20);
        // 告诉浏览器,这个请求用图片方式打开
        resp.setContentType("image/png");
        // 网站存在缓存,不让浏览器缓存
        resp.setDateHeader("expires", -1);
        resp.setHeader("Cache-Control", "no-cache");
        resp.setHeader("Pragma", "no-cache");
        // 图片IO流
        ImageIO.write(image,"png", resp.getOutputStream());
    }

    // 生成验证码
    private String getCode() {
        ArrayList<Character> list=new ArrayList<>();
        for (int i = 0; i < 26; i++) {
            list.add((char)('a'+i));
            list.add((char)('A'+i));
        }
        StringBuilder sb=new StringBuilder();
        //随机抽取四次字母
        Random r=new Random();
        for (int i = 0; i < 4; i++) {
            int idx=r.nextInt(list.size());
            sb.append(list.get(idx));
        }
        int number=r.nextInt(10);
        //StringBuilder的append方法可以接收任意数据类型
        sb.append(number);

        //如果需要修改字符串的内容-->先把字符串变成字符数组,再修改
        char[] chs=sb.toString().toCharArray();
        int randomIdx=r.nextInt(5);
        char temp=chs[chs.length-1];
        chs[chs.length-1]=chs[randomIdx];
        chs[randomIdx]=temp;
        String str=new String(chs);
        System.out.println(str);
        return sb.toString();
    }
}

Ⅳ. 实现重定向

 B收到A请求后,B会通知A去访问C,这个过程叫做重定向。

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  //路径要包含当前项目路径
  resp.sendRedirect("/s1/hello");

  //重定向包含以下两步
  // resp.setHeader("Location", "/s1/hello");//设置重定向的路径
  // resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);//设置状态码:302
}

(3)重定向和转发的区别:

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

不同点:

请求跳转的时候,url不会产生变化(状态码:307);

重定向的时候,url 地址栏会发生变化(状态码:302)。

转发:将同一个请求传给下一个页面。

重定向:创建一个新的请求传给下一个页面,之前的请求结束生命周期。

注:如果两个页面之间需要通过 request 来传值,则必须使用转发,不能使用重定向。

9.HttpServletRequest

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

获取前端传递的参数,请求转发

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>首页</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/login" method="post">
        用户名:<input type="text" name="username"><br>
         密码:<input type="password" name="password"><br>
         爱好:
         <input type="checkbox" name="hobbys" value="代码"> 代码
         <input type="checkbox" name="hobbys" value="唱歌"> 唱歌
         <input type="checkbox" name="hobbys" value="女孩"> 女孩
         <input type="checkbox" name="hobbys" value="电影"> 电影
         <br>
         <input type="submit" name="提交">
    </form>
</body>
</html>`
public class LoginServlet extends HttpServlet {
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     this.doPost(req, resp);
 }
 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     // 处理请求中文乱码(后期可以使用过滤器来解决)
     req.setCharacterEncoding("utf-8");
     resp.setCharacterEncoding("utf-8");
     String username = req.getParameter("username");
     String password = req.getParameter("password");
     String[] hobbys = req.getParameterValues("hobbys");
     System.out.println(username);
     System.out.println(password);
     System.out.println(Arrays.toString(hobbys));
     // 这里的 / 代表当前的web应用,所以不需要再加上/s1 这个上下文路径了,否则会出现404错误
     req.getRequestDispatcher("/success.jsp").forward(req,resp);
 }
}

六、Cookie 和 Session

1.会话

服务器无法识别每一次 Http 请求的出处(不知道来源于哪个终端),它只会接收到一个请求信号。

所以就存在一个问题:服务器可能将用户A的响应发送给其他人(用户B)。

必须有一种技术来让服务器知道请求来自哪,这就是会话技术。

会话:客户端和服务器之间发生的一系列连续的请求和响应的过程

        (整个过程:打开浏览器 --> 进行操作 --> 关闭浏览器)。

会话状态:指服务器和浏览器在会话过程中产生的状态信息,借助于会话状态,服务器能够把属于同一次会话的一系列请求和响应关联起来。

2.实现会话的两种方式

cookie:客户端技术,(响应、请求)

session:服务端技术,利用这个技术,可以保存用户的会话信息,也可以把信息或者数据放在session中。

3.Cookie

① 从请求中拿到cookie

② 服务器响应给客户端cookie

Cookie[] cookies = req.getCookies();// 获得cookie
cookie.getName();// 获得cookie中的key
cookie.getValue();// 获得cookie中的value
new Cookie("username","zhangsan");// 新建一个cookie
cookie.setMaxAge(24*60*60);// 设置cookie的有效期,单位:秒
resp.addCookie(cookie);// 响应给客户端一个cookie

cookie 的保存位置:

一般情况下,会保存在本地的用户目录下的 AppData 文件夹中

除此之外,也会存放在浏览器的缓存目录中。

不论哪种方式,最终都是保存在本地硬盘当中。


Question:一个网站的 cookie 是否存在上限?

 一个 cookie 只能保存一个信息;

但一个web站点(网站)可以给浏览器发送多个cookie,最多存放20个cookie;

单个 cookie 存储的数据大小有限制:4kb;

浏览器上限:300个cookie。


删除 cookie的两种方式:

① 不设置有效期,默认关闭浏览器,自动失效

② 设置有效期时间为0

解决 cookie 的中文乱码问题:

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

Test:保存用户上一次访问时间

public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决中文乱码
        resp.setContentType("text/html");
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();
        // Cookie,服务器端从客户端获取cookie
        Cookie[] cookies = req.getCookies();// 数组,说明cookie可以有多个
        // 判断cookie是否
        if (cookies != null) {
            out.write("你上一次登录的时间是:");
            for (int i = 0; i < cookies.length; i++) {
                // 获取cookie的名字
                if (cookies[i].getName().equals("lastLoginTime")) {
                    // 获取cookie的值
                    long l = Long.parseLong(cookies[i].getValue());
                    Date date = new Date(l);
                    out.write(date.toLocaleString());
                }
            }
        } else {
            //进不来-->浏览器会往cookie中添加一些其他的属性,第一次也不为空
            out.write("你是第一次登录!");
        }
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        cookie.setMaxAge(24 * 60 * 60);// 设置cookie的有效期为一天,单位是:秒
        resp.addCookie(cookie);
    }
}

 注意:即使没有手动往 cookie 中存储数据,cookie也不为空,会有一个自带的 JSESSIONID 属性(识别请求的来源)。

 4.Session

定义:打开浏览器时,服务器就会给每一个用户(浏览器)创建一个Session对象。

        一个session独占一个浏览器,只要浏览器没有关闭,这个session就存在。

// 得到session
HttpSession session = req.getSession();
// 给session中存东西
session.setAttribute("name", "张三");
// 获取session的id
String sessionId = session.getId();
// 从session中获取数据
session.getAttribute("name");

session和cookie的区别:

cookie:把用户的数据写给用户的浏览器

                ① 保存在浏览器

                ② 保存的数据是 String

                ③ 可以长期保存在浏览器中,与会话无关

                ④ 可以保存多个,保存不重要信息

Session:把用户的数据写到用户独占的 Session 对象中

                ① 保存在服务器

                ② 保存的数据是 Object

                ③ 随着会话结束而销毁

                ④ 保存相对少一点,只保存重要信息,以减少服务器资源的浪费

public class SessionServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决中文乱码
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        // 得到session
        HttpSession session = req.getSession();
        // 给session中存东西
        session.setAttribute("name", "张三");
        // 获取session的id
        String sessionId = session.getId();
        // 判断session是不是新创建
        if (session.isNew()) {
            resp.getWriter().write("session创建成功,ID:" + sessionId);
        } else {
            resp.getWriter().write("session已经存在了,ID:" + sessionId);
        }
        // session创建的时候做了什么事情
        /*Cookie cookie = new Cookie("JSESSIONID", sessionId);
        resp.addCookie(cookie);*/
        //------------------
        // 从session中获取数据
        String name = (String) session.getAttribute("name");
        //------------------
        // 从session中删除指定name的数据
        session.removeAttribute("name");
        // 手动注销session
        session.invalidate();
    }
}

会话自动过期:

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

七、JSP

1.定义

Java Server Pages:Java服务端页面,和servlet一样,用于动态web技术

特点:写jsp就像在写html

区别:html只给用户提供静态的数据

          jsp页面中可以嵌入Java代码,为用户提供动态数据

2.原理

代码层面:

服务器内部原理:

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

在work 目录中,发现 jsp 页面变成了 java 程序,最终也会被转换成一个 Java 类。 

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

 打开 index_jsp.java,我们发现它继承了一个 HttpJspBase 类

而这个 HttpJspBase 类 ,正是继承了 HttpServlet 类,所以: jsp 本质上就是一个 Servlet!

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

在 _jspService方法中:

① 请求判断

② 内置一些对象(9个)

final javax.servlet.jsp.PageContext pageContext;    // 页面上下文
javax.servlet.http.HttpSession session = null;        // session
final javax.servlet.ServletContext application;        // applicationContext
final javax.servlet.ServletConfig config;            // config:配置
javax.servlet.jsp.JspWriter out = null;                // out:输出流
final java.lang.Object page = this;                    // page:当前
HttpServletRequest request;                            // 请求
HttpServletResponse response;                        // 响应

 ③ 输出页面前增加的代码

response.setContentType("text/html;charset=UTF-8");// 设置响应的页面类型
pageContext = _jspxFactory.getPageContext(this, request, response,
         null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;

④ 以上的内置对象可以在jsp页面中直接使用

原理图:

在JSP页面中:

只要是JAVA代码就会原封不动的输出

如果是HTML代码,就会被转换为如下格式,输出到前端

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

3.基础语法

 JSP作为java技术的一种应用,Java所有语法它都支持。但它同时拥有一些自己扩充的语法)。

(1)jsp表达式

格式:<%= 变量或表达式%>

<%--
作用:用来将程序的输出,输出到客户端
--%>
<%= new java.util.Date() %>

(2)jsp脚本片段

格式:<% %>

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

在 jsp 脚本片段中,嵌入 html 代码

<%--EL表达式:${变量} --%>
<% for (int i = 0; i < 3; i++) { %>
    <h1>Hello World! ${i}</h1>
<% } %>

(3)jsp声明

格式:<%! %>

<%!
static {
 System.out.println("loading Servlet!");
}
private int globalVar =0;
public void method(){
 System.out.println("进入了该方法!");
}
%>

区别:

JSP声明:会被编译到 jsp 生成 java 的类中!
其他的,就会被生成到 _jspService 方法中!

(4)jsp注释

格式:<%--注释--%>

注意:jsp 的注释不会在客户端(浏览器)显示,而 HTML 的注释会。

4.jsp指令

(1)定制错误页面

<%--当该页面出现错误(异常)之后,会跳转到500.jsp中--%>
<%@ page errorPage="error/500.jsp" %>

可以在web.xml定制全局的错误页面,产生对应的错误代码后,就会跳转到对应的 jsp 页面。

(2)包含页面头部和尾部

区别:

@include 会将包含的页面中的代码直接拷贝过来,合二为一

jsp:include 会将包含的页面进行引用,本质上还是两个互不影响的页面。

5.九大内置对象 

① pageContext:页面上下文,获取页面信息,存东西

② Request:表示⼀次请求,HttpServletRequest,存东西

③ Response:表示⼀次响应,HttpServletResponse。

④ Session:表示⼀次会话,HttpSession,存东西:保存用户信息。

⑤ Aapplication:表示当前 Web 应用,全局对象,ServletContext,存东西:保存所有用户共享信息。

⑥ Config:当前 JSP 对应的 Servlet 的 ServletConfig 对象,获取当前 Servlet 的配置信息。

⑦ out:向浏览器输出数据,JspWriter。

⑧ page:当前 JSP 对应的 Servlet 对象,Servlet。

⑨ exception:表示 JSP ⻚⾯发⽣的异常,Exception。

其中,常用的是 request、response、session、application、pageContext

存东西的区别:

 //保存的数据只在一个页面中有效
pageContext.setAttribute("name1","张三1");

//保存的数据只在一次请求中有效,请求转发会携带这个数据
request.setAttribute("name2","张三2");

//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
session.setAttribute("name3","张三3");

//保存的数据只在服务器中有效,从打开服务器到关闭服务器
application.setAttribute("name4","张三4");

从底层到高层(作用域):page --> request --> session --> application

使用场景:

request:客户端向服务器发送请求,产生的数据,可能过会就用不上了,例:某个文章。

session:客户端向服务器发送请求,产生的数据,可能用完了还要用,例:用户信息。

application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用。例:全局参数。

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

相关依赖: 

<!-- JSTL表达式的依赖 -->
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>jakarta.servlet.jsp.jstl</artifactId>
    <version>2.0.0</version>
</dependency>
<!-- standard标签库 -->
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

(1)EL表达式:${}

        获取数据

        执行运算

        获取web开发的常用对象

(2)JSP标签:

<%-- 引用页面 --%>
<jsp:include page="header.jsp"></jsp:include>

<%-- 
    请求转发,并携带参数 
    http://localhost:8080/s1/jsptag1.jsp?name=zhangsan&age=20
 --%>
<jsp:forward page="jspTag2.jsp">    
    <jsp:param name="name" value="zhangsan"/>    
    <jsp:param name="age" value="20"/>
</jsp:forward>

(3)JSTL表达式:

JSTL 标签库的使用,就是为了弥补 HTML 标签的不足。

它定义了许多标签,标签的功能和 java 代码一样。

使用步骤:

① jsp文件中引入对应的 taglib 

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>

② 在 tomcat 安装目录下的 lib 文件夹中,也需要引入 jstl 的包,否则会报错:JSTL解析错误。

③ 使用其中的方法


Ⅰ. c:if 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>if测试</h3>
    <hr>
    <form action="core.jsp" method="get">
        <%--
            EL表达式获取表单中的数据
            ${param.参数名}
        --%>
        <input type="text" name="username" value="${param.username}">
        <input type="submit" value="登录">
    </form>
    <%--判断如果提交的用户名是管理员,则登录成功--%>
    <c:if test="${param.username=='admin'}" var="isAdmin">
        <c:out value="管理员欢迎您!"/>
    </c:if>
    <%--自闭合标签--%>
    <c:out value="${isAdmin}"/>
</body>
</html>

Ⅱ. c:choose 和 c:when

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

Ⅲ. c:foreach 

<%
    List<String> nameLists = new ArrayList<>();
    nameLists.add("张三");
    nameLists.add("李四");
    nameLists.add("王五");
    nameLists.add("赵六");
    nameLists.add("田六");
    request.setAttribute("list",nameLists);
%>

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

 八、Filter过滤器

Filter:过滤器 ,用来过滤请求中的数据(如:处理中文乱码,登录验证….)。

步骤:

① 导入 jakarta.servlet.Filter 类 

② 实现 Filter 接口,重写方法

import jakarta.servlet.*;
import java.io.IOException;

public class FilterDemo implements Filter {
    //web启动时,过滤器初始化,随时等待过滤对象出现
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter初始化=====》");
    }
    
    /**
     * 1. 过滤器器中的所有代码在过滤特定请求(路径)时都会执行
     * 2. 必须要让过滤器继续通行,把请求和响应继续传递给后续所有过滤器和servlet。
            chain.doFilter(request,response);
     */
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        servletResponse.setContentType("text/html");
        System.out.println("Filter执行前=====》");
        //放行,如果不写,程序到这里就停止了
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("Filter执行后=====》");
    }

    // web服务器关闭时,过滤器才会销毁
    public void destroy() {
        System.out.println("Filter销毁=====》");
    }
}

注意: 

必须要让过滤器继续通行:chain.doFilter(request,response);  把请求和响应继续传递给后续所有过滤器和servlet。

如果没有调用这个方法,过滤器链就会被中断,后续的过滤器和目标资源将不会被执行。

请求不会继续传递下去,而是直接在当前过滤器中结束。


③ 在 web.xml 中 配置 filter,设置特定的请求(路径)

    <filter>
        <filter-name>FilterDemo</filter-name>
        <filter-class>com.mihoyo.filter.FilterDemo</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>FilterDemo</filter-name>
        <!--只要是/fliter下的任何请求,都走这个过滤器-->
        <url-pattern>/filter/*</url-pattern>
    </filter-mapping>

 应用:用户只有登录后,才能进入主页。用户没登录或者退出登录后,就不能进入主页了。

(1)用户登录之后,向 session 中存放数据

(如果用直接 sessionId 的话,会频繁涉及 session 的创建和销毁,造成资源浪费。

    向 session 中存数据的话,session 还在,可以复用)

由于 session 中存入的数据的名字会频繁使用(存取),一旦发生变化,很多地方都要修改。

该数据的名字静态常量的形式,存储一个常量类中。写名字的地方,直接调用该常量即可。

如果名字发生变化,就只要修改该类即可。 

(2)进入主页的时候,通过过滤器判断:用户是否已经登录(session中存的数据是否存在)

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (request.getSession().getAttribute(Constant.USER_SESSION) == null) {
            response.sendRedirect("/error.jsp");
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }

九、监听器

步骤:

① 编写一个监听器:实现监听器的接口

import jakarta.servlet.*;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;

public class OnlineCountListener implements HttpSessionListener {
    //创建session监听:创建Session时会触发一次该事件。
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        ServletContext ctx = httpSessionEvent.getSession().getServletContext();
        System.out.println(httpSessionEvent.getSession().getId());
        Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
        if (onlineCount==null){
            onlineCount = 1;
        }else {
            int count = onlineCount;
            onlineCount = count + 1;
        }
        ctx.setAttribute("OnlineCount",onlineCount);
    }
    //销毁session监听:销毁session时,会触发一次该事件。
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        ServletContext ctx = httpSessionEvent.getSession().getServletContext();
        Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
        if (onlineCount==null){
            onlineCount = 0;
        }else {
            int count = onlineCount;
            onlineCount = count - 1;
        }
        ctx.setAttribute("OnlineCount",onlineCount);
    }
}

② 在 web.xml 中配置监听器

    <!--注册监听器-->
    <listener>
        <listener-class>com.mihoyo.listener.OnlineCountListener</listener-class>
    </listener>

③ 看情况是否使用

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>当前有<%=request.getServletContext().getAttribute("OnlineCount")%>人在线</h1>
</body>
</html>

注意:

tomcat 在启动时,会进行多次连接。因为可能会连接失败,但session已经创建了。

此时就会造成只有一个客户端(浏览器),但 session 数量不唯一的情况。

此时不重启服务器,而是重新发布项目, session 就会自动销毁了,显示就正常了。

  • 13
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值