sun公司开发动态web的一门技术,是服务器三大组件(servlet,filter,listener)之一,主要用来处理请求和响应
如果想开发 Servlet 程序,需要完成
最后实现的效果是,当url处输入"/hello"时,Servelt将会进行拦截跳转并执行相关的方法
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>1_Servlet_testartifactId>
<version>1.0version>
<packaging>warpackaging>
<name>1_Servlet_test Maven Webappname>
<url>http://www.example.comurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.7maven.compiler.source>
<maven.compiler.target>1.7maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
<scope>providedscope>
dependency>
dependencies>
<build>
<finalName>1_Servlet_testfinalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-pluginartifactId>
<version>3.1.0version>
plugin>
<plugin>
<artifactId>maven-resources-pluginartifactId>
<version>3.0.2version>
plugin>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.0version>
plugin>
<plugin>
<artifactId>maven-surefire-pluginartifactId>
<version>2.22.1version>
plugin>
<plugin>
<artifactId>maven-war-pluginartifactId>
<version>3.2.2version>
plugin>
<plugin>
<artifactId>maven-install-pluginartifactId>
<version>2.5.2version>
plugin>
<plugin>
<artifactId>maven-deploy-pluginartifactId>
<version>2.8.2version>
plugin>
plugins>
pluginManagement>
build>
project>
DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<servlet>
<servlet-name>Firstservlet-name>
<servlet-class>ServletDemo01servlet-class>
servlet>
<servlet-mapping>
<servlet-name>Firstservlet-name>
<url-pattern>/hellourl-pattern>
servlet-mapping>
web-app>
import javax.servlet.*;
import java.io.IOException;
/**
* @author 13544
*/
public class ServletDemo01 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
// 生命周期方法:当Servlet第一次被创建对象时执行该方法,该方法在整个生命周期中只执行一次
}
@Override
public ServletConfig getServletConfig() {
// 当停止tomcat时也就销毁的servlet
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 处理请求和响应
System.out.println("hello world");
// ServletRequest 和 ServletResponse
// ServletRequest:Servlet容器对于接受到的每一个Http请求,都会创建一个ServletRequest对象,并把这个对象传递给Servlet的Sevice( )方法
// ServletResponse:表示一个Servlet响应,在调用Servlet的Service( )方法前,Servlet容器会先创建一个ServletResponse对象,并把它作为第二个参数传给Service( )方法。ServletResponse隐藏了向浏览器发送响应的复杂过程
}
@Override
public String getServletInfo() {
// 提供有关servlet 的信息,如作者、版本、版权
return null;
}
@Override
public void destroy() {
// 生命周期方法:当Servlet被销毁时执行该方法
}
}
基本概述和相关方法
public class ServletDemo01 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
// 生命周期方法:当Servlet第一次被创建对象时执行该方法,该方法在整个生命周期中只执行一次
}
@Override
public ServletConfig getServletConfig() {
// 当停止tomcat时也就销毁的servlet
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 处理请求和响应
// 每当请求Servlet时,Servlet容器就会调用这个方法
}
@Override
public String getServletInfo() {
// 返回一段描述
// 可以提供有关servlet 的信息,如作者、版本、版权
return null;
}
@Override
public void destroy() {
// 生命周期方法:当Servlet被销毁时执行该方法
}
}
配置Servelt提前创建
有时候我们需要在Servlet创建的时候做一些资源加载等等耗时操作,所以如果Servlet在第一次接收请求的时候才创建的话必然会影响用户的访问速度,所以此时我们需要让Servlet提前创建,将Servlet的创建提前到服务器启动的时候。
通过修改web.xml中Servlet的配置可以实现:
<servlet>
<servlet-name>HelloServletservlet-name>
<servlet-class>com.atguigu.servlet.HelloServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
Servlet接口有一个实现类是GenericServlet,而GenericServlet有一个子类是HttpServlet,我们创建Servlet的时候会选择继承HttpServlet,因为它里面相当于也实现了Servlet接口,并且对一些方法做了默认实现;而且子类的功能会比父类的更加强大
我们编写Servlet类继承HttpServlet的时候,只需要重写doGet()和doPost()方法就行了,因为HttpServlet重写了service()方法,在service()方法中判断请求方式,根据不同的请求方式执行doXXX()方法
Servlet上下文,整个项目共享这一个对象,它伴随着Servlet整个的生命周期
ServletContext servletContext1 = getServletContext();
ServletContext servletContext3 = req.getServletContext();
ServletContext玩法简介
请求
在Servlet API中,定义了一个HttpServletRequest接口,它专门用来封装HTTP请求消息
用我们自己的话来理解: Request就是服务器中的一个对象,该对象中封装了HTTP请求的 请求行、请求头和请求体 的内容
这个对象包含三个部分
而Request能干嘛呢?
getMethod() 获取请求方式,如get,post
getContextPath() 获得当前应用上下文路径 ,就是那个项目路径类似
getRequestURI() 获得请求地址,不带主机名
getRequestURL() 获得请求地址,带主机名
// 根据请求头的name获取value
// 目标:获取name为user-agent的请求头的信息
// user-agent请求头中包含的是客户端浏览器信息
getHeader(String name), 根据请求头的name获取请求头的值
// 我的理解就是提交表单的时候,用来获取用户的一些账户密码校验信息
请求参数是客户端携带给服务器的由键值对组成的数据,例如"username=aobama&password=123456"这种类型的数据
携带请求参数有如下形式
URL地址后面附着的请求参数,例如http://localhost:8080/app/hellServlet?username=汤姆
表单携带请求参数
Ajax请求携带请求参数(不知道是什么,后面回来看看)
方法名 | 返回值类型 | 方法描述 |
---|---|---|
request.getParameterMap() | Map |
获取当前请求的所有参数,以键值对的方式存储到Map中 |
request.getParameter(“请求参数的名字”) | String | 根据一个参数名获取一个参数值 |
request.getParameterValues(“请求参数的名字”) | String [] | 根据一个参数名获取多个参数值 |
request.getParameterNames() | Enumeration | 获取当前请求的所有参数的参数名 |
示例表单代码
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>发送请求携带请求参数title>
head>
<body>
<a href="/webday0601/demo02?username=aobama&password=123456">访问ServletDemo02a><br/>
<form action="/webday0601/demo02" method="get">
用户名<input type="text" name="username"/><br/>
密码<input type="text" name="password"/><br/>
昵称<input type="text" name="nickname"/><br/>
邮箱<input type="text" name="email"/><br/>
兴趣爱好<input type="checkbox" name="hobby" value="basketball"/>篮球
<input type="checkbox" name="hobby" value="football"/>足球
<input type="checkbox" name="hobby" value="yumaoball"/>羽毛球
<input type="checkbox" name="hobby" value="pingpangball"/>乒乓球<br/>
<button type="submit">提交button>
form>
body>
html>
解决POST请求的参数乱码只需要在获取请求参数前调用request.setCharacterEncoding("UTF-8")
就行了
请求转发是从一个资源跳转到另一个资源,在这个过程中客户端不会发起新的请求
// 操作方法
request.getRequestDispatcher("路径").forward(request,response);
案例代码
ServletDemo03的代码
public class ServletDemo03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ServletDemo03执行了...")
//请求转发跳转到ServletDemo04
request.getRequestDispatcher("/demo04").forward(request, response);
}
}
ServletDemo04的代码
public class ServletDemo04 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ServletDemo04执行了...")
}
}
特征点
我们之前学过全局域的范围(ServletContext),全局域是整个项目范围的所有动态资源都能够共享的一个范围;而请求域的范围只是在一次请求中的动态资源能够共享的一个范围
相关API
request.setAttribute(key,value)
request.getAttribute(key)
案例代码
ServletDemo03的代码
public class ServletDemo03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ServletDemo03执行了...")
String username = "aobama";
//将username存储到request域对象中
request.setAttribute("username",username);
//请求转发跳转到ServletDemo04
request.getRequestDispatcher("/demo04").forward(request, response);
}
}
ServletDemo04的代码
public class ServletDemo04 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = (String)request.getAttribute("username");
System.out.println("在ServletDemo04中获取username:"+username)
}
}
响应
在Servlet API中,定义了一个HttpServletResponse接口(doGet,doPost方法的参数),它继承自ServletResponse接口,专门用来封装HTTP响应消息。由于HTTP响应消息分为响应行、响应头、响应体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应头、响应体的方法
用我们自己的话说: Response就是服务器端一个对象,它里面可以封装要响应给客户端的响应行、头、体的信息
这个对象包含三个部分
能做些什么?
由于服务器端在输出内容的时候进行编码使用的字符集和客户端进行解码的时候使用的字符集不一致,所以会发生响应数据乱码问题。
我们解决响应数据乱码问题只需要在获取字符输出流之前,执行如下代码就可以了:
// 输出的文本/超文本内容的编码格式设置为UTF-8
response.setContentType("text/html;charset=UTF-8");
Servlet基础之HttpServletResponse详解
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 响应resp 一些方法的使用 resp.setStatus(403);状态码
// resp.setContentType("text/html;charset=UTF-8");
// 踩坑,如果输出的是图片,设置UTF-8会乱码
String realPath = getServletContext().getRealPath("WEB-INF/m2a.jpg");
FileInputStream is = new FileInputStream(realPath);
byte[] buffer = new byte[1024];
int len = 0;
ServletOutputStream os = resp.getOutputStream();
while ((len = is.read(buffer)) != -1) {
//写
os.write(buffer, 0, len);
}
os.close();
is.close();
}
重定向是由项目中的一个资源跳转到另一个资源,在这个过程中客户端会发起新的请求
response.sendRedirect("路径");
案例代码
ServletDemo02的代码
public class ServletDemo01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ServletDemo02执行了...")
//请求转发跳转到ServletDemo02
response.sendRedirect("/webday0602/demo03");
}
}
ServletDemo03的代码
public class ServletDemo03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("在ServletDemo03执行了.....")
}
}
重定向的特征
重定向和请求转发的对比
相关API
new Cookie(String name,String value);
response.addCookie(cookie);
request.getCookies() ; //得到所有的cookie对象。是一个数组,开发中根据key得到目标cookie
cookie.getName() ; //返回cookie中设置的key
cookie.getValue(); //返回cookie中设置的value
案例代码
在ServletDemo01中创建Cookie数据并响应给客户端
public class ServletDemo01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 创建一个cookie对象,用于存放键值对
Cookie cookie = new Cookie("cookie-message","hello-cookie");
//2. 将cookie添加到response中
//底层是通过一个名为"Set-Cookie"的响应头携带到浏览器的
response.addCookie(cookie);
}
}
ServletDemo02获取Cookie数据的代码
public class ServletDemo02 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 从请求中取出cookie
//底层是由名为"Cookie"的请求头携带的
Cookie[] cookies = request.getCookies();
//2. 遍历出每一个cookie
if (cookies != null) {
for (Cookie cookie : cookies) {
//匹配cookie的name
if (cookie.getName().equals("cookie-message")) {
//它就是我们想要的那个cookie
//我们就获取它的value
String value = cookie.getValue();
System.out.println("在ServletDemo02中获取str的值为:" + value);
}
}
}
}
}
相关API
// 获得session(如果第一次调用的时候其实是创建session,第一次之后通过sessionId找到session进行使用)
request.getSession();
// 获取值
Object getAttribute(String name) ;
// 存储值
void setAttribute(String name, Object value) ;
// 移除值
void removeAttribute(String name) ;
// 强制Session立即失效
session.invalidate();
// 获取默认的最大闲置时间
int maxInactiveIntervalSecond = session.getMaxInactiveInterval();
// 设置默认的最大闲置时间
session.setMaxInactiveInterval(15);
// 1.调用request对象的方法尝试获取HttpSession对象
HttpSession session = request.getSession();
// 2.调用HttpSession对象的isNew()方法
boolean wetherNew = session.isNew();
// 3.打印HttpSession对象是否为新对象
System.out.println("wetherNew = " + (wetherNew?"HttpSession对象是新的":"HttpSession对象是旧的"));
// 4.调用HttpSession对象的getId()方法
String id = session.getId();
// 5.打印JSESSIONID的值
System.out.println("JSESSIONID = " + id);
拦截器,Filter 不是一个servlet,它不能产生一个response,但是它能够在一个request到达servlet之前预处理request,也可以在 response离开servlet时处理response。换句话说,filter其实是客户端与servlet中间的一个传递者,并且它可以对要传递 的东西进行修改。
效果:访问被拦截器拦的链接时会进入后端拦截器
Web.xml配置拦截器
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<servlet>
<servlet-name>Firstservlet-name>
<servlet-class>servlet.ServletDemo01servlet-class>
servlet>
<servlet-mapping>
<servlet-name>Firstservlet-name>
<url-pattern>/hellourl-pattern>
servlet-mapping>
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>servlet.FilterDemo01filter-class>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
FilterDemo01.java
package servlet;
import java.util.logging.LogRecord;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 13544
*/
@WebFilter("/*") // 访问所有资源前都会被拦截
public class FilterDemo01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("FilterDemo01-init....");
// Filter.super.init(filterConfig);
}
@Override
public void destroy() {
System.out.println("FilterDemo01-destroy");
// Filter.super.destroy();
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("UTF-8");
//
System.out.println("FilterDemo01-doFilter.....");
//这句代码表示放行
filterChain.doFilter(servletRequest, servletResponse);
}
}
生命周期阶段 | 执行时机 | 生命周期方法 |
---|---|---|
创建对象 | Web应用启动时 | init方法,通常在该方法中做初始化工作 |
拦截请求 | 接收到匹配的请求 | doFilter方法,通常在该方法中执行拦截过滤 |
销毁 | Web应用卸载前 | destroy方法,通常在该方法中执行资源释放 |
<url-pattern>/*url-pattern>
<url-pattern>/hellourl-pattern>
<url-pattern>*.pngurl-pattern>
<url-pattern>Firsturl-pattern>
一个请求可能被多个过滤器所过滤,只有当所有过滤器都放行,请求才能到达目标资源,如果有某一个过滤器没有放行,那么请求则无法到达后续过滤器以及目标资源,多个过滤器组成的链路就是过滤器链
过滤器链中每一个Filter执行的顺序是由web.xml中filter-mapping配置的顺序决定的。如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低
监听器分类
ServletContextListener是监听ServletContext对象的创建和销毁的,因为ServletContext对象是在服务器启动的时候创建、在服务器关闭的时候销毁,所以ServletContextListener也可以监听服务器的启动和关闭
将来学习SpringMVC的时候,会用到一个ContextLoaderListener,这个监听器就实现了ServletContextListener接口,表示对ServletContext对象本身的生命周期进行监控。
创建监听器类
package com.atguigu.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* 包名:com.atguigu.listener
*
* @author Leevi
* 日期2021-06-19 10:26
* 编写监听器的步骤:
* 1. 写一个类实现对应的:Listener的接口(我们这里使用的是ServletContextListener),并且实现它里面的方法
* 1.1 contextInitialized()这个方法在ServletContext对象被创建出来的时候执行,也就是说在服务器启动的时候执行
* 1.2 contextDestroyed()这个方法会在ServletContext对象被销毁的时候执行,也就是说在服务器关闭的时候执行
*
* 2. 在web.xml中注册(配置)监听器
*/
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("在服务器启动的时候,模拟创建SpringMVC的核心容器...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("在服务器启动的时候,模拟销毁SpringMVC的核心容器...");
}
}
注册监听器
<listener>
<listener-class>com.atguigu.listener.ContextLoaderListenerlistener-class>
listener>