1. Servlet简介
Servlet 是运行在服务端tomcat的,从代码层面上来讲Servlet就是一个接口
用来接收、处理客户端请求、响应给浏览器的动态资源。在整个Web应用中,Servlet主要负责接收处理请求、协同调度功能以及响应数据。可以把Servlet称为Web应用中的控制器 Servlet是运行在服务端的,Servlet必须在WEB项目中开发且在Tomcat服务容器中运行
2. Servlet开发流程
2.1. 开发一个UserServlet
public class UserServlet extends HttpServlet { @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { String username = req. getParameter ( "username" ) ; if ( "wyb" . equals ( username) ) { resp. getWriter ( ) . write ( "NO" ) ; } else { resp. getWriter ( ) . write ( "YES" ) ; } }
}
自定义一个类,继承HttpServlet类 重写service方法,用于处理用户请求的服务方法 HttpServletRequest:请求对象,由请求报文经过tomcat转换而来的,通过该对象可以获取请求中的信息 HttpServletResponse:响应对象,会被tomcat转换为响应的报文,通过该对象可以设置响应中的信息 Servlet对象的生命周期(创建,初始化,处理服务,销毁)由tomcat管理的,无需new HttpServletRequest和HttpServletResponse 两个对象也是由tomcat负责转换,在调用service方法时传入的
2.2. 在web.xml为UseServlet配置请求的映射路径
<?xml version="1.0" encoding="UTF-8"?>
< web-app xmlns = " https://jakarta.ee/xml/ns/jakartaee" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation= " https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version = " 5.0" > < servlet> < servlet-name> userServlet</ servlet-name> < servlet-class> com.wyb.servlet.UserServlet</ servlet-class> </ servlet> < servlet-mapping> < servlet-name> userServlet</ servlet-name> < url-pattern> /userServlet</ url-pattern> </ servlet-mapping>
</ web-app>
Servlet并不是文件系统中实际存在的文件或者目录,所以为了能够请求到该资源,需要为其配置映射路径,配置在web.xml中 servlet-name:servlet的别名,定义要见名知意就好 url-pattern:定义Servlet的请求映射路径,一个servlet可以对应多个不同的url-pattern,多个servlet不能使用相同的url-pattern url-pattern中可以使用一些通配写法 /:通配所有资源,不包括jsp文件 /*:通配所有资源,包括jsp文件 /a/* :匹配所有以a前缀的映射路径 *.action:匹配所有以action为后缀的映射路径
2.3. 开发一个form表单,向servlet发送一个get请求并携带username参数
<! DOCTYPE html >
< html lang = " en" >
< head> < meta charset = " UTF-8" > < title> Title</ title>
</ head>
< body> < form action = " userServlet" > 请输入用户名:< input type = " text" name = " username" /> < br> < input type = " submit" value = " 校验" > </ form>
</ body>
</ html>
2.4. 映射关系图
3. Servlet注解方式配置
3.1. @WebServlet注解源码
package jakarta. servlet. annotation ;
import java. lang. annotation. Documented ;
import java. lang. annotation. ElementType ;
import java. lang. annotation. Retention ;
import java. lang. annotation. RetentionPolicy ;
import java. lang. annotation. Target ;
@Target ( { ElementType . TYPE } )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
public @interface WebServlet { String name ( ) default "" ; String [ ] value ( ) default { } ; String [ ] urlPatterns ( ) default { } ; int loadOnStartup ( ) default - 1 ; WebInitParam [ ] initParams ( ) default { } ; String description ( ) default "" ; . . . . . .
}
3.2. @WebServlet注解使用
使用@WebServlet注解替换Servlet配置
@WebServlet ( name = "userServlet" , urlPatterns = { "/userServlet1" , "/userServlet2" , "/userServlet" } , initParams = { @WebInitParam ( name = "encoding" , value = "UTF-8" ) } , loadOnStartup = 6
)
public class UserServlet extends HttpServlet { @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { String encoding = getServletConfig ( ) . getInitParameter ( "encoding" ) ; System . out. println ( encoding) ; String username = req. getParameter ( "username" ) ; if ( "wyb" . equals ( username) ) { resp. getWriter ( ) . write ( "NO" ) ; } else { resp. getWriter ( ) . write ( "YES" ) ; } }
}
4. Servlet生命周期
生命周期 对应方法 执行时机 执行次数 构造对象 构造器 第一次请求或者容器启动 1 初始化 init() 构造完毕后 1 处理服务 service(HttpServletRequest req,HttpServletResponse resp) 每次请求 多次 销毁 destory() 容器关闭 1
4.1. 声明周期测试
servlet
package com. wyb. servlet ;
import jakarta. servlet. ServletException ;
import jakarta. servlet. http. HttpServlet ;
import jakarta. servlet. http. HttpServletRequest ;
import jakarta. servlet. http. HttpServletResponse ;
import java. io. IOException ; public class ServletLifeCycle extends HttpServlet { public ServletLifeCycle ( ) { System . out. println ( "构造器" ) ; } @Override public void init ( ) throws ServletException { System . out. println ( "初始化方法" ) ; } @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { System . out. println ( "service方法" ) ; } @Override public void destroy ( ) { System . out. println ( "销毁方法" ) ; }
}
配置Servlet
< servlet> < servlet-name> servletLifeCycle</ servlet-name> < servlet-class> com.wyb.servlet.ServletLifeCycle</ servlet-class> < load-on-startup> 1</ load-on-startup> </ servlet> < servlet-mapping> < servlet-name> servletLifeCycle</ servlet-name> < url-pattern> /servletLiftCycle</ url-pattern> </ servlet-mapping>
4.2. 生命周期总结
Servlet对象在容器中是单例的 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程 多个线程可能会使用相同的Servlet对象,所以在Servlet中,不要轻易定义一些容易经常发生修改的成员变量 load-on-startup中定义的正整数表示实例化顺序,如果数字重复,容器会自行解决实例化顺序问题,但是应该避免重复 Tomcat容器中已经定义了一些随系统启动实例化的servlet,我们自定义的servlet的load-on-startup尽量不要占用数字1-5
5. Servlet继承结构
5.1. Servlet接口
Servlet 规范接口,所有Servlet必须实现
public void init(ServletConfig config) throws ServletException;
初始化方法,容器在构造servlet对象后自动调用的方法,容器负责实例化一个ServletConfig对象,并在调用该方法时传入 ServletConfig对象可以为Servlet 提供初始化参数 public ServletConfig getServletConfig();
获取ServletConfig对象,后续可以通过该对象获取Servlet初始化参数 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
处理请求并做出响应的服务方法,每次请求产生由容器调用 容器创建一个ServletRequest对象和ServletResponse对象,容器在调用service方法时,传入这两个对象 public String getServletInfo();
public void destroy();
5.2. GenericServlet 抽象类
GenericServlet 抽象类是对Servlet接口一些固定功能的粗糙实现,以及对service方法的再次抽象声明并定义了一些其他相关功能方法
private transient ServletConfig config;
public GenericServlet() { }
public void destroy() { }
public String getInitParameter(String name)
public Enumeration<String> getInitParameterNames()
public ServletConfig getServletConfig()
获取初始Servlet初始配置对象ServletConfig的方法 public ServletContext getServletContext()
public String getServletInfo()
public void init(ServletConfig config) throws ServletException()
public void init() throws ServletException
重载init方法,为了让我们自己定义初始化功能的方法 public void log(String msg)
public void log(String message, Throwable t)
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
public String getServletName()
5.3. HttpServlet 抽象类
abstract class HttpServlet extends GenericServlet
,HttpServlet抽象类,除了基本的实现以外,增加了更多的基础功能
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
public HttpServlet() {}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
对服务方法的实现 在该方法中,将请求和响应对象转换成对应HTTP协议的HttpServletRequest HttpServletResponse对象 调用重载的service方法 public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
重载的service方法,被重写的service方法所调用 在该方法中,通过请求方式判断调用具体的do***方法完成请求的处理 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
对应不同请求方式的处理方法 除了doOptions和doTrace方法,其他的do*** 方法都在故意响应错误信息
5.4. 自定义Servlet
继承关系图解 自定义Servlet中,必须要对处理请求的方法进行重写
要么重写service方法 要么重写doGet/doPost方法
6. ServletConfig和ServletContext
6.1. ServletConfig
为Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象 容器会为每个Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性 ServletConfig是一个接口
package jakarta. servlet ;
import java. util. Enumeration ;
public interface ServletConfig { String getServletName ( ) ; ServletContext getServletContext ( ) ; String getInitParameter ( String var1) ; Enumeration < String > getInitParameterNames ( ) ;
}
方法名 作用 getServletName() 获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称 getServletContext() 获取ServletContext对象 getInitParameter() 获取配置Servlet时设置的『初始化参数』,根据名字获取值 getInitParameterNames() 获取所有初始化参数名组成的Enumeration对象
public class ServletA extends HttpServlet { @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { ServletConfig servletConfig = this . getServletConfig ( ) ; String value = servletConfig. getInitParameter ( "param1" ) ; System . out. println ( "param1:" + value) ; Enumeration < String > parameterNames = servletConfig. getInitParameterNames ( ) ; while ( parameterNames. hasMoreElements ( ) ) { String paramaterName = parameterNames. nextElement ( ) ; System . out. println ( paramaterName + ":" + servletConfig. getInitParameter ( paramaterName) ) ; } }
}
< servlet> < servlet-name> ServletA</ servlet-name> < servlet-class> com.wyb.servlet.ServletA</ servlet-class> < init-param> < param-name> param1</ param-name> < param-value> value1</ param-value> </ init-param> < init-param> < param-name> param2</ param-name> < param-value> value2</ param-value> </ init-param> </ servlet> < servlet-mapping> < servlet-name> ServletA</ servlet-name> < url-pattern> /servletA</ url-pattern> </ servlet-mapping>
6.2. ServletContext
ServletContext对象又称为上下文对象,应用域对象 容器会为每个app创建一个独立唯一的ServletContext对象 ServletContext对象为所有的Servlet共享 ServletContext为所有的Servlet提供初始配置参数
<?xml version="1.0" encoding="UTF-8"?>
< web-app xmlns = " https://jakarta.ee/xml/ns/jakartaee" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation= " https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version = " 5.0" > < context-param> < param-name> paramA</ param-name> < param-value> valueA</ param-value> </ context-param> < context-param> < param-name> paramB</ param-name> < param-value> valueB</ param-value> </ context-param>
</ web-app>
在Servlet中获取ServletContext并获取参数
package com. wyb. servlet ;
import jakarta. servlet. ServletConfig ;
import jakarta. servlet. ServletContext ;
import jakarta. servlet. ServletException ;
import jakarta. servlet. http. HttpServlet ;
import jakarta. servlet. http. HttpServletRequest ;
import jakarta. servlet. http. HttpServletResponse ;
import java. io. IOException ;
import java. util. Enumeration ; public class ServletA extends HttpServlet { @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { ServletContext servletContext = this . getServletContext ( ) ; String valueA = servletContext. getInitParameter ( "paramA" ) ; System . out. println ( "paramA:" + valueA) ; Enumeration < String > initParameterNames = servletContext. getInitParameterNames ( ) ; while ( initParameterNames. hasMoreElements ( ) ) { String paramaterName = initParameterNames. nextElement ( ) ; System . out. println ( paramaterName + ":" + servletContext. getInitParameter ( paramaterName) ) ; } }
}
其他API 获取资源真实路径:无论项目的部署路径发生什么变化,都会动态获取项目运行时的实际路径 : String realPath = servletContext.getRealPath("资源在web目录中的路径");
获取项目的上下文路径:在部署进入tomcat时所使用的路径,可以解决一些后端页面渲染技术或者请求转发和响应重定向中的路径问题 :String contextPath = servletContext.getContextPath();
域对象API 域对象: 用于存储和传递数据的对象,传递数据不同的范围,称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同 ServletContext:应用,ServletContext域:应用域,是webapp中最大的域,可以在本应用内实现数据的共享和传递 webapp中的三大域对象:应用域,会话域,请求域 |
API 功能解释 void setAttribute(String key,Object value); 向域中存储/修改数据 Object getAttribute(String key); 获得域中的数据 void removeAttribute(String key); 移除域中的数据
7. HttpServletRequest
HttpServletRequest是一个接口,父接口是ServletRequest HttpServletRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service方法时传入 HttpServletRequest代表客户端发来的请求,所有请求中的信息都可以通过该对象获得
7.1. 常见API
获取请求行信息相关(方式,请求的url,协议及版本)
API 功能解释 StringBuffer getRequestURL(); 获取客户端请求的url String getRequestURI(); 获取客户端请求项目中的具体资源 int getServerPort(); 获取客户端发送请求时的端口 int getLocalPort(); 获取本应用在所在容器的端口 int getRemotePort(); 获取客户端程序的端口 String getScheme(); 获取请求协议 String getProtocol(); 获取请求协议及版本号 String getMethod(); 获取请求方式
API 功能解释 String getHeader(String headerName); 根据头名称获取请求头 Enumeration getHeaderNames(); 获取所有的请求头名字 String getContentType(); 获取content-type请求头
API 功能解释 String getParameter(String parameterName); 根据请求参数名获取请求单个参数值 String[] getParameterValues(String parameterName); 根据请求参数名获取请求多个参数值数组 Enumeration getParameterNames(); 获取所有请求参数名 Map<String, String[]> getParameterMap(); 获取所有请求参数的键值对集合 BufferedReader getReader() throws IOException; 获取读取请求体的字符输入流 ServletInputStream getInputStream() throws IOException; 获取读取请求体的字节输入流 int getContentLength(); 获得请求体长度的字节数
API 功能解释 String getServletPath(); 获取请求的Servlet的映射路径 ServletContext getServletContext(); 获取ServletContext对象 Cookie[] getCookies(); 获取请求中的所有cookie HttpSession getSession(); 获取Session对象 void setCharacterEncoding(String encoding) ; 设置请求体字符集
8. HttpServletResponse
HttpServletResponse是一个接口,父接口是ServletResponse HttpServletResponse是Tomcat预先创建的,在Tomcat调用service方法时传入 HttpServletResponse代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,可以设置响应信息
8.1. 常见API
API 功能解释 void setStatus(int code); 设置响应状态码
API 功能解释 void setHeader(String headerName, String headerValue); 设置/修改响应头键值对 void setContentType(String contentType); 设置content-type响应头及响应字符集(设置MIME类型)
API 功能解释 PrintWriter getWriter() throws IOException; 获得向响应体放入信息的字符输出流 ServletOutputStream getOutputStream() throws IOException; 获得向响应体放入信息的字节输出流 void setContentLength(int length); 设置响应体的字节长度,其实就是在设置content-length响应头
API 功能解释 void sendError(int code, String message) throws IOException; 向客户端响应错误信息的方法,需要指定响应码和响应信息 void addCookie(Cookie cookie); 向响应体中增加cookie void setCharacterEncoding(String encoding); 设置响应体字符集
9. 请求转发和响应重定向
请求转发和响应重定向是web应用中,间接访问项目资源的两种手段,也是Servlet控制页面跳转的两种手段 请求转发通过HttpServletRequest实现,响应重定向通过HttpServletResponse实现
9.1. 请求转发
9.2. 请求转发特点(面试题)
通过HttpServletRequest对象获取请求转发器实现 请求转发是服务器内部的行为,对客户端是屏蔽的 客户端只发送了一次请求,客户端地址栏不变 服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源 因为全程只有一个HttpServletRequset对象,所以请求参数和请求域中的数据可以传递 请求转发可以转发给其他Servlet动态资源,也可以转发给一些静态资源以实现页面跳转 请求转发可以转发给WEB-INF下受保护的资源 请求转发不能转发到本项目以外的外部资源
@WebServlet ( "/servletA" )
public class ServletA extends HttpServlet { @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { RequestDispatcher requestDispatcher = req. getRequestDispatcher ( "servletB" ) ; String username = req. getParameter ( "username" ) ; System . out. println ( username) ; req. setAttribute ( "reqKey" , "requestMessage" ) ; requestDispatcher. forward ( req, resp) ; }
}
@WebServlet ( "/servletB" )
public class ServletB extends HttpServlet { @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { String username = req. getParameter ( "username" ) ; System . out. println ( username) ; String reqMessage = ( String ) req. getAttribute ( "reqKey" ) ; System . out. println ( reqMessage) ; resp. getWriter ( ) . write ( "servletB response" ) ; }
}
9.3. 响应重定向
9.4. 响应重定向特点(面试题)
响应重定向通过HttpServletResponse对象的sendRedirect方法实现 响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,是在服务端提示下的客户端的行为 客户端至少发送了两次请求,客户端地址栏是要变化的 服务端产生了多对请求和响应对象,且请求和响应对象不会传递给下一个资源 因为全程产生了多个HttpServletRequset对象,所以请求参数和请求域中的数据不可以传递 重定向可以是其他Servlet动态资源,也可以是一些静态资源以实现页面跳转 重定向不可以到给WEB-INF下受保护的资源 重定向可以到本项目以外的外部资源
@WebServlet ( "/servletA" )
public class ServletA extends HttpServlet { @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { String username = req. getParameter ( "username" ) ; System . out. println ( username) ; req. setAttribute ( "reqKey" , "requestMessage" ) ; resp. sendRedirect ( "servletB" ) ; }
}
@WebServlet ( "/servletB" )
public class ServletB extends HttpServlet { @Override protected void service ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException { String username = req. getParameter ( "username" ) ; System . out. println ( username) ; String reqMessage = ( String ) req. getAttribute ( "reqKey" ) ; System . out. println ( reqMessage) ; resp. getWriter ( ) . write ( "servletB response" ) ; }
}