Web开发

Web开发

英文中web表示“网”的意思,它用于表示web服务器(主机)供浏览器访问的资源;web是一种基于互联网的服务,提供了一种图形化、易于访问的界面,使用户可以在互联网上查找和浏览信息。
web是由很多html网页组成的,网页由链接互相关联形成网络结构。

互联网、万维网和因特网

  • 互联网:多个设备能彼此通信而组成的网络,internet;最广泛的概念。
  • 因特网:是互联网中的一种,使用tcp/ip协议让不同设备进行通信,由成千上万台计算机组成的网络,Internet
  • 万维网:world wide web,是基于互联网的信息服务系统,通过HTTP传输数据,由大量网页链接关联而成的网络资源。

互联网 > 因特网 > 万维网

web服务器上提供外界访问的web资源分为:

  • 静态资源(html css 图片等),数据始终不变,所有的用户看到的都是相同的内容。
  • 动态资源,能够根据用户交互动态生成内容

在Java Web中,Web指的是一种基于互联网的服务,使用Java技术来实现动态Web的功能。

服务器:通常是一台高性能的计算机,在网络环境下为客户端提供某种服务的专用计算机。具备强大的处理能力、高速的存储和网络连接,可以处理大量的请求并保证服务的稳定性和可靠性。

Tomcat

Tomcat是一个开源的、轻量级web应用服务器。

核心功能是接受客户端的请求,并传递给相应的Servlet进行处理,再将处理结果返回给客户端进行响应。在这个过程中tomcat有两个重要的角色:

  • 连接器:tomcat监听端口接受外界请求,并将请求处理后传递给容器做业务处理。(前端和后台交互的桥梁,通过HTTP(s)服务)
  • 容器:tomcat是Servlet的容器,负责存放和管理Servlet,提供了Servlet的运行环境和生命周期管理。处理Servlet的初始化、请求处理和销毁等操作。使开发者专注于实现业务逻辑。

注意:一般服务器比如 Apache、Nginx、IIS 等,它们的功能往往都比较单一,只能提供 http(s) 服务,让用户访问静态资源,它们不能执行任何编程语言,所以一般服务器只能部署静态网站;要想部署动态网站还需要一个运行环境(RunTime),以支持编程代码的运行。

部署动态网站的组件:
在这里插入图片描述
注:上面tomcat软件自带了web服务器模块,提供了基本的HTTP服务又有Servlet的执行环境,简化了部署流程。

Tomcat 目录:
在这里插入图片描述

Servlet

为什么会出现Servlet?

如果使用现有的html,css,javascript来获取后台数据库数据可以吗?Answer:不行,上面三个技术只能操作前端的静态数据,不能操作数据库。数据库是存储在服务器端的数据集合,要实现前后端数据的交互,就需要一个能够处理动态数据的技术,Servlet就是为此而生的。

Web服务器(如Tomcat、Weblogic)本身只能处理静态资源的请求,即直接返回已存在的文件内容。但当需要根据用户请求动态生成内容时(例如,从数据库中查询数据并展示在网页上),就需要Servlet这样的组件来处理这些动态资源的请求。


什么是Servlet?

Servlet = server + applet(即服务端程序),是运行在服务器上的Java程序,用来接收、处理客户端请求并动态的生成web内容返回给浏览器。

实现Servlet的三种方式

  • 实现 javax.servlet.Servlet 接口,重写其全部方法。
  • 继承 javax.servlet.GenericServlet 抽象类,重写 service() 方法。
    (GenericServlet 是一个通用 的 Servlet 类,并没有针对某种场景进行特殊处理,尤其是 HTTP 协议,我们必须手动分析和封装 HTTP 协议的请求信息和响应信息。)
  • 继承 javax.servlet.http.HttpServlet 抽象类,重写 doGet() 或 doPost() 方法。(一般常用这个实现Servlet)

servlet的配置文件(web.xml):

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet</servlet-class>
    </servlet>
   <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>
</web-app>

说明:

<web-app>: 根元素。
<servlet> :用于注册 Servlet,即给 Servlet 起一个独一无二的名字。
<servlet> 包含两个主要的子元素 <servlet-name> 和 <servlet-class>,分别用于指定 Servlet 的名称和 Servlet 的完整限定名(包名+类名)。
<servlet-mapping> :用于定义 Servlet 与 URL 之间的映射。
<servlet-mapping> 包含两个子元素 <servlet-name> 和 <url-pattern>,分别用于指定 Servlet 的名称和虚拟路径。
  • 即访问 http://localhost:8080/项目名/MyServlet 可以映射到servlet 为MyServlet的类上处理请求

Servlet注解:

  • 为了简化 Servlet 的配置,Servlet 3.0 中增加了注解支持,例如:@WebServlet、@WebInitParm 、@WebFilter 和 @WebLitener 等,这使得 web.xml 从 Servlet 3.0 开始不再是必选项了。
@WebServlet(urlPatterns="/MyServlet")
public class MyServlet extend HttpServlet{
	...
}

Servlet生命周期

Servlet 的生命周期就是 Servlet 从创建到销毁的过程。Servlet 的生命周期由 Servlet 容器管理,主要分为以下 3 个阶段。

初始化阶段 init()
运行时阶段 service()
销毁阶段 destory()

生命周期的作用:

资源管理和初始化:例如,在启动一个Web应用程序时,我们可以初始化数据库连接、加载配置文件等。
这些资源可以在整个应用程序生命周期内重复使用,提高应用程序的性能和效率。

日志记录和监控:通过Servlet的生命周期,我们可以方便地进行日志记录和监控。
例如,在Servlet的初始化阶段,我们可以记录应用程序启动的时间和状态;
在处理请求时,我们可以记录请求的详细信息和响应的结果;在Servlet的销毁阶段,我们可以记录应用程序关闭的时间和状态。
通过这些日志记录,我们可以监控应用程序的性能和健康状况,及时发现并解决问题。

load-on-startup

  • load-on-startup 是 servlet 元素的子元素,用来标记 Servlet 容器启动时是否初始化当前 Servlet,以及当前 Servlet 的初始化顺序。

  • load-on-startup 元素取值规则如下:
    它的取值必须是一个整数;
    值< 0 或者没有指定时,则表示容器在该 Servlet 被首次请求时才会被加载;
    值大于 >= 0 ,表示容器在启动时就加载并初始化该 Servlet,取值越小,优先级越高;
    当取值相同时,容器就会自行选择顺序进行加载。

Servlet多重路径

  • 配置多个urlPatterns
   <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myServlet</url-pattern>
        <url-pattern>/myServlet3</url-pattern>
    </servlet-mapping>
  • 注解方式使用字符串数组
@WebServlet(urlPatterns = {"/uploadFile", "/uploadFile2"})
public class MyServlet extends HttpServlet {
  ...
}

ServletConfig对象

Servlet容器初始化Servlet时会为该Servlet创建一个对象ServletConfig,通过该对象可以获取Servlet的初始化信息。

获取ServletConfig对象

doGet()/doPOST()/service()方法中使用  this.getServletConfig()

获取初始化参数

    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet</servlet-class>
        <!-- Servlet 初始化参数 -->
        <init-param>
            <param-name>name</param-name>
            <param-value>编程帮</param-value>
        </init-param>
    </servlet>
 Enumeration<String> initParameterNames = this.getServletConfig().getInitParameterNames();
       // 遍历集合获取初始化参数名称
        while (initParameterNames.hasMoreElements()) {
            // 获取初始化参数名称
            String initParamName = initParameterNames.nextElement();
            // 获取相应的初始参数的值
            String initParamValue = config.getInitParameter(initParamName);
        }

ServletContext对象

  • Servlet 容器启动时,会为每个 Web 应用(webapps 下的每个目录都是一个 Web 应用)创建一个唯一的 ServletContext 对象,该对象一般被称为“Servlet 上下文”。
    ServletContext 对象的生命周期从 Servlet 容器启动时开始,到容器关闭或应用被卸载时结束。
//获取方式
//通过HttpServletRequest
1: ServletContext servletContext = request.getServletContext();
//通过servletConfig对象获取
2: ServletContext context = this.getServletConfig().getServletContext();
//通过父类GenericServlet获取
3ServletContext context = super.getServletContext();
//通过Session获取
4: ServletContext conxtext = request.getSession().getServletContext();

ServletContext 的应用主要有以下 3 个:

  • 获取上下文初始化参数
 <!--在web-app下设置全局初始化参数 -->
    <context-param>
        <param-name>name</param-name>
        <param-value>编程帮</param-value>
    </context-param>
    <context-param>
        <param-name>url</param-name>
        <param-value>www.biancheng.net</param-value>
    </context-param>
		ServletContext context = super.getServletContext();
        // 返回 context 上下文初始化参数的名称
        Enumeration<String> initParameterNames = context.getInitParameterNames();
        while (initParameterNames.hasMoreElements()) {
            // 获取初始化参数名称
            String initParamName = initParameterNames.nextElement();
            // 获取相应的初始参数的值
            String initParamValue = context.getInitParameter(initParamName);
        }
  • 实现不同Servlet 之间的数据通讯
ServletContext context = this.getServletConfig().getServletContext();

//将一个Java对象与name绑定,并将其作为属性保存到servletContext中
context.setAttribute(String name,Object object); 

//根据name获取ServletContext中存储的值
context.getAttribute(String name);

//根据指定的name删除servletContext中的值
context.removeAttribute(String name);

demo: 编写一个统计页面访问量的案例

在某个Servlet中:
 	@Override
    public void init() throws ServletException {
        //设置页面访问次数的变量
        super.getServletContext().setAttribute("visitCount",0);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        resp.setContentType("text/html;charset=utf-8");
        ServletContext context = super.getServletContext();
        context.setAttribute("visitCount",((int)context.getAttribute("visitCount"))+1);
        resp.getWriter().write("<h1>访问次数:"+ context.getAttribute("visitCount") +"</h1>");
    }
  • 读取 Web 应用下的资源文件
    在src/main/resources下创建db.properties配置文件
name=编程帮
url=www.biancheng.net
desc=编程帮,欢迎你
		// 获取相对路径中的输入流对象
        InputStream ins = getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
        // 获取输入流
        Properties pro = new Properties();
        // 加载
        pro.load(ins);
        // 获取文件中的内容
        String name = pro.getProperty("name");
        String url = pro.getProperty("url");
        String desc = pro.getProperty("desc");

HttpServletRequest和HttpServletResponse

  • Servlet 容器接收到来自客户端的 HTTP 请求后,容器会针对该请求分别创建一个 HttpServletRequest 对象和 HttpServletReponse 对象。

  • 容器将 HttpServletRequest 对象和 HttpServletReponse 对象以参数的形式传入 service() 方法内,并调用该方法。

  • 在 service() 方法中 Servlet 通过 HttpServletRequest 对象获取客户端信息以及请求的相关信息。

  • 请求处理完成后,将响应信息封装到 HttpServletReponse 对象中,容器再将响应信息返回给客户端。之后,HttpServletRequest 对象和 HttpServletReponse 对象被销毁。

protected void doGet(HttpServletRequest request, HttpServletResponse response)  {
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().println("请求方式:" + request.getMethod() + "<br/>" +
                "客户端的 IP 地址:" + request.getRemoteAddr() + "<br/>" +
                "应用名字(上下文):" + request.getContextPath() + "<br/>" +
                "URI:" + request.getRequestURI() + "<br/>" +
                "请求字符串:(url的?之后的内容)" + request.getQueryString() + "<br/>" +
                "Servlet所映射的路径:" + request.getServletPath() + "<br/>" +
                "客户端的完整主机名:" + request.getRemoteHost() + "<br/>"
        );
    }
HttpServletRequest获取表单请求中的参数:
 <form action="/submitData" method="post">
 	<input type="text" name="username" /> 这里的name属性必须要有,供服务器获取该参数时使用
 	<input type="checkbox" name="language" value="JAVA" />JAVA
    <input type="checkbox" name="language" value="C" />C语言 
    <select name="city">
            <option value="beijing">北京</option>
            <option value="shanghai">上海</option>
    </select>
    <input type="submit" value="提交" />
 </form>
public class getParamServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp){
    	String username = req.getParameter("username"); //根据表单的name属性获取参数
    	String city= req.getParameter("city");
    	String[] languages = req.getParameter("language"); //复选框可以选中多个,所以用数组来接受数据
    }
}
form表单数据中的乱码问题

根据请求的方式不同,乱码一般是get请求乱码和post请求乱码。

  • get请求乱码(tomcat8以后的版本中乱码已经被解决)
    tomcat没有设置字符编码时默认是ISO-8859-1格式进行url编码,所以不支持中文;
// 获取请求中的参数
String username = req.getParameter("username");
//中文乱码解决
username = new String(username.getBytes("ISO-8859-1"),"UTF-8");
  • post请求乱码问题
    post请求时会将请求参数放在请求体中,一般设置中文编码utf-8,request对象接收请求以后会将数据放入request缓冲区中,缓冲区的编码格式默认为ISO-8859-1,编码不同导致乱码。
解决方法:将request缓冲区编码设置为utf-8
request.setCharacterEncoding("utf-8");
HttpServletResponse

HttpServletResponse继承至ServletResponse接口,用来封装请求处理之后的响应数据并返回给客户端。

response中的输出流可以向客户端输出数据;包含

  • 字符流: PrintWriter out = response.getWriter();
    • out.write("<h1>hello world</h1>");
  • 字节流:ServletOutoutStream out = response.getOutoutStream();
    • out.write("编程帮 www.biancheng.net".getBytes("UTF-8"));用来输出二进制数据(一般在下载某个二进制文件时使用)

response中的中文乱码:

  • 字符流乱码:一定导致乱码,因为通过字符流输出的数据会放在response缓冲区中,缓冲区的默认编码是ISO-8859-1,不支持中文,所以需要设置缓冲区中的编码为utf-8
response.setContentType("text/html;charset=UTF-8");
  • 字节流乱码
    字节流是否乱码,取决于中文转字节数组采用的编码和浏览器的编码是否一致。
response.setContentType("text/html;charset=UTF-8");
response.getOutoutStream().write("你好".getBytes("UTF-8"));

请求转发和重定向

请求转发和重定向都是用来进行页面间跳转的,但是实现跳转的方式不同:

  • 请求转发(forward):
    web应用在处理客户端请求时通常会需要多个资源(如Servlet)之间进行共同协作才可完成; 比如用户在登陆时需要servlet验证用户名和密码是否正确,如果正确在跳转到首页界面,首页界面需要用户名展示。(这个场景必须要验证和跳转界面共同完成才行)
    • 请求转发:servlet容器接收到请求以后,会先对请求做一些预处理,然后再将请求传递给其他资源(如Servlet、jsp)而无需客户端发起新的请求。
    • 请求转发发生在服务端,整个过程只有一次请求与响应,所以不同资源之间可以共享请求中的数据;由于是一次请求所以浏览器url不会发生变化。
    • 请求转发不支持跨域请求,只能跳转到当前应用中的资源。
request.getRequestDispatcher(String path).forward(request, response);
  • 重定向(redirect):
    重定向属于客户端行为。服务器在收到客户端请求后,会通知客户端浏览器重新向另外一个 URL 发送请求,这称为请求重定向。它本质上是两次 HTTP 请求。
    • 重定向是两次请求,请求的url会发生变化,支持跨域请求。
    • 服务端会以状态码为302的响应信息返回给浏览器,该响应的含义是:通知浏览器再次发送请求访问另外一个URL资源; 该响应中的参数location存储重定向的url
response.sendRedirect(String path);

会话跟踪技术

HTTP协议是基于请求响应模式的无状态协议,这个无状态指:

  • Http对于事务处理没有记忆能力,无法维护用户的上下文信息,不能保存用户的状态;
  • Http请求都是相互独立的,不会受到上一次请求的影响,也不会影响下一次请求。
    在web应用中一般都需要用户的状态,比如在下单之后,需要根据当前用户名获取下单的商品支付,原生的HTTP中无法保存用户名,这就引进了会话跟踪技术。

会话跟踪技术,从打开某个浏览器访问网站开始,到关闭浏览器时结束称为一次会话;该技术用来帮助服务器记录用户状态和数据

常用的会话技术有:

  • Cookie 客户端会话技术
    • Cookie 属于客户端会话技术,是服务器发送给浏览器的一小段文本,存储在客户端浏览器的内存或硬盘中;当浏览器保存了Cookie后再一次请求时会将Cookie放在请求头中回传给服务器。
  • Session 服务端会话技术
    • Session属于服务端会话技术,是浏览器访问Web应用时,服务器为客户浏览器创建一个Session对象,每个浏览器独占一个Session;用户可以将数据存储在的Session中,当用户再次访问资源时可以从Session中获取。
Cookie

Cookie有两种:

  • 会话级别Cookie(默认): Cookie保存在浏览器内存中,浏览器一旦关闭Cookie就失效;
  • 持久化Cookie:Cookie以文本文件形式保存在硬盘上。

工作流程:客户端访问服务器时,服务器通过在HTTP响应中添加set-Cookie字段保存Cookie信息,在发送给浏览器;浏览器得到响应再将Cookie信息保存在内存或硬盘上;用户再次请求服务器时会将Cookie放在请求头中回传给服务器,服务器获取请求头中的Cookie来跟踪用户的状态。


创建Cookie对象,Servlet的http包中定义了Cookie类,通过其带参构造方法创建:

Cookie只能保存文本(字符串)信息,所以Cookie的value只能是String
Cookie c1 = new Cookie("username","张三");

Cookie的获取和保存:

这里的response,request 是HttpServletResponseHttpServletRequest
// 添加Cookie(在响应中添加set-Cookie字段)
response.addCookie(c1);
//获取Cookie信息
Cookie[] cookies = req.getCookies();

操作Cookie常用方法:

  • getMaxAge() /setMaxAge() 用于获取/设置Cookie 的最大有效时间/秒。默认情况下取值为 -1,表示该 Cookie 保留到浏览器关闭为止;为 0 时,表示删除该 Cookie
  • getName() 用于获取 Cookie 的名称。
  • getPath()/setPath() 用以获取/设置Cookie路径
  • getValue()/setValue() 用于获取/设置Cookie的值

删除Cookie方法:

  • 关闭浏览器时自动删除Cookie
  • 使用 setMaxAge(0) 手动删除 Cookie,需要使用 setPath 方法指定 Cookie 的路径,且该路径必须与创建 Cookie 时的路径保持一致。
Session

Session虽然是属于服务端技术,但是他的实现离不开Cookie的支持,实现原理如下:

  • 当客户端第一次访问服务器时,服务器会创建一个 Session 对象,并为该 Session 对象分配一个唯一的 SessionID(用来标识这个 Session 对象);
  • 服务器将 SessionID 以 Cookie(Cookie 名称为:“JSESSIONID”,值为 SessionID 的值)的形式发送给客户端浏览器;
  • 客户端浏览器再次发送 HTTP 请求时,会将携带 SessionID 的 Cookie 随请求一起发送给服务器;
  • 服务器从请求中读取 SessionID,然后根据 SessionID 找到对应的 Session 对象。
// 获取Session对象
HttpSession session = request.getSession();

Session常用API

  • getCreationTime() 返回创建 Session 的时间;
  • getId() 返回 Seesion 的 ID;
  • getLastAccessedTime() 返回客户端上一次发送与此 Session 关联的请求的时间;
  • getMaxInactiveInterval()/setMaxInactiveInterval() 获取/设置Session过期时间/秒;
  • invalidate() 使 Session 失效
  • getServletContext() 返回 Session 所属的 ServletContext 对象

Session的过期时间,Session驻留在服务器中一段时间后没有被使用就会自动销毁,Session的过期时间有以下两种设置方法:
1.在web.xml中设置

<session-config>
	<session-timeout>10</session-timeout> <!--单位是分钟 必须为整数,if <=0 永不过期-->
</session-config>

2.在代码中设置

//设置会话的过期时间
request.getSession().setMaxInactiveInterval(120);  //单位秒
Session 的生命周期
  • Session 对象创建
    Session 对象在容器第一次调用 request.getSession() 方法时创建。值得注意的是,当客户端访问静态资源时,服务器不会创建 Session 对象。

  • Session 对象销毁
    Session 对象在如下 3 种情况下会被销毁:

    • Session 过期;
    • 调用 session.invalidate() 方法,手动销毁 Session;
    • 服务器关闭。

Servlet的三大域对象

  • request域(HttpServletRequest)
    生命周期:一次请求中有效。
    作用范围:整个请求链(转发有效,重定向无效)。
  • session域(HttpSession)
    生命周期:一次会话中有效
    作用范围:同一次会话中的所有请求
  • application域(ServletContext):
    生命周期:整个Web应用的生命周期(从Web应用启动到停止)。
    作用范围:整个Web应用(所有的用户都可以共享ServletContext中的数据)。

这三大域对象都提供了用于存储和获取属性的方法

setAttribute(String name, Object object):用于存储一个对象到域中,与指定的名称关联。
getAttribute(String name):用于从域中获取指定名称关联的对象。
removeAttribute(String name):用于从域中移除指定名称关联的对象。
Servlet记录上次访问的时间
@WebServlet("/visit")
//记录上次访问的时间
public class VisitedServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置页面的编码格式
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        // 获取当前时间
        Date date = new Date();
        // 设置时间格式化
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        HttpSession session = req.getSession();

        out.write("<h1>欢迎访问本网站!</h1>");
        out.write("<h3>创建Session时间:"+sdf.format(session.getCreationTime())+"</h3>");
        out.write("<h3>当前会话的SessionID:"+session.getId()+"</h3>");
        out.write("<h3>上次访问Session的时间:"+sdf.format(session.getLastAccessedTime())+"</h3>");

        if (session.getAttribute("lastVisitTime")==null){
            out.write("<h3>这是首次访问!</h3>");
        }else out.write("<h3>上次访问网站的时间:"+session.getAttribute("lastVisitTime")+"</h3>");

        // 将当前时间保存到session中
        session.setAttribute("lastVisitTime",sdf.format(date));
    }
}

Servlet Filter过滤器

Filter过滤器是Servlet中定义的拦截请求,过滤响应的规范(接口)。是Servlet中最实用的组件,常用来做用户的权限控制、过滤敏感词、设置统一编码格式等。

实现Filter方式,实现Filter接口即可
注册Filter:使用@WebFilter注解 / 在web.xml中配置

  <filter>
    <filter-name>ValidFilter</filter-name>
    <filter-class>com.servlet.ValidFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>ValidFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

Filter 工作流程:
在这里插入图片描述

demo:拦截所有请求并打印

@WebFilter("/*")
public class ValidFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;
            System.out.println("请求路径:" + req.getServletPath());
            // 可以在此处添加其他过滤逻辑
            // 继续传递请求
            chain.doFilter(req, resp);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}
}

过滤器链:在 Web 应用中,可以部署多个 Filter,若这些 Filter 都拦截同一目标资源,则它们就组成了一个 Filter 链(也称过滤器链)。客户端的请求在这些过滤器之间传递,直到传递给目标资源。

Filter 链中 Filter 的执行顺序
通过 web.xml 配置的 Filter 过滤器,执行顺序由 <filter-mapping> 标签的配置顺序决定。<filter-mapping> 靠前,则 Filter 先执行,靠后则后执行

通过 @WebFilter 注解配置的 Filter 过滤器,无法进行排序

案例:过滤敏感词
有一个评论页面,可以对提交的评论数据进行敏感词过滤功能

// 给定敏感词:滚,他妈的,去死,...
jsp评论页面

<body>
    <h1 style="text-align: center">评论!</h1>
    <form action="${pageContext.request.contextPath}/comment" method="post">
        <textarea name="comment"></textarea>
        <input type="submit" value="提交评论"/>
    </form>
    <br/>
    <span style="color: red">${errorInfo}</span>
</body>

filter处理
@WebFilter(urlPatterns = "/comment",initParams = @WebInitParam(name="forbidden",value="滚,他妈的,去死,卧槽"))
public class CommentFilter implements Filter {
    private String[] forbiddens = null;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String forbidden = filterConfig.getInitParameter("forbidden");
        forbiddens = forbidden.split(",");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        String comment = request.getParameter("comment");
        for (String forbidden : forbiddens) {
            if (comment.contains(forbidden)){
                request.setAttribute("errorInfo","你输入的内容有不当评论");
                request.getRequestDispatcher("/header.jsp")
                        .forward(request,response);
                return;
            }
        }
        chain.doFilter(request,response);
    }
    @Override
    public void destroy() { }
}

Servlet Listener监听器

Listener监听器是实现特定监听接口的Java程序,这个程序专门用于监听另一个Java对象的状态变化(对象的创建销毁、方法调用、属性改变),当被监听对象的状态变化时监听器中的方法立即执行。

Servlet 规范中定义了 8 个监听器接口,可以用于监听 ServletContext、HttpSession 和 ServletRequest 对象的生命周期和属性变化事件。

监听器 Listener 按照监听的事件划分,可以分为 3 类:

  • 监听对象创建和销毁的监听器(ServletContextListener,HttpSessionListener,ServletRequestListener)
  • 监听对象中属性变更的监听器(ServletContextAttributeListener,HttpSessionAttributeListener,ServletRequestAttributeListener)
  • 监听 HttpSession 中的对象状态改变的监听器(HttpSessionBindingListener)

比如对于ServletRequestListener监听请求的创建和销毁,可用于监听某个IP访问网站的频率,如果频率过高做一些限制访问;也可以做日志记录,记录用户访问了哪些资源…

注册 Servlet 监听器有 2 种方式,分别是:

  • 在 web.xml 中注册监听器;
    <listener>
        <listener-class>全限定类名</listener-class>
    </listener>
  • 使用 @WebListener 注册监听器。
Servlet监听器统计网站在线人数

登录界面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<form action="/showLoginItems" method="get">
    <input type="text" name="username" placeholder="请输入用户名" />
    <input type="submit" value="登录" />
</form>
</body>
</html>
  • 使用 HttpSessionListener 和 HttpSessionAttributeListener 实现

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<form action="/showLoginItems" method="get">
    <input type="text" name="username" placeholder="请输入用户名" />
    <input type="submit" value="登录" />
</form>
</body>
</html>

展示所有用户列表

@WebServlet("/showLoginItems")
public class ShowLoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求响应编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        //获取表单中的数据
        String username = req.getParameter("username");
        out.write("<h2>欢迎你!"+username+"</h2>");
        //查看当前会话是否有用户登录
        String logined = (String) req.getSession().getAttribute("username");
        if (logined != null && !"".equals(logined)){
            out.write("<h1>编程帮 www.biancheng.net</h1>"
                    + "<h3>您好,您已经登录了账户:" + logined + "</h3>"
                    + "如要登录其他账号,请先退出当前账号重新登录!");
        }else {// 将当前账号加入会话中
            req.getSession().setAttribute("username", username);
        }
        // 获取已经登录的用户集合
        List<String> loginedUsers = (List<String>) super.getServletContext().getAttribute("loginedUsers");
        if (loginedUsers != null){
            out.write("<table border='1' cellspacing='0' cellpadding='3'><tr><td>当前在线人数:"+loginedUsers.size()+"</td></tr>\r\n");
            for (String loginedUser : loginedUsers) {
                out.write("<tr><td>用户:" + loginedUser + "</td><tr>\r\n");
            }
            out.write("</table><br/>" + "<a href='/LogoutServlet'>退出登录</a>");
        }
    }
}

监听Session和ServletContext的Attribute

@WebListener
public class OnlineListener implements HttpSessionListener, HttpSessionAttributeListener{
    // 监听用户登录
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {
        // 从上下文中获取已经登录的用户集合
        List<String> loginedUsers = (List<String>) event.getSession().getServletContext().getAttribute("loginedUsers");
        loginedUsers = loginedUsers==null || loginedUsers.size()==0 ? new ArrayList<>():loginedUsers;
        String userName = (String) event.getSession().getAttribute("username");
        // 将用户添加到已登录集合中
        loginedUsers.add(userName);
        System.out.println("用户:"+userName+"加入在线用户了");
        event.getSession().getServletContext().setAttribute("loginedUsers",loginedUsers);
    }
    // 监听用户退出
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        String username = (String) session.getAttribute("username");
        ServletContext context = session.getServletContext();
        List<String> loginedUsers = (List<String>) context.getAttribute("loginedUsers");
        if(username!=null && loginedUsers!=null && loginedUsers.size()>0){
            // 从在线列表中删除当前用户名
            loginedUsers.remove(username);
            System.out.println("用户:"+username+"退出了");
            System.out.println("-----------会话已经销毁了-----------");
        }
    }
}

用户登出:

@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //退出登录操作,将此次session进行销毁
        //触发HttpSessionListener监听器的sessionDestroyed方法
        request.getSession().invalidate();
        //跳转回登录页面
       response.sendRedirect("/login.jsp");
    }
}

结果:
chrome中登录:
在这里插入图片描述
Edge中登录:
在这里插入图片描述

  • 上面代码监听时也可以使用HttpSessionBindingListener , 用于监听 JavaBean 对象绑定到 HttpSession 对象和从 HttpSession 对象解绑的事件
    • valueBound ( event) 当对象被绑定(添加)到 HttpSession 对象中时触发
    • valueUnbound event) 当对象从 HttpSession 对象中解除绑定(移除)时触发

JSP

jsp = Java server page, 是一种动态开发web网页技术,就是在传统的html文件中插入Java代码和jsp标签。

jsp是servlet的扩展,可以在jsp中使用servlet的所有功能,jsp的本质就是servlet,只不过jsp更注重于页面网页的设计。用户访问 JSP 页面时,JSP 代码会被翻译成 Servlet 代码,最终,以字符串的形式向外输出 HTML 代码。所以,JSP 只是在 Servlet 的基础上做了进一步封装。

设计jsp的目的: servlet不能镶嵌 HTML 代码,因此输出 HTML 比较困难,jsp就是开发者为了客服这些而产生的。

jsp相对于servlet的优点:

  • 易于维护
    相对于 Servlet 来说,JSP 更易于管理。在 JSP 中,可以将业务逻辑与网页设计分开,而在 Servlet 技术中,它们是混合在一起的。
  • 快速开发:无需重新编译和部署
    JSP 页面被修改后,不需要重新编译和部署项目。而 Servlet 被修改后,需要重新编译和部署。

JSP编译
当浏览器请求 JSP 时,JSP 容器会首先检查是否需要编译页面。如果该页面从未被编译过,或者自上次编译以来对其进行了修改,则编译该页面。

编译过程包括 3 个步骤:

  • 解析 JSP:容器解析 JSP 文件,查看是否有语法错误
  • 翻译 JSP:容器把 JSP 文件翻译为 Servlet 类
  • 编译 Servlet

jsp页面基本格式:

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

其中:<%@ page %> 用来指定该页面的配置信息;contentType指定该网页类型为html以及字符编码为UTF-8; language=“java” 指定网页编程语言为Java;

<%= %> 等价于 <%out.print();%> ,是 JSP 中的输出语句;从格式上看jsp就是在HTML中添加一个头部声明。

JSP脚本:在jsp中可以通过jsp脚本写入Java代码,标准格式如下:

<jsp:scriptlet>
	Java代码
</jsp:scriptlet>
----------------------------
等价于:

<% Java代码 %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%= "Hello JSP" %>
    <%
        String name = request.getParameter("name");
        out.println("hello :"+name);
    %>
</body>

在这里插入图片描述
JSP 声明 语句用于声明一个或多个变量、方法,以供后面的 Java 代码使用。

<jsp:declaration>
声明语句
</jsp:declaration>
--------------------
等价于:
<%! 声明变量、方法等 %>
<%!
	int num = 0;
	Cricle c = new Cricle(2.0);
%>
jsp指令

目的:告诉web服务器如何处理jsp页面的请求和响应;以<%@ 开始,%>结尾。

常见的三个jsp指令:

  • <%@ page… %> 定义与页面相关的属性,比如脚本语言、页面编码、页面类型等
  • <%@ include… %> 引入其他jsp文件
  • <%@ taglib… %> 声明并导入其他标签库

page指令中常见的属性:

  • contentType : 声明页面的编码格式,页面类型
  • import : 导入Java的类库,比如<%@ import=“java.util.Date” %>
  • language: 指定脚本语言

使用import导入多个类库时加上逗号以分割:

<%@ page contentType="text/html;charset=UTF-8"
         language="java"
         import="java.util.Date,java.text.SimpleDateFormat"
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%!
        int num=112;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    %>
    <%
        out.println("my age is "+num);
        out.println("当前时间是:"+sdf.format(new Date()));
    %>
</body>
</html>

include指令的属性:

  • file 引入外部文件 <%@ include file="URL" %>
header.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1 style="text-align: center">这是头部!</h1>
</body>
</html>
---------------------------------
另一个jsp文件
<body>
    <%@ include file="header.jsp" %>
    <%!
        int num=112;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    %>
    <%
        out.println("my age is "+num);
        out.println("当前时间是:"+sdf.format(new Date()));
    %>
</body>

taglib指令
语法:<%@ taglib="taglibUrl" prefix="taglibPre" %>

url :指标签库的引用地址,prefix指定标签库的前缀

Jsp动作:

<jsp:forward> 动作用来将请求转发到另一个页面中,请求的参数数据会被一起转发到目标页面。

<jsp:forward page="url"/>

<jsp:param> 动作用来传递请求参数信息:

	<jsp:forward page="index.jsp">
        <jsp:param name="name" value="编程" />
        <jsp:param name="age" value="443" />
    </jsp:forward>

<jsp:useBean> 用于获取 Bean 对象。

<jsp:useBean id = "name" class = "package.class" scope= "page | request | session | application" />

其中id表示创建对象的变量名,class表示对象的全限定名,scope表示对象的可用范围
Jsp内置对象

JSP 内置对象又称为隐式对象,它们由容器实现和管理。

  • request : 获取用户请求信息
  • response: 可将处理的信息封装返回给客户端
  • session :保存/获取用户信息
  • out : 输出内容到html
  • application : 共享所有用户的信息
  • config : 用户的初始化信息
  • page : 当前的页面,类似Java中的this
  • pageContext : 当前页面的页面容器,用于访问 page、request、application 和 session 的属性
  • exception:JSP 文件执行时发生的错误和异常(在 JSP 页面的 page 指令中指定 isErrorPage=true才可使用)

其中out对象常用方法:

 print()	将内容直接打印在 HTML 标签中
 println()	类似于 print,唯一区别是 println 方法添加了换行符
 newLine()	输出换行字符
 clear()	清除页面缓冲区
jsp el表达式

使用jsp<% %>脚本输出变量传递数据,降低页面的可读性,为了简化页面jsp提供了el表达式,可以以更简洁、方便的形式来访问变量和参数。

el表达式语法:

${变量/表达式}

demo:

@WebServlet(value = "/elTest")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        Student stu = new Student("张三", 18, "1班");
        req.setAttribute("stu", stu);
        String[] arr = {"语文:99","数学:123"};
        req.setAttribute("score", arr);
        req.setAttribute("name", "Brett");
        req.getRequestDispatcher("/firstPage.jsp").forward(req, resp);
    }
}
<%
    request.setAttribute("age",33);
%>
	<div>
        <span>${name}</span> <!-- 获取普通属性 -->
        <span>${age}</span>
        <br>班级:<span>${stu.cls}</span> <!-- 获取对象属性 -->
        <br><span>成绩:${score[0]}</span> <!-- 获取数组属性 -->
        <br><span>成绩:${score[1]}</span>
        <br>学生:<span>${empty stu? "空对象": stu.name}</span> <!--判断对象是否为空-->
    </div>

获取项目的应用程序名

<form action="${pageContext.request.contextPath}/xxxServlet"></form>
jstl 核心库

jstl : jsp通用标签库,它封装了 JSP 应用的通用核心功能。
使用前需要引入标签库 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

标签属性使用:

  • <c:set> 设置对象/变量属性值
	<c:set var="names" value="佳妮"></c:set>
    <div>
        <span>my name is ${names}</span>
    </div>
  • <c:if> 条件判断
<body>
<%
    request.setAttribute("age",33);
%>
    <c:if test="${age>=18}">  <!-- test表示判断条件 -->
        <p>你已经成年了</p>
    </c:if>
    <c:if test="${age<18}">
        <p>你还是未成年人</p>
    </c:if>
</body>
  • <c:choose> 条件匹配,类似于Java中switch-case
<%
    request.setAttribute("age",33);
%>
    <c:choose>
        <c:when test="${40>age and age>=18}">
            <div>成年人</div>
        </c:when>
        <c:when test="${age<18}">
            <div>未成年</div>
        </c:when>
        <c:otherwise>  <!-- 两个条件都不满足时输出 -->
            <div>少年</div>
        </c:otherwise>
    </c:choose>
  • <c:forEach> 循环遍历集合中的数据
    Servlet中:
protected void doGet(HttpServletRequest req, HttpServletResponse resp{
        String[] arr = {"语文:99","数学:123","英语:91"};
        req.setAttribute("arr",arr);
        req.getRequestDispatcher("/firstPage.jsp").forward(req, resp);
    }

jsp中:其中items是遍历的集合对象,var是当前对象的别名

<c:forEach var="item" items="${arr}">
    <div>${item}</div>
</c:forEach>

什么是ThreadLocal?

ThreadLocal是jdk中一个非常有用的类,从名字上来看叫做线程变量

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值