1 Servlet
1.1 UML 图
Servlet 是接口,我们常使用的 HttpServlet 是一个实现类,支持 Http 协议。创建我们自己的 Servlet 类继承 HttpServlet 时,必须重写 doPost 和 doGet 两个方法中的一个。
1.2 Servlet 生命周期
- Web 容器加载我们创建的 Servlet 类 class 到内存中
- 当第一次请求时先调用构造器构造一个 servlet 实例,Web 容器中一个 Servlet 类只有一个实例
- 第一次构造了一个 Servlet 实例后,调用 init 方法进行初始化,只会调用一次
- service 方法处理请求参数和响应,每个请求都会在单个线程中执行
- Web 容器停止或实例被回收,会调用 destroy 进行资源回收操作,只会调用一次
servlet 初始化参数配置中 load-on-startup 可以指定当前 Servlet 类在 Web 容器启动时而不是等到第一个请求到来时就构造 Servlet 实例并执行 init 方法。
1.3 搭建 Servlet 程序
1.3.1 目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| \apache-tomcat-9.0.54\webapps\ D:. ├─docs ├─examples ├─host-manager ├─ROOT │ └─servlet │ ├─src │ └─top │ └─reajason │ HelloServlet.java └─WEB-INF │ web.xml │ ├─classes │ └─top │ └─reajason │ HelloServlet.class └─lib
|
- 在 tomacat 主目录中 webapps 下新建一个 servlet 目录,作为 web 项目
- 在 src 下编写 Java 代码
- WEB-INF 中 web.xml 配置当前 web 项目
- WEB-INF 中 classes 用来接收 Java 代码生成的 class 文件
- WEB-INF 中 lib 用来放第三方库
1.3.2 代码编写
.\src\top\reajason\HelloServlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package top.reajason;
import java.io.*; import javax.servlet.*; import javax.servlet.http.*;
public class HelloServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<h1>Hello Servlet</h1>");
}
@Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { doGet(request, response); } }
|
.\WEB-INF\web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?xml version="1.0" encoding="UTF-8"?> <!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> <servlet> <servlet-name>hello</servlet-name> <servlet-class>top.reajason.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
|
1.3.3 编译运行
在 servlet 目录下打开终端运行如下命令:apache-tomcat-9.0.54\webapps\servlet
- -d:指定编译后的 class 文件到 .\WEB-INF\classes\ 下
- -classpath:指定类路径
1
| javac -encoding utf-8 -d .\WEB-INF\classes\ -classpath "..\..\lib\servlet-api.jar;.;.\WEB-INF\classes\" .\src\top\reajason\*.java
|
进入 tomcat 主目录的 bin 下面运行 startup.bat,开启 tomcat 服务
浏览器输入:http://localhost:8080/servlet/hello
2 请求与响应
Servlet 绝大多数都在 service 方法中响应处理请求,HttpServlet 重写了 Servlet 接口的 service 方法以请求方式分成了 doGet、doPost、doHead……,当请求方式是哪一种就走哪一个方法。方法的参数类型也变成了 HttpServletRequest 和 HttpServletResponse。
2.1 请求
2.1.1 UML 图
HttpServletRequest 实现了 ServletRequest 接口,并定义了更多关于 Http 的方法。
2.1.2 常用方法
获取请求参数:
1 2 3 4 5 6 7
| String getMethod();
String getParameter(String name); String[] getParameterValues(String name);
Enumeration<String> getParameterNames();
|
获取请求头:
1 2 3 4 5 6 7 8
| String getHeader(String name);
Enumeration<String> getHeaders(String name);
int getIntHeader(String name);
Enumeration<String> getHeaderNames();
|
会话相关:
1 2 3 4 5
| Cookie[] getCookies();
HttpSession getSession(); HttpSession getSession(boolean b);
|
获取请求输入流:
1 2 3 4 5
| ServletInputStream getInputStream();
BufferedReader getReader();
|
获取地址相关:
1 2 3 4 5 6 7 8
| String getRemoteAddr(); String getRemoteHost(); int getRemotePort();
String getServerName(); int getServerPort();
|
请求转发:
1 2 3 4
| RequestDispatcher getRequestDispatcher(String name);
|
属性相关:
1 2 3 4 5 6 7
| setAttribute(String name, Object obj);
Object getAttribute(String name); Enumeration<String> getAttributeNames();
void removeAttribute(String name);
|
其他:
1 2 3 4 5 6 7
| Part getPart(String name);
Collection<Part> getParts();
ServletContext getServletContext();
|
2.2 响应
2.2.1 UML 图
HttpServletResponse 实现了 ServletResponse 接口,并定义了更多关于 Http 的方法。
2.2.2 常用方法
设置响应头:
1 2 3 4 5 6 7
| void setHeader(String head, String value); void setIntHeader(String head, int value);
void setStatus(int);
void setContentType();
|
获取输出流:
1 2 3 4
| PrintWriter getWriter();
ServletOutputStream getOutputStream();
|
重定向:
1
| void sendRedirect(String url);
|
3 ServletConfig
每一个 Servlet 类都有一个 ServletConfig,不能共享。Web 容器启动后 Servlet 类初始化后 ServletConfig 中的值就不变了。
使用方法:
在 web.xml 中配置 ServletConfig 初始化参数,在 servlet 标签中使用 init-param 标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?xml version="1.0" encoding="UTF-8"?> <!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> <servlet> <servlet-name>rq</servlet-name> <servlet-class>top.reajason.HelloServlet</servlet-class> <init-param> <param-name>username</param-name> <param-value>ReaJason</param-value> </init-param> <init-param> <param-name>gender</param-name> <param-value>male</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
|
在 Servlet 类中获取:
1 2
| getServletConfig().getInitParameter("username"); getServletConfig().getInitParameter("gender");
|
4 ServletContext
每一个 Web 应用有一个 ServletContext(每个 JVM 有一个 ServletContext,分布式应用时会有不同的),该应用类所有 Servlet 类都可以访问
初始化参数使用方法:
在 web.xml 中配置 ServletContext 初始化参数,使用 context-param 标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?xml version="1.0" encoding="UTF-8"?> <!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>
<context-param> <param-name>username</param-name> <param-value>ReaJason</param-value> </context-param>
<servlet> <servlet-name>rq</servlet-name> <servlet-class>top.reajason.HelloServlet</servlet-class> <init-param> <param-name>username</param-name> <param-value>ReaJason</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>rq</servlet-name> <url-pattern>/rq</url-pattern> </servlet-mapping> </web-app>
|
在 Servlet 类中调用获取参数
1
| getServletContext().getInitParameter("username");
|
ServletContext 也有属性相关的方法,设置之后可以 Web 应用全局访问,用于数据共享,可配合 ServletContextListener 监听器在创建时初始化属性。
5 HttpSession
HttpSession 对象在与一个特定客户的整个会话期间都存在,对于会话期间客户做的所有请求,从中得到的所有信息都可以用 HttpSession 对象保存。
会话死亡的三种情况:
- 超时
- 调用 invalidate() 方法
- 应用结束
在分布式应用中 ServletContext、ServletConfig 每个 VM 都会有一个备份,但是 HttpSession 只会有一个,相同的 HttpSession 不会出现在两个 VM 中。Web 容器会操作 HttpSession 对象在多个 VM 中进行移动完成请求,这个过程中 HttpSessionAttributeListener 监听器发挥作用。
5.1 获取 HttpSession 对象
1 2 3 4 5 6
| request.getSession();
request.getSession(false);
request.getSession(true);
|
5.2 HttpSession 常用方法
1 2 3 4 5 6 7 8 9 10 11 12
| boolean isNew();
long getCreationTime();
long getLastAccessedTime();
void setMaxInactiveInterval(int seconds);
int getMaxInactiveInterval();
coid invalidate();
|
属性相关:
1 2 3 4 5 6 7
| void setAttribute(String name, Object obj);
Object getAttribute(String name); Enumeration<String> getAttributeNames();
void removeAttribute(String name);
|
当客户端不支持 Cookie 时使用 URL 重写的方法
1 2 3
| response.encodeURL("/login"); response.sendRedirect(response.encodeRedirectURL("/login"));
|
5.3 Cookie
向响应中添加 Cookie 的步骤
1 2 3 4 5 6
| Cookie cookie = new Cookie("username", "ReaJason");
cookie.setMaxAge(30*60);
response.addCookie(cookie);
|
从请求中获取特定 cookie 的步骤
1 2 3 4 5 6 7 8 9 10
| Cookie[] cookies = request.getCookies();
for(int i = 0; i < cookies.length; i++){ Cookie cookie = cookie[i]; if("username".equals(cookie.getName())){ out.println(cookie.getValue()); break; } }
|
6 监听器
6.1 ServletContextListener
ServletContext 监听器,监听 ServletContext 初始化时和销毁时执行对应方法
1 2 3 4
| public interface ServletContextListener extends EventListener { default public void contextInitialized(ServletContextEvent sce) {} default public void contextDestroyed(ServletContextEvent sce) {} }
|
使用方法:
编写自定义类实现 ServletContextListener,并实现两个方法
在 web.xml 中注册监听器
1 2 3 4 5 6 7
| <web-app>
<listener> <listener-class>top.reajason.listener.MyServletContextListener</listener-class> </listener>
</web-app>
|
6.2 ServletContextAttributeListener
ServletContext 属性监听器,监听 ServletContext 添加、删除、修改属性时调用对应方法
1 2 3 4 5
| public interface ServletContextAttributeListener extends EventListener { default public void attributeAdded(ServletContextAttributeEvent event) {} default public void attributeRemoved(ServletContextAttributeEvent event) {} default public void attributeReplaced(ServletContextAttributeEvent event) {} }
|
使用方法同 ServletContextListener
6.3 ServletRequestListener
ServletRequest 监听器,监听请求初始化和销毁的时候调用对应方法,比如记录请求日志
1 2 3 4
| public interface ServletRequestListener extends EventListener { default public void requestDestroyed(ServletRequestEvent sre) {} default public void requestInitialized(ServletRequestEvent sre) {} }
|
使用方法同 ServletContextListener
6.4 ServletRequestAttributeListener
ServletRequest 属性监听器,监听 ServletRequest 添加、删除、修改属性时调用对应方法
1 2 3 4 5
| public interface ServletRequestAttributeListener extends EventListener { default public void attributeAdded(ServletRequestAttributeEvent srae) {} default public void attributeRemoved(ServletRequestAttributeEvent srae) {} default public void attributeReplaced(ServletRequestAttributeEvent srae) {} }
|
使用方法同 ServletContextListener
6.5 HttpSessionListener
HttpSession 监听器,监听 HttpSession 创建和销毁时调用对应方法
1 2 3 4
| public interface HttpSessionListener extends EventListener { default public void sessionCreated(HttpSessionEvent se) {} default public void sessionDestroyed(HttpSessionEvent se) {} }
|
使用方法同 ServletContextListener
6.6 HttpSessionBindingListener
HttpSession 绑定监听器,监听一个自定类与 HttpSession 绑定状态调用对应方法
1 2 3 4
| public interface HttpSessionBindingListener extends EventListener { default public void valueBound(HttpSessionBindingEvent event) {} default public void valueUnbound(HttpSessionBindingEvent event) {} }
|
使用方法,在属性类中实现 HttpSessionBindingListener 接口中对应方法,无需在 web.xml 中注册。
1 2 3 4 5 6 7 8 9 10 11
| import javax.servlet.http.*;
public class Dog implements HttpSessionBindingListener{ ...; public void valueBound(HttpSessionBindingEvent event){ } public void valueUnbound(HttpSessionBindingEvent event){ } }
|
6.7 HttpSessionAttributeListener
HttpSession 属性监听器,监听 HttpSession 添加、删除、修改属性时调用对应方法
1 2 3 4 5 6
| public interface HttpSessionAttributeListener extends EventListener { default public void attributeAdded(HttpSessionBindingEvent event) {} default public void attributeRemoved(HttpSessionBindingEvent event) {} default public void attributeReplaced(HttpSessionBindingEvent event) {}
}
|
使用方法同 ServletContextListener
6.8 HttpSessionActivationListener
HttpSession 迁移监听器,监听绑定在 HttpSession 的自定义类从一个 JVM 迁移时调用对应方法。
HttpSession 迁移时只会迁移 Serialization 属性,因此非 Serializable 属性可以通过该监听器解决。
1 2 3 4
| public interface HttpSessionActivationListener extends EventListener { default public void sessionWillPassivate(HttpSessionEvent se) {} default public void sessionDidActivate(HttpSessionEvent se) {} }
|
使用方法:
- 在自定义类中实现当前监听器接口
- 在 web.xml 注册当前监听器
7 过滤器
FileChain 过滤器链,使得在拦截一个请求时可以经过多个过滤器进行操作。
可以使用包装类进行请求和响应的定制操作:
- ServletRequestWrapper
- HttpServletRequestWrapper
- ServletRequestWrapper
- HtpServletResponseWrapper
定义过滤器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package top.reajason;
import java.io.*; import javax.servlet.*; import javax.servlet.http.*;
public class MyFilter implements Filter{
private FilterConfig fc;
public void init(FilterConfig config) throws ServletException{ this.fc = config; }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException{ HttpServletRequest httpReq = (HttpServletRequest)req; fc.getServletContext().log("进入过滤器了"); chain.doFilter(req, resp); }
public void destroy(){} }
|
web.xml 配置过滤器解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <filter> <filter-name>LRequest</filter-name> <filter-class>top.reajason.MyFilter</filter-class> <init-param> <param-name>LogFileName</param-name> <param-value>UserLog.txt</param-value> </init-param> </filter>
<filter-mapping> <filter-name>LRequest</filter-name> <servlet-name>hello</servlet-name> </filter-mapping>
|