java-servlet的学习

概念

Servlet含义是服务器端的小程序 在整个Web应用中,Servlet主要负责处理请求、协调调度功能。我们可以把Servlet称为Web应用中的控制器

具体功能:

  1. 接收请求
  2. 处理请求(调用业务逻辑 service 方法)
  3. 分发页面(转发重定向)
  4. 返回响应

生命周期

servlet 的生命周期:

  1. 创建对象:第一次请求和修改Web应用启动时,只会经历一次
  2. 初始化:创建对象后,调用方法 init(ServletConfig servletConfig),只会经历一次
  3. 处理请求 :接收到请求后,调用方法service(ServletRequest servletRequest, ServletResponse servletResponse),会经历多次
  4. 清理操作:Web应用卸载之前,调用方法destroy(),只会经历一次

总结: Servlet 第一次接收请求时,会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service()),从接收第二次请求开始,每一次都是服务

默认情况下:Servlet在第一次接收到请求的时候才创建对象,不过可修改 servelt 创建对象的时机

修改方法:修改web.xml 配置文件的 Servlet配置

<servlet>
  <!-- 配置名称 -->
  <servlet-name>HelloServlet</servlet-name>
  <!-- 配置全类名 -->
  <servlet-class>com.example.servlet.HelloServlet</servlet-class>
  <!-- 配置启动顺序 -->
  <load-on-startup>1</load-on-startup>
</servlet>

HttpServlet和Servlet继承关系

创建Servlet

首先导入依赖

两种创建方法:

​ 实现 Servlet 接口

​ 继承 HttpServlet 类

实现 Servlet 接口 ,对应生命周期方法

@WebServlet("/hello")
public class HelloServlet implements Servlet {
  // 初始化方法
  @Override
  public void init(ServletConfig servletConfig) throws ServletException {}
  // 每次servlet被访问 调用该方法
  @Override
  public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    System.out.println("hello servlet");
  }
  // ServletConfig 对象
  @Override
  public ServletConfig getServletConfig() {return null;}
  // 获取 Servlet 信息
  @Override
  public String getServletInfo() {return null;}
  // servlet 销毁调用的方法
  @Override
  public void destroy() {}
}

继承 HttpServlet 重写 doGet, doPost等方法

根据你的请求方法,调用不同的 doXXX 方法

Servlet 和 HttpServlet关系图

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
  // 重写 doGet
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.printIn("git 请求");
  }
  // 重写 doPost
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.printIn("post 请求");
  }
}

request 和 response 对象

  • request就是将请求文本封装而成的对象,所以通过request能获得请求文本中的所有内容,请求头、请求体、请求行 。常用来获取客户端传过来的参数信息。转发请求到另一个地址
  • 客户端请求后响应请求的对象,通常可设置响应头,实现页面重定向,定时刷新,设置cookie等功能

访问路径配置

两种方法:

注解 @WebServlet

xml配置文件

@WebServlet

  • name:Servlet的名称,默认为该类的简单类名;
  • value/urlPatterns:定义Servlet处理的请求URL模式;
  • loadOnStartup:指示Servlet容器在应用程序启动时是否预加载Servlet;
  • initParams:Servlet初始化参数;
  • description:Servlet描述信息;
  • asyncSupported:表明该Servlet是否支持异步操作。

通过注解配置访问路径 如 @WebServlet(“\hello”) 通过访问项目地址+ \hello 访问

可配置多路径 如 @WebServlet(urlPatterns={“/hello1”, “hello2”})

支持通配符 * 配置 如 @WebServlet(“/hello/* “),@WebServlet(“ *.do “)

web.xml

<!-- 配置 servlet 信息 -->
<servlet>
  <!-- 配置名称 -->
  <servlet-name>HelloServlet</servlet-name>
  <!-- 配置全类名 -->
  <servlet-class>com.example.servlet.HelloServlet</servlet-class>
</servlet>
<!-- 配置 servlet 路径 -->
<servlet-mapping>
	<!-- 找到上面配置的 servlet 名称,配置上对应的路径-->
  <servlet-name>HelloServlet</servlet-name>
  <url-pattern>/hello</url-pattern>
</servlet-mapping>

ServletConfig与ServletContext

类比

类比

ServletConfig

可直接通过方法getServletConfig()获取,它在Servlet初始化的时候被创建,通过这个对象我们可以获取到servletName,ServletContext对象,初始化参数等

ServletConfig 接口

方法 描述
getServletName() 获取 servlet-name 标签定义的名字
getServletContext() 获取 ServletContext 对象
getInitParameter() 获取配置Servlet时设置的『初始化参数』,根据名字获取值
getInitParameterNames() 获取所有初始化参数名组成的Enumeration对象

代码举例

// 配置 初始化参数
@WebServlet(urlPatterns = "/hello",initParams = {
  @WebInitParam(name = "encoding", value = "UTF8"),
  @WebInitParam(name = "hello", value = "world")
})
public class HelloServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取 servletConfig 对象
    ServletConfig servletConfig = this.getServletConfig();
    // 获取初始化参数
    String encoding = servletConfig.getInitParameter("encoding");
    String hello = servletConfig.getInitParameter("hello");
    System.out.println(encoding);
    System.out.println(hello);

    // 获取所有初始化参数名
    Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
    while (initParameterNames.hasMoreElements()) {
      // 拿到每一个参数名
      String name = initParameterNames.nextElement();
      System.out.println("name = " + name);
      // 通过拿到的参数名获取对应的 value
      String initParameter = servletConfig.getInitParameter(name);
      System.out.println("value = "+initParameter);
    }
    
  }
}

配置 初始化参数 方法2 xml配置文件

<servlet>
  <servlet-name>hello</servlet-name>
  <servlet-class>org.example.servlet.TestServlet</servlet-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>hello</param-name>
    <param-value>world</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>hello</servlet-name>
  <url-pattern>/hello</url-pattern>
</servlet-mapping>

ServletContext

代表:整个Web应用。ServletContext对象的生命周期和整个Web应用的生命周期一致

ServletContext对象的作用:

  1. 有了ServletContext对象,就可以共享从应用程序中的所有资源访问到的数据信息,并且可以动态注册web对象
  2. 可以获得应用域的全局初始化参数,以及达到Servlet之间的数据共享
  3. 可以作为域对象在整个应用中共享数据;域对象即在一定的作用范围内实现资源共享
  4. 可以用来获取应用中的资源在服务器上的绝对路径
  5. 获取文件的mime类型: 在网络传输中,并不是以扩展名来区分文件的类型,都是以mime类型;如:text/html;表示一个html文件

域对象

在Servlet规范中,一共有4个域对象。request请求域PageContext页面域session会话域,application应用域。ServletContext就是其中的一个。是web应用中最大的作用域,也叫application应用域,可以实现整个应用之间的数据共享

常用方法:

  • getRealPath():获取某个资源的真实路径
  • getInitParameter(): 获取整个Web应用级别的初始化参数
  • setAttribute() / getAttribute():Web应用范围的域对象存入/取出对象

Web应用级别的初始化参数

<!-- 配置Web应用的初始化参数 -->
<context-param>
  <param-name>handsomeMan</param-name>
  <param-value>alsoMe</param-value>
</context-param>

获取方法

String handsomeMan = servletContext.getInitParameter("handsomeMan");
System.out.println("handsomeMan = " + handsomeMan);

init-param 区别

<init-param>是放在一个servlet内的,所以这个参数是只针对某一个servlet而言的

所以他们的区别就有点像全局变量和和局部变量的<context-param>是针对整个项目,所有的servlet都可以取得使用,<init-param>只能是在那个servlet下面配置,就在那个servlet里面调用

转发和重定向

转发

转发定义 在请求的处理过程中,Servlet完成了自己的任务,需要把请求转交给下一个资源继续处理

关键:由于转发操作的核心部分是在服务器端完成的,所以浏览器感知不到,整个过程中浏览器只发送一次请求

request.getRequestDispatcher("/转发的地址").forward(request, response);

重定向

定义:在请求的处理过程中,Servlet完成了自己的任务,然后以一个响应的方式告诉浏览器:要完成这个任务还需要你另外再访问下一个资源

代码

response.sendRedirect("/重定向的地址");

关键:由于重定向操作的核心部分是在浏览器端完成的,所以整个过程中浏览器共发送两次请求

转发和重定向的应用场景

可以简单的判断:能用转发的先用转发,如果转发不行,再使用重定向。

  • 需要通过同一个request对象把数据携带到目标资源:只能用转发
  • 如果希望前往下一个资源之后,浏览器刷新访问的是第二个资源:只能用重定向

请求参数

获取请求参数的常用方法

方法 描述
Map<String, String[]> request.getParameterMap() 获取包含全部请求参数的Map
String request.getParameter(“请求参数的名字”) 获取参数名称对应的值
String [] request.getParameterValues(“请求参数的名字”) 获取参数名称对应的值,常用对 checkbox 提交的数据
Enumeration request.getParameterNames() 获取所有参数名组成的Enumeration对象

请求响应设置字符集

设置字符集避免乱码问题

post 请求,在处理请求的方法里写在最前面

// 使用request对象设置字符集
request.setCharacterEncoding("UTF-8");

get 新版本内部处理好了

响应设置

response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.write("hello world!");

Session 会话

客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端下次客户端给服务器发请求时,会把 sessionID 带给服务器,服务器就能获取到,就判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端

常用方法

方法 描述
request.getSession() 获取当前的会话,没有则创建一个新的会话
request.getSession(true) 效果和不带参数相同
request.getSession(false) 获取当前会话,没有则返回null,不会创建新的
session.getId() 获取sessionID
session.isNew() 判断当前session是否是新的
ession.getMaxInactiveInterval() 获取 session的非激活间隔时长,默认1800秒
session.setMaxInactiveInterval() 设置 session的非激活间隔时长
session.invalidate() 强制性让会话立即失效

session保存作用域

session保存作用域是和具体的某一个session对应的

区分其他作用域

  • request:一次请求响应范围
  • session:一次会话范围有效
  • application: 一次应用程序范围有效

常用的API:

  • void session.setAttribute(k,v):向当前会话存储数据
  • Object session.getAttribute(k):根据 key 向当前会话获取数据
  • void removeAttribute(k) :根据 key 向当前会话移除数据

代码举例

public class HelloWorldServlet extends HttpServlet {
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    // 1.通过request对象获取session对象
    HttpSession session = request.getSession();

    // 2.设置数据名称和数据的值
    String attrName = "sessionHelloAttrName";
    String attrValue = "sessionHelloAttrValue";

    // 3.将数据存入会话域
    session.setAttribute(attrName, attrValue);

  }
}

在浏览器端临时存储数据

键值对

键和值都是字符串类型

数据量很小

没有 cookie: 在服务器端没有创建Cookie并返回的情况下,浏览器端不会保存Cookie信息。双方在请求和响应的过程中也不会携带Cookie的数据

创建和读取

创建 cookie 返回

// 1.创建Cookie对象
Cookie cookie = new Cookie("cookie-message", "hello-cookie");

// 2.将Cookie对象添加到响应中
response.addCookie(cookie);

// 3.返回响应
processTemplate("page-target", request, response);

浏览器拿到Cookie之后,以后的每一个请求都会携带Cookie信息

读取 cookie

// 1.通过request对象获取Cookie的数组
Cookie[] cookies = request.getCookies();

// 2.遍历数组
for (Cookie cookie : cookies) {
  System.out.println("cookie.getName() = " + cookie.getName());
  System.out.println("cookie.getValue() = " + cookie.getValue());
}

Cookie的时效性

会话级Cookie:默认情况

  • 保存在浏览器的内存里,在浏览器关闭时会被释放

持久化Cookie:

  • 服务器端调用Cookie的setMaxAge(以秒为单位的存活时间)方法
    • 存活时间正数:还能够存活的秒数
    • 存活时间为零:告诉浏览器立即删除这个Cookie
    • 存活时间为负:告诉浏览器这个Cookie是一个会话级Cookie
  • 服务器把这个Cookie返回给浏览器时就等于通知浏览器:这个Cookie还能存在多长时间

给Cookie设置过期时间

// 正数:Cookie的过期时间,以秒为单位
// 负数:表示这个Cookie是会话级的Cookie,浏览器关闭时释放
// 0:通知浏览器立即删除这个Cookie
cookie.setMaxAge(20);

domain和path

上网时间长了,本地会保存很多Cookie。对浏览器来说,访问互联网资源时不能每次都把所有Cookie带上。浏览器会使用Cookie的domain和path属性值来和当前访问的地址进行比较,从而决定是否携带这个Cookie。

  • domain:表示这个Cookie属于哪个网站,通常以域名为值
  • path:表示这个Cookie属于网站下的哪一个具体的资源

filter 过滤器

过滤器三要素

  1. 拦截:把请求拦截下来才能够做后续的操作
  2. 过滤:业务代码处理
  3. 放行:让请求继续去访问它原本要访问的资源

使用步骤

  1. 实现javax.servlet.Filter接口
  2. 在 doFilter()方法执行过滤
  3. 处理完:chain.doFilter(request, response)放行,或者进行重定向或转发

代码

@WebFilter("*.do")
public class HelloFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {}

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    System.out.println("helloA");
    //放行
    filterChain.doFilter(servletRequest,servletResponse);
    System.out.println("helloA2");
  }

  @Override
  public void destroy() {}
}

配置 filter url 像servlet 一样可以采用注解和 web.xml 配置文件

可配置通配符,例如 @WebFilter(“*.do”)表示拦截所有以 .do 结尾的请求

xml配置

<filter>
  <!-- 配置Filter的名称 -->
  <filter-name>HelloFilter</filter-name>

  <!-- 配置Filter的全类名,便于Servlet容器创建Filter对象 -->
  <filter-class>com.example.filter.HelloFilter</filter-class>
</filter>

<!-- 配置Filter要拦截的目标资源 -->
<filter-mapping>
  <!-- 指定这个mapping对应的Filter名称 -->
  <filter-name>HelloFilter</filter-name>

  <!-- 通过请求地址模式来设置要拦截的资源 -->
  <url-pattern>/HelloFilter</url-pattern>
</filter-mapping>

多个 filter 执行顺序

  1. 注解的方式进行配置:按照全类名字母排序执行
  2. xml的方式进行配置:按照配置文件的顺序

生命周期

生命周期环节 调用的方法 时机 次数
创建对象 无参构造器 默认:Web应用启动时 一次
初始化 init(FilterConfig filterConfig) 创建对象后 一次
处理请求 doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 接收到请求后 多次
清理操作 destroy() Web应用卸载之前 一次

监听器

概念

Servlet监听器:Servlet规范中定义的一种特殊类,它用于监听Web应用程序中的ServletContext,HttpSession 和HttpServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件

监听对象:

  1. ServletContext:application,整个应用只存在一个

  2. HttpSession:session,针对每一个对话

  3. ServletRequest:request,针对每一个客户请求

监听内容:创建、销毁、属性改变事件
监听作用:可以在事件发生前、发生后进行一些处理,一般可以用来统计在线人数和在线用户、统计网站访问量、系统启动时初始化信息等。

分类

监听域对象本身

  • ServletContext
  • HttpSession
  • HttpServletRequest

监听域对象的属性域

  • 应用域
  • 会话域
  • 请求域

监听存入Session域的对象本身

  • 对象是否存入会话域
  • 对象是否和Session一起被钝化到了硬盘上

监听器列表

对象的创建与销毁

ServletContextListener

作用:监听ServletContext对象的创建与销毁

方法名 作用
contextInitialized(ServletContextEvent sce) ServletContext创建时调用
contextDestroyed(ServletContextEvent sce) ServletContext销毁时调用

ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象

HttpSessionListener

作用:监听HttpSession对象的创建与销毁

方法名 作用
sessionCreated(HttpSessionEvent hse) HttpSession对象创建时调用
sessionDestroyed(HttpSessionEvent hse) HttpSession对象销毁时调用

HttpSessionEvent对象代表从HttpSession对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpSession对象

ServletRequestListener

作用:监听ServletRequest对象的创建与销毁

方法名 作用
requestInitialized(ServletRequestEvent sre) ServletRequest对象创建时调用
requestDestroyed(ServletRequestEvent sre) ServletRequest对象销毁时调用

ServletRequestEvent对象代表从HttpServletRequest对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpServletRequest对象。另外还有一个方法可以获取到当前Web应用的ServletContext对象

属性创建、修改和销毁

ServletContextAttributeListener

作用:监听ServletContext中属性的创建、修改和销毁

方法名 作用
attributeAdded(ServletContextAttributeEvent scab) 向ServletContext中添加属性时调用
attributeRemoved(ServletContextAttributeEvent scab) 从ServletContext中移除属性时调用
attributeReplaced(ServletContextAttributeEvent scab) 当ServletContext中的属性被修改时调用

ServletContextAttributeEvent对象代表属性变化事件,它包含的方法如下

方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getServletRequest () 获取触发事件的ServletRequest对象

HttpSessionBindingListener

作用:监听某个对象在Session域中的创建与移除

方法名 作用
valueBound(HttpSessionBindingEvent event) 该类的实例被放到Session域中时调用
valueUnbound(HttpSessionBindingEvent event) 该类的实例从Session中移除时调用

HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取当前事件涉及的属性名
getValue() 获取当前事件涉及的属性值
getSession() 获取触发事件的HttpSession对象

HttpSessionActivationListener

作用:监听某个对象在Session中的序列化与反序列化。

方法名 作用
sessionWillPassivate(HttpSessionEvent se) 该类实例和Session一起钝化到硬盘时调用
sessionDidActivate(HttpSessionEvent se) 该类实例和Session一起活化到内存时调用

HttpSessionEvent对象代表事件对象,通过getSession()方法获取事件涉及的HttpSession对象

简单举例:监听ServletContext对象的创建与销毁

@WebListener
public class MyServletContextListener implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent servletContextEvent) {
    System.out.println("Servlet上下文对象初始化动作被我监听到了....");
  }

  @Override
  public void contextDestroyed(ServletContextEvent servletContextEvent) {
    System.out.println("Servlet上下文对象销毁动作被我监听到了.....");
  }
}

配置添加 @WebListener 或配置xml文件

<listener>
  <listener-class>com.example.listener.MyServletContextListener</listener-class>
</listener>

参考资料

尚硅谷教学

代码重工