Servlet2.5
什么是Servlet
Servlet
是JavaEE
规范之一.Servlet
是JavaWeb
三大组件之一.(Servlet, Filter,Listener
)Servlet
是运行在服务器上的一个Java
程序, 处理Web
客户端发送的请求以及返回响应信息给Web
客户端;javax.servlet.Servlet
接口定义了Servlet
的规范, 包括构造器,初始化方法, 处理请求与返回响应方法以及销毁方法等规范;
实现Servlet
- 实现
javax.servlet.Servlet
接口, 重写抽象方法;
package com.bryan.study;
import javax.servlet.*;
import java.io.IOException;
public class MyServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 这个方法就是Servlet处理请求与返回响应的方法
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
WEB-INF
文件夹下web.xml
配置文件编写
<servlet>
<!--定义Servlet的名称-->
<servlet-name>MyServlet</servlet-name>
<!--指定当前Servlet指向的类名-->
<servlet-class>com.bryan.study.MyServlet</servlet-class>
<!--指定当前Servlet加载时机, 默认为-1 表示访问时才实例化初始化, 大于0时, 则项目启动时就初始化, 按照此数值的大小顺序加载web.xml中所有此数值大于0的servlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!--匹配Servlet的名称-->
<servlet-name>MyServlet</servlet-name>
<!--Web客户端访问Url的资源路径-->
<url-pattern>/myServlet</url-pattern>
</servlet-mapping>
浏览器通过URL访问Servlet程序机制
访问路径 | http://localhost:8080/javaweb/myServlet | 解释 |
---|---|---|
超文本传输协议 | HTTP: | 客户端和服务端进行数据传输的一种规则 |
IP地址 | localhost | 定位运行服务器的电脑地址 |
端口号 | :8080 | 定位电脑中服务器运行的程序 这里就是Tomcat 程序 |
项目路径 | /javaweb | 定位运行在Tomcat 中的项目 |
资源路径 | /myServlet | 解析web.xml 文件, 根据url-pattern 匹配 找到servlet-name , 根据servlet-name 找到servlet-class , 然后实例化Servlet , 初始化Servlet , 最后执行Servlet.service(resquest, response) ;方法 |
Servlet生命周期
- 实例化: 调用
Servlet
的构造器 - 初始化: 调用
Servlet.init(ServletConfig servletConfig)
方法 - 处理请求与响应: 调用
Servlet.service(ServletRequest servletRequest, ServletResponse servletResponse)
方法 - 销毁: 调用
Servlet.destroy()
方法;
其中 实例化和初始化默认只会在客户端第一次访问时被调用; 处理请求与响应在客户端每次的访问中都会被调用; 销毁方法在Tomcat
容器停止时被调用;
Servlet类的继承体系![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/308468c008661f0f644ef7cbe76003ea.png)
请求分发
根据javax.servlet.http.HttpServlet
源码可以看出 在重写父类的service()方法中 将请求和响应分别强制转换为HttpServletRequest
和HttpServletResponse
, 然后调用了自身的重载protected void service(HttpServletRequest req, HttpServletResponse resp)
方法, 在此方法中通过HttpServletRequest .getMethod()
方法获取请求的方式, 通过不同的方式调用了不同的处理方法; 例如get
请求调用了doGet()
方法, post
请求调用了doPost()
方法;
public abstract class HttpServlet extends GenericServlet {
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
this.service(request, response);
} else {
throw new ServletException("non-HTTP request or response");
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
}
那么实际开发中可以通过继承javax.servlet.http.HttpServlet
类, 只需要重写对应的请求分发方法, 既可以处理不同的业务
public class MyHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// DO SOMETHING FROM GET METHOD REQUEST
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// DO SOMETHING FROM GET METHOD REQUEST
}
}
ServletConfig
ServletConfig
: 存储Servlet
的配置信息, 在Servlet
初始化时由Tomcat
程序调用Servlet
的init()
方法传入;
Servlet
实例和ServletConfig
实例都是由Tomcat
负责创建初始化,Servlet
默认是在客户端第一次访问时创建, 每一个Servlet
实例都有一个属于自己的ServletConfig
实例;
public interface Servlet {
// 可以看到Servlet源码中 init方法的形参就是ServletConfig
void init(ServletConfig var1) throws ServletException;
// 通过此方法获取当前Servlet的ServletConfig
ServletConfig getServletConfig();
....
}
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private transient ServletConfig config;
// 在GenericServlet 源码中 init方法将Tomcat传入的ServletConfig赋值给自己的属性private transient ServletConfig config
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
// 通过此方法获取当前Servlet的ServletConfig, 直接返回了属性private transient ServletConfig config , 所以调用此方法时必须保证init()方法里将Tomcat传入的ServletConfig赋值给属性private transient ServletConfig config
public ServletConfig getServletConfig() {
return this.config;
}
}
ServletConfig
的作用:
- 可以获取
Servlet
程序的别名servlet-name
的值 - 获取初始化参数
init-param
的值 - 获取
ServletContext
对象
public interface ServletConfig {
// 获取web.xml文件中配置的servlet-name别名的值
String getServletName();
// 获取ServletContext对象实例
ServletContext getServletContext();
// 获取初始化参数 init-param的值
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
}
ServletContext
ServletContext
:Servlet
上下文对象; 一个Web
工程只有一个ServletContext
对象实例. 在Web
工程部署启动时被Tomcat实例化; 在Web
工程停止时被销毁;
作用: 1. 获取web.xml
中配置的上下文参数context-param
; 2. 获取当前工程路径; 3.后驱工程部署后在服务器硬盘上的绝对路径;4. 像Map一样存取数据;
public class MyHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
ServletContext servletContext = servletConfig.getServletContext();
// 获取上下文参数`context-param`
Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
while (initParameterNames.hasMoreElements()){
String paramName = initParameterNames.nextElement();
String paramValue = servletContext.getInitParameter(paramName);
System.out.println(paramName+":"+paramValue);
}
// 获取工程路径
String contextPath = servletContext.getContextPath();
System.out.println(contextPath);
// 获取工程绝对路径
String realPath = servletContext.getRealPath("/");
System.out.println(realPath);
// 设置属性值
System.out.println("key被设置之前"+servletContext.getAttribute("key"));
// 由于servletContext是在Tomcat启动项目时就被实例化, 那么第一次请求在servletContext没有设置key的值时,就会获取到空值, 在第一次请求servletContext设置了key的值后,可以获取到value值, 并且后面每次的请求时servletContext中将一直存在key的值;
// 由于一个web项目只有一个servletContext实例, 那么在其中一个Servlet对servletContext对象设置属性值后,其他Servlet也能获取到被设置的属性值;
servletContext.setAttribute("key","value");
System.out.println("key被设置之后"+servletContext.getAttribute("key"));
super.doGet(req, resp);
}
}
HttpServletResquest
每次请求,Tomcat
服务器就会吧请求过来的Http
协议信息解析封装到HttpServletRequest
对象中. 然后传入Servlet
的service()
方法中使用.
public class MyHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取项目路径
String reqContextPath = req.getContextPath();
System.out.println(reqContextPath);
// 获取参数字符串
String queryString = req.getQueryString();
System.out.println(queryString);
// 获取 /项目路径/资源路径
String requestURI = req.getRequestURI();
System.out.println(requestURI);
// 获取 访问路径 http://ip:port/项目路径/资源路径
StringBuffer requestURL = req.getRequestURL();
System.out.println(requestURL);
String characterEncoding = req.getCharacterEncoding();
System.out.println(characterEncoding);
// get请求 参数中文乱码 可修改tomcat配置/conf/server.xml文件的配置 <Connector connectionTimeout="50000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
String username1 = req.getParameter("username");
System.out.println(username1);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// post请求参数中文乱码,可使用设置编码解决, 但是设置编码一定要在获取参数之前, 否则会设置失效
req.setCharacterEncoding("utf-8");
String username1 = req.getParameter("username");
System.out.println(username1);
String password = req.getParameter("password");
System.out.println(password);
// 设置属性
req.setAttribute("key", "value");
// 获取属性
Object key = req.getAttribute("key");
// 移除属性
req.removeAttribute("key");
}
}
请求转发
请求转发指的是服务器收到请求后,由服务器主动从一个资源跳转到另一个资源的操作;
特点: 1. 浏览器地址栏没有变化;2. 客户端总共只有一次请求; 3. 所有经过转发的Servlet共用一个Request; 4. 可以转发到WEB-INF目录下;5. 不可以转发访问工程以外的资源;
public class MyHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置属性
req.setAttribute("MyHttpServletKey","MyHttpServletValue");
// 获取转发器 / 表示 "http://ip:port/项目路径/" 因此这里填写其他项目路径或访问外部链接,是无法形成转发
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/dispatcherServlet");
req.setAttribute("MyHttpServletKey1","MyHttpServletValue1");
// 服务器主动转发
requestDispatcher.forward(req, resp);
}
}
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
// 获取转发前设置的属性
Object myHttpServletKey = req.getAttribute("MyHttpServletKey");
Object myHttpServletKey1 = req.getAttribute("MyHttpServletKey1");
System.out.println(username);
System.out.println(myHttpServletKey);
System.out.println(myHttpServletKey1);
}
}
HttpServletResponse
HttpServletResponse
由Tomcat
创建并传递给Servlet
使用.如果需要设置返回给客户端信息, 则通过HttpServletResponse
来进行设置;
HttpServletResponse
可以获取两种流, 一种字节流, 一般用来下载文件使用; 一种字符流, 用来回复客户端字符串信息; 两种流在同一个HttpServletResponse
中只能选其一使用; 如果同时获取两种流,则程序将无法通过;
public class MyHttpServlet extends HttpServlet {
// 解决响应中文乱码 方案一
// 设置服务器响应编码
//resp.setCharacterEncoding("UTF-8");
// 告诉浏览器使用什么编码解析响应
//resp.setHeader("Content-Type","text/html;charset=UTF-8");
// 解决响应中文乱码 方案二
// 此方法一定要在获取流对象之前才有效
resp.setContentType("text/html;charset=UTF-8");
// 一般用于下载文件
// ServletOutputStream outputStream = resp.getOutputStream();
// 两种流在同一个response中只能选其一使用
PrintWriter writer = resp.getWriter();
writer.write("您被转发了");
}
重定向
请求重定向: 客户端向服务器发送请求, 服务器响应客户端新地址.客户端继续访问新地址的操作;
特点:1. 浏览器地址栏发生变化;2. 客户端至少有两次请求; 3. 每次被访问的的Servlet不共用一个Request; 4. 不可以重定向到WEB-INF目录下;5. 可以重定向访问工程以外的资源;
public class MyHttpServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("redireceKey","你猜你能接收到吗?");
// 方案一
// 设置状态码302 告诉浏览器需要重定向
resp.setStatus(302);
// 设置重定向新的地址
resp.setHeader("Location","http://localhost:8080/javaweb/redirectServlet");
// 方案二
// resp.sendRedirect("http://localhost:8080/javaweb/redirectServlet")
}
}
// 接受重定向信息
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
Object redireceKey = req.getAttribute("redireceKey");
System.out.println(redireceKey); // 这里无法获取重定向之前被设置的属性值
PrintWriter writer = resp.getWriter();
writer.write("您被重定向了");
}
}
JSP
JSP
: Java Server Pages
,是一种动态网页开发技术; 实际开发中直接使用Servlet
中Response
返回HTML
会造成开发与维护困难; 而JSP
就是为了解决这一问题产生的技术;
JSP
本质上是一个Servlet
, 在客户端第一次访问JSP
页面时, 可以发现Tomcat
程序将JSP
页面全部编写为同名.java
文件,并编译为.class
字节码文件;反编译这些文件可以看到这些.java
文件就是JSP
页面同名的类, 并且继承了org.apache.jasper.runtime.HttpJspBase
; 而这个类刚好就继承了javax.servlet.http.HttpServlet
;
因为JSP
本质也是一个Servlet
, 那么在实际开发中可以使用JAVA Servlet
程序处理实际业务逻辑, 将需要在页面的展示的数据放入Request
中(req.setAttribute(key,value))
, 通过请求转发(req.getRequestDispatcher("/xxx.jsp")
)至JSP
页面, 因为请求转发时所有的Servlet
都共用一个Request
, 那么在JSP
页面中就可以从Request
中直接获取数据(req.getAttribute("key")
)展示即可; 这样就达到了视图层与业务层分离的思想, 代码开发与维护会相对方便; 同时为了防止客户端直接访问JSP
页面, 通常JSP
页面会放置在WEB-INF
包下, 通过JAVAServlet
程序请求转发来访问JSP
页面;
Listener监听器
Listener
是JavaEE
的规范, 监听某种事物的变化, 然后通过回调函数, 对程序进行相应的处理;
ServletContextListener
ServletContext
的监听器, 接口中定义了两个回调函数, 分别在ServletContext
初始化被调用, ServletContext
销毁时被调用;
使用监听器的步骤: 1. 提供监听器接口的实现类; 2. 重写回调函数, 处理相应的业务逻辑;3. web.xml
配置文件中配置监听器;
//1. 提供监听器接口的实现类
public class MyServletContextListener implements ServletContextListener {
// 2. 重写回调函数, 处理相应的业务逻辑
@Override
public void contextDestroyed(ServletContextEvent sce) {
// ServletContextEvent 被监听的事件
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext+"被销毁");
}
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext+"被创建");
}
}
<!-- 3. web.xml配置文件中配置监听器 -->
<listener>
<listener-class>com.bryan.study.listener.MyServletContextListener</listener-class>
</listener>
Cookie
服务器通知客户端保存键值对信息的一种技术; 客户端存放Cookie后,每次请求都会携带Cookie发送给服务端;
// 基础Servlet 使用反射技术 分发不同的action
public class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doAction(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
doAction(req,resp);
}
protected void doAction(HttpServletRequest req, HttpServletResponse resp){
String action = req.getParameter("action");
if (action != null && action != "") {
Class<? extends BaseServlet> clazz = this.getClass();
Method method = null;
try {
method = clazz.getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if (method != null) {
try {
method.invoke(this, req, resp);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
// 客户端发送请求 htpp://localhost:8080/javaweb/cookieServlet?action=cookies
public class CookieServlet extends BaseServlet{
public void cookies(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建cookie
Cookie cookie = new Cookie("cookieName","cookieValue");
cookie.setMaxAge(-1); // 设置cookie的存活时间 负数表示关闭浏览器就消失, 0表示立刻删除, 正数表示存活的秒数 默认是-1
resp.addCookie(cookie);
Cookie cookie1 = new Cookie("cookieName1","cookieValue1");
cookie.setMaxAge(3600); // 设置cookie的存活时间 负数表示关闭浏览器就消失, 0表示立刻删除, 正数表示存活的秒数 默认是-1
resp.addCookie(cookie1);
// 获取cookie
Cookie[] cookies = req.getCookies();
Cookie getCookie = null;
for (Cookie c : cookies) {
if (c.getName().equals("cookieName")) {
getCookie = c;
break;
}
}
if (null != getCookie) {
// 删除
getCookie.setMaxAge(0);
resp.addCookie(getCookie);
}
}
}
设置网页记住登录名
public class LoginServlet extends BaseServlet{
public void login(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
if ("bryan".equals(username) && "123456".equals(password)) {
Cookie cookie = new Cookie("username", username);
cookie.setMaxAge(3600); // 登录成功后将登录名放进cookie并且设置失效时间
resp.addCookie(cookie);
resp.getWriter().write("登录成功");
} else {
resp.getWriter().write("登录失败");
}
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<base href="http://localhost:8080/javaweb/">
</head>
<body>
<form action="loginServlet" method="post">
<input type="hidden" name="action" value="login">
<!-- jsp页面 如果浏览器有cookie 则自动赋值 -->
用户名:<input name="username" value="${cookie.username.value}"/><br />
密 码:<input name="password" /><br>
<button type="submit">提交</button>
</form>
</body>
</html>
SESSION
Session
是用来维护客户端和服务器之间关联的一种技术, 每个客户端在服务器上有一个自己的Session
会话;
Session
是基于Cookie
的, 如果浏览器的Cookie
被禁用了, 那么将导致每次访问都需要创建新的Session
public class SessionServlet extends BaseServlet{
public void session(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 第一次请求时, 服务器会创建新的session, 并将session的ID放入key为JSESSIONID的cookie中
HttpSession session = req.getSession();
// 第一次请求时, 这个获取到的值一定是空的, 如果后面设置了此key的值,那么后续的请求在session没有过期的情况下就能获取到此key的值
Object k1 = session.getAttribute("k");
System.out.println(k1);
session.setAttribute("k","v");
Object k = session.getAttribute("k");
System.out.println(k);
Cookie[] cookies = req.getCookies();
for (Cookie c: cookies) {
if (c.getName().equals("JSESSIONID")) {
String value = c.getValue();
System.out.println(value);
}
}
// 移除
// session.removeAttribute("k");
// 设置有效时间 单位秒 负数表示永不失效 在Tomcat conf包下web.xml中有配置默认时间 1800秒 也就是30分钟
// 如果想要修改某个项目的所有session默认过期时间 则可以在项目的web.xml文件中配置session-timeout
// 如果只需修改某个单独的session过期时间, 则调用session.setMaxInactiveInterval(secondtime)方法 单位为秒
// session.setMaxInactiveInterval(3600);
// 使session立刻失效
// session.invalidate();
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("SessionID="+session.getId() +",是否是新的Session:"+session.isNew()+", Session的有效期:"+session.getMaxInactiveInterval());
}
}
Filter
过滤器: 拦截请求, 过滤响应; 是JavaEE
的一个接口规范;在javaweb
开发中,配置过滤器,可以统一拦截处理请求和响应;
生命周期: 1.初始化: Filter
在项目启动是被Tomcat
实例化, 并调用其自身的init()
方法进行初始化; 2. 每一次请求时, 会调用Filter.doFilter()
方法, 拦截请求; 3. 项目关闭时, Tomcat
调用destory()
方法销毁Filter
;
<!--在web.xml配置文件中配置filter-->
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.bryan.study.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<!--拦截的url匹配条件 其中/表示 http://ip:port/工程路径/; *表示全匹配-->
<!--可以是 / , /* , *.html, /abc.* 等等-->
<url-pattern>/*</url-pattern>
<url-pattern>/</url-pattern>
<url-pattern>*.html</url-pattern>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
/** 实现Filter接口,并重写抽象方法*/
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 获取配置的filter名称
String filterName = filterConfig.getFilterName();
System.out.println(filterName+"被初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession session = request.getSession();
Object username = session.getAttribute("username");
System.out.println("前置");
// 放行
filterChain.doFilter(servletRequest,servletResponse);
response.addCookie(new Cookie("key","value"));
System.out.println("后置");
}
@Override
public void destroy() {
System.out.println("被销毁");
}
}
多个Filter的执行机制
多个Filter的执行顺序根据项目web.xml中配置的Filter顺序从上向下执行;
Servlet3.0
Servlet3.0相较于Servlet2.5增加注解配置, 不在需要web.xml配置文件; 增加Servlet异步处理支持; 对文件上传API的简化支持;
@WebServlet
@WebServlet 修饰Servlet类,用于部署Servlet类的相关配置;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
// 指定Servlet的名称 相当于 web.xml中的 servlet-name
String name() default "";
// 指定Servlet的匹配路径 相当于 web.xml中的 url-pattern
String[] value() default {};
// 指定Servlet的匹配路径 相当于 web.xml中的 url-pattern
// value属性与urlPatterns属性二选一使用 不可同时使用
String[] urlPatterns() default {};
// 指定Servlet的实例化时机 默认-1 指定第一次请求时实例化, 大于0时表示在项目启动时实例化, 且所有配置大于0的Servlet按照此数值大小顺序实例化
int loadOnStartup() default -1;
// 指定Servlet初始化参数 相当于web.xml中<init-param></init-param>
WebInitParam[] initParams() default {};
// 指定Servlet是否支持异步 默认是false 表示同步处理
boolean asyncSupported() default false;
String smallIcon() default "";
String largeIcon() default "";
String description() default "";
String displayName() default "";
}
@WebServlet(
name="myAnnotationServlet",
// 指定servlet url-pattern
value="/myAnnotationServlet",
// 指定初始化参数
initParams={@WebInitParam(name="username",value = "bryan"), @WebInitParam(name="password",value = "123456")},
// 指定servlet的加载时机
loadOnStartup = 1
)
public class MyAnnotationServlet extends BaseServlet {
public void annotationServlet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("使用了注解定义Servlet");
}
}
@WebFilter
@WebFilter修饰Filter类,用于部署Filter类的相关配置;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebFilter {
String description() default "";
String displayName() default "";
// 指定拦截器的初始化参数 相当于web.xml的init-param
WebInitParam[] initParams() default {};
// 指定Filter的名称 相当于 web.xml的 filter-name
String filterName() default "";
String smallIcon() default "";
String largeIcon() default "";
// 指定Filter拦截的具体的Servlet的名称集合
String[] servletNames() default {};
// 指定Filter拦截的路径 相当于 web.xml中的url-pattern
String[] value() default {};
// 指定Filter拦截的路径 相当于 web.xml中的url-pattern
String[] urlPatterns() default {};
// 指定Filter拦截请求的类型, 默认是DispatcherType.REQUEST
DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
// 指定拦截器是否支持异步
boolean asyncSupported() default false;
}
@WebFilter(value = "/*",
dispatcherTypes = DispatcherType.FORWARD, // 表示只拦截被Servlet转发过来的请求
filterName = "myAnnotationFilter")
public class MyAnnotationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
@Override
public void destroy() {
}
}
@WebListener
@WebListener修饰Listener类,用于部署Listener类的相关配置;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebListener {
String value() default "";
}
@WebListener
public class MyAnnotationContextListener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext+"被销毁");
}
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
String username = servletContext.getInitParameter("username");
System.out.println(servletContext+"被创建");
}
}
注解配置与文件配置同时存在情况分析
- 对于
Servlet
配置, 如果一个Servlet
类同时被@WebServlet
修饰, 又在web.xml
中配置了<servlet></servelt>
;@WebServlet
的value
与web.xml
中的url-pattern
不同时, 那么相当于这个Servlet
有多个url-pattern
访问路径;@WebServlet
的value
与web.xml
中的url-pattern
相同时, 项目将无法启动, 不支持两者相同; - 对于
Filter
配置, 如果一个Filter
类同时被@WebFilter
修饰, 又在web.xml
中配置了<filter></filter>
;无论url-pattern
是否相同,Tomcat
会实例化两个Filter
, 也就是认为@WebFilter
与web.xml
中是不同的Filter
; - 若
Listener
才用了两种方式同时注册, 则仅仅相当于一个Listener
;
Servlet异步处理
- 声明
Servlet
,增加asyncSupported
属性,开启异步支持; - 通过
request
获取异步上下文AsyncContext
; - 调用
AsyncContext.start()
方法 并传入实现Runnable
接口的实例 启动子线程; - 调用
AsyncContext.complete()
方法 通知子线程完成;
// 1. 配置asyncSupported = true 表示当前Servlet支持异步
@WebServlet(value = "/asyncServlet", asyncSupported = true)
public class AsyncServlet extends BaseServlet {
public void async(HttpServletRequest req, HttpServletResponse resp) throws Exception {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write(Thread.currentThread().getName()+"主线程Servlet开始<br>");
writer.write(resp.toString());
// 2. 通过HttpServletRequest 获取AsyncContext
AsyncContext ac = req.startAsync();
// 设置响应超时时间 如果业务处理逻辑时间超过此设定的时间,还没有调用ac.complete(),将有可能发生异常
ac.setTimeout(100000);
// 设置监听器
ac.addListener(new MyAsyncListener());
// 3. 调用 AsyncContext.start()方法 并传入实现Runnable接口的实例 启动子线程
ac.start(new AsyncSupportedService(ac));
writer.write(Thread.currentThread().getName()+"主线程Servlet结束<br>");
}
}
public class AsyncSupportedService implements Runnable {
private AsyncContext ac;
public AsyncSupportedService(AsyncContext ac) {
this.ac = ac;
}
@Override
public void run() {
try {
ServletResponse response = ac.getResponse();
PrintWriter writer = response.getWriter();
writer.write(Thread.currentThread().getName()+"子线程开始<br>");
writer.write(response.toString());
int sum = 0;
for (int i = 0; i <= 10 ; i++) {
sum += i;
System.out.println(i);
Thread.sleep(1000);
}
writer.write("SUM="+sum+"<br>");
writer.write(Thread.currentThread().getName()+"子线程结束<br>");
// 4. 调用AsyncContext.complete()方法 通知子线程完成
ac.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
动态注册三大组件
使用ServletContextListener
监听器, 在项目启动时通过ServletContextEvent
获取ServletContext
, 调用ServletContext
的相关API动态注册三大组件;
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
// 获取动态注册器 动态注册Servlet
ServletRegistration.Dynamic dynamicServlet = servletContext.addServlet("dynamicServlet", DynamicServlet.class);
dynamicServlet.addMapping("/dynamicServlet"); // 添加url-pattern
dynamicServlet.setLoadOnStartup(2); // 添加Servlet实例化机制
dynamicServlet.setInitParameter("username","bryan"); // 设置初始化参数
dynamicServlet.setAsyncSupported(false); // 设置是否支持异步
// 动态注册过滤器
FilterRegistration.Dynamic dynamicFilter = servletContext.addFilter("dynamicFilter", DynamicFilter.class);
// dynamicFilter.addMappingForServletNames();
// 设置url-pattern
dynamicFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),false,"/*");
dynamicFilter.setInitParameter("password","123456");
// 动态注册监听器
servletContext.addListener(DynamicListener.class);
}
}