Servlet与应用服务器
客户端和服务器之间的通信是通过网络建立连接,然后彼此之间传递数据,为了让对方明白自己的信息,彼此之间就会约定一套规范,这就是应用层协议。应用层协议不一定是众所周知的规范,但通信双方应该能够明白这套规范(能够进行协议解析)。要规范出一套协议并说明解析过程并不简单,所以就有标准化组织定义了一些常用的协议,如FTP协议、Http协议等。
服务器和客户端之间的通信需要先建立连接,在接收到对方的数据时也需要先进行协议分析以明白对方的意图,然后根据自己的业务需求做出相应的处理(或自己去做其他事情,或给与相应的应答)。整过过程非常繁杂且存在冗余,于是有聪明人就决定把与业务无关的部分拆分出来,形成稳定的、与业务无关的代码,只要满足相同的协议,就可以复用,而与业务相关的代码必须进行编写,前者就是应用服务器,后者为应用程序,两者之间的接口就形成了一组规范(而我王浪更愿意把前者叫做应用程序容器,后者叫做应用程序,两者之和叫做应用服务器)。
以Http协议为例,Http协议是一个短连接协议,由客户端建立连接并提出请求,服务器响应请求后断开连接。我们编写服务器程序的过程大致如下:首先进行端口监听,然后根据监听内容和应用层协议对请求进行分析,根据请求与业务需要返回相应结果,然后断开连接。从中可以看出,从监听端口收到请求到返回结果并断开连接的过程是非常复杂的,然而这整个过程中,除了根据请求与业务需要返回相应结果外,其他部分对于任何符合该协议的系统都是可重用的,于是程序的第一部分就监听端口获得请求,解析协议后加工数据并回调第二部分的接口,当回调接口返回数据后再返回给客户端并断开连接,第二部分完成业务处理并将处理过程根据要求注册到第一部分,这个注册接口就是一组规范,包括注册方式,第一部分回调第二部分时的参数和回调函数的返回值,Servlet就是其中的一个规范,而Tomcat就是实现了Servlet规范的应用服务器,而我们基于Servlet编写的业务代码就是应用程序。
配置文件web.xml
web.xml是Servlet应用程序和Servlet容器建立关系的桥梁。在Servlet容器启动时会解析一些配置文件并会根据解析结果做出相应的初始化工作(其中就包括Servlet的注册),Servlet容器的初始化过程这里就不详解,总之Servlet容器一定会按照Servlet规范解析应用程序根目录下的WEB-INF/web.xml文件(tomcat会在解析该文件之前解析tomcat安装目录下的web.xml文件,该web.xml作用在tomcat启动的所有应用,其中有个默认的DefaultServlet的注册,在找不到匹配路径的情况下该Servlet会进行处理)。下面以web-app4.0为例解析web.xml的结构:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 根节点,其他节点只能是其子孙节点,整个文件有且仅有一个根节点,而且标签为web-app,web-app2.3及以前用dtd定义,不同子节点必须按先后顺序书写,2.4以后用xsd定义,子节点顺序可以不必关注 -->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<!-- 定义了WEB应用的名字,现无实际用处 -->
<display-name>test</display-name>
<!-- 声明WEB应用的描述信息,现无实际用处 -->
<description>this is a test application</description>
<!-- context-param元素声明应用范围内的初始化参数,键值对,可以有多个或者没有 -->
<context-param>
<param-name>key</param-name>
<param-value>value</param-value>
</context-param>
<!-- Listener元素指出事件监听程序类,事件监听程序在建立、修改和删除会话或servlet环境时得到通知,可以有多个或者没有 -->
<listener>
<listener-class>com.wanglang.test.listener.MyListener</listener-class>
</listener>
<!-- filter的注册,将一个名字与一个实现javax.servlet.Filter接口的类相关联,可以有多个或者没有,filter-mapping的节点顺序代表着filter的执行顺序 -->
<filter>
<filter-name>filterName</filter-name>
<filter-class>com.wanglang.test.filter.MyFilter</filter-class>
<!-- 该filter可见的参数,可以有多个或者没有 -->
<init-param>
<param-name>key</param-name>
<param-value>value</param-value>
</init-param>
</filter>
<!-- 把已命名注册的filter与一个或多个url正则表达式关联起来,当url符合这些正则表达式之一的请求到来就会进入过滤器 -->
<filter-mapping>
<filter-name>filterName</filter-name>
<url-pattern>/abc/*</url-pattern>
<url-pattern>/def/*</url-pattern>
<!-- 拦截的请求分发类型 -->
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<!-- servlet的注册,将一个名字与一个实现javax.servlet.Servlet接口的类相关联,可以有多个或者没有 -->
<servlet>
<servlet-name>myServlet</servlet-name>
<!-- filter-class与jsp-file只能有一个,不能同时存在,分别注册servlet和jsp -->
<servlet-class>com.wanglang.test.servlet.MyServlet</servlet-class>
<jsp-file>/static/abc.jsp</jsp-file>
<!-- 该servlet可见的参数,可以有多个或者没有 -->
<init-param>
<param-name>key</param-name>
<param-value>value</param-value>
</init-param>
<!-- 为负数时表示有访问时才加载该Servlet,为正数或0表示启动容器时加载,值越小越先加载,值相等由容器决定策略 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 把已命名注册的servlet与一个或多个url正则表达式关联起来,当url符合这些正则表达式之一的请求到来就会进入相应servlet -->
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/abc/*</url-pattern>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<!-- 配置session的超时时间,当大于该配置的时间未被访问时丢弃session -->
<session-config>
<session-timeout>120</session-timeout>
</session-config>
<!-- 服务器在收到以/结尾的url(直接访问根目录时可以不以/结尾)时,认定是目录,会根据列表中的配置名依次访问地址,直到第一次发现地址可用便访问该地址 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.action</welcome-file>
</welcome-file-list>
<!-- 当返回的错误码与error-code相同或抛出指定的exception时,重定向到指定url,可以有多个或者没有 -->
<error-page>
<!-- error-code与exception-type只能有一个 -->
<error-code>400</error-code>
<exception-type>com.wanglang.test.Exception.MyException</exception-type>
<location>/abc/m.abc</location>
</error-page>
<!-- 标签库配置,有些版本不需要次标签,直接把其子标签<taglib>放在web-app标签下面 -->
<jsp-config>
<!-- 引用标签库的uri和对应的标签库文件位置,可以配置多个标签库 -->
<taglib>
<taglib-uri>http://java.sun.com/jsp/jstl/fmt</taglib-uri>
<taglib-location>/WEB-INF/fmt.tld</taglib-location>
</taglib>
</jsp-config>
</web-app>
当访问地址除去应用上下文路径及前面的内容,再除去?及之后的参数,剩下的部分就可以用来做url匹配,如http://wanglang.com/myproject/abc/user.html?ab=4最后用于做匹配的就是/abc/user.html。url匹配不是简单的正则匹配,它有着自己的规则。所有规则总结为四种,第一为精确匹配,就是一字不差的匹配,如/abc/af只能匹配/abc/af,因为url用于匹配的部分必然是以/开始的,所以精确匹配必须以/开头;第二为路径匹配,其格式为/多级path/*或者/*,两个都会匹配/path;第三为扩展名匹配,格式为*.扩展名;第四为默认匹配,格式就一个斜线为/。这些格式都是定的,不要任意组合。
对于servlet,url-pattern映射匹配过程是有优先顺序的,精确匹配 > 路径匹配(如果符合多个路径匹配,选择匹配长度最长的) > 扩展名匹配(如果符合某个扩展名匹配但也符合路径匹配,会优先匹配路径匹配) > 默认匹配,当有一个servlet匹配成功以后,就不会去理会剩下的servlet了。而filter的匹配不存在优先顺序,会把所有匹配的列出来形成一个链。
配置信息数据结构
在Servlet框架中,表示配置信息的类主要有三个,代表整个应用上下文的类ServletContext(这也不能算是配置类),表示当前Servlet配置信息的类ServletConfig,表示当前Filter配置信息的类FilterConfig。
// 表示应用上下文,一个web应用对应一个上下文,它包含了容器和整个应用的基本信息和配置
public interface ServletContext {
// 上下文路径,如/riskcontrol,访问路径=协议://地址:端口/上下文路径/各个配置的url映射?参数键值对
String getContextPath();
// 根据相同容器下另一个web应用的上下文路径获取其应用上下文
ServletContext getContext(String var1);
// 获取容器信息,如"Apache Tomcat/7.0.86"
String getServerInfo();
// 获取<context-param>配置的参数
String getInitParameter(String var1);
// 设置上下文参数
boolean setInitParameter(String var1, String var2);
// 获取<context-param>配置的所有参数名
Enumeration<String> getInitParameterNames();
// <display-name>
String getServletContextName();
// 获取某个注册的Servlet信息
ServletRegistration getServletRegistration(String var1);
// 获取所有注册的Servlet信息
Map<String, ? extends ServletRegistration> getServletRegistrations();
// 获取某个注册的Filter信息
FilterRegistration getFilterRegistration(String var1);
// 获取所有注册的Filter信息
Map<String, ? extends FilterRegistration> getFilterRegistrations();
// 获取所有attribute的names
Enumeration<String> getAttributeNames();
// 获取某个名的属性
Object getAttribute(String name);
// 设置属性
void setAttribute(String name, Object object);
// 删除属性
void removeAttribute(String name);
}
// 一个注册的Servlet对应一个ServletConfig
public interface ServletConfig {
// 注册的Servlet名
String getServletName();
// 获取当前应用的应用上下文
ServletContext getServletContext();
// 获取该servlet的初始化参数
String getInitParameter(String var1);
// 获取该servlet的所有初始化参数名
Enumeration<String> getInitParameterNames();
}
// 一个注册的Filter对应一个FilterConfig
public interface FilterConfig {
// 注册的Filter名
String getFilterName();
// 获取当前应用的应用上下文
ServletContext getServletContext();
// 获取该filter的初始化参数
String getInitParameter(String var1);
// 获取该filter的所有初始化参数名
Enumeration<String> getInitParameterNames();
}
Servlet
从广义上将,Servlet指的是一组规范,从狭义上讲,Servlet是实现了Servlet接口的类,从更狭义上讲,Servlet就是一个接口,Servlet的实现类主要负责各类业务处理。
Servlet是单例,使用的时候一定要注意全局变量。
Servlet的生命周期为init->service->destroy,当第一个映射到某个Servlet的请求到来或者容器启动时会创建Servlet(根据Servlet配置时的标签决定创建时机),直到容器被关闭时销毁Servlet,在此期间每一个请求到来时,都会调用相应Servlet的Service方法。虚拟类GenericServlet类同时实现了Servlet与ServletConfig接口,其实就是让通过Servlet获取ServletConfig再访问Servlet的配置信息可以直接用GenericServlet的方法直接访问。
Servlet接口和GenericServlet的设计并非针对http协议的,针对http协议有其相应的实现类,这就是继承GenericServlet的子类HttpServlet,该类不同的地方主要有两点,第一,添加了service(HttpServletRequst req, HttpServletResponse res)方法,由于容器回调的是service(ServletRequest var1, ServletResponse var2)方法,所以其实现的service(ServletRequest var1, ServletResponse var2)方法就是直接将参数类型强转为HttpServletRequest和HttpServletRespons后调用service(HttpServletRequst req, HttpServletResponse res)方法,这就要求容器封装后传过来的参数的真实类型也必须是HttpServletRequest和HttpServletResponse(容器肯定根据协议是http协议真实创建的请求和响应对象都是HttpServletRequest和HttpServletResponse);第二,实现了service(HttpServletRequst req, HttpServletResponse res)方法,就是根据请求类型为post、get或head等调用了相应的doXxx方法,Xxx为请求方式如Post、Get、Head等,所以如果业务处理的Servlet可以接受所有请求类型,那么直接重写service(HttpServletRequst req, HttpServletResponse res)方法,否则重写相应的doXxx方法。也就是真实的业务处理只需要继承HttpServlet类并实现相应的doXxx方法或service方法。
@WebServlet注解可以通过注解来配置Servlet而无需在web.xml中配置servlet标签,使用方式在Servlet上添加@WebServlet(name=“servletName”, value/urlPatterns={"/servlet/a", “/servlet/b”}, loadOnStartup=1, initParams={@WebInitParam(name=“cc”, value = “b”), @WebInitParam(name=“abee”, value = “b”)})。现在value属性与urlPatterns是一样的作用,通常情况下只需要配置一个value指定访问路径就可以了。
// Servlet接口类,所有Servlet必须是其子类
public interface Servlet {
// 创建Servlet后立即调用
void init(ServletConfig var1) throws ServletException;
// 获取Servlet配置信息
ServletConfig getServletConfig();
// 每个请求到来时会调用该方法
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
// 获取Servlet信息
String getServletInfo();
// 容器关闭前回调该方法
void destroy();
}
过滤器
过滤器主要负责在客户端的请求访问后端资源之前,拦截这些请求,以及在服务器的响应发送回客户端之前,处理这些响应。
Filter的生命周期为init->doFilter->destroy,当容器启动时会创建Filter,直到容器被关闭时销毁Filter,在此期间每一个请求到来时,都会调用相应Filter的doFilter方法。虚拟类GenericFilter类同时实现了Filter与FilterConfig接口,其实就是让通过Filter获取FilterConfig再访问Filter的配置信息可以直接用GenericFilter的方法直接访问。
Filter接口和GenericFilter的设计并非针对http协议的,针对http协议有其相应的实现类,这就是继承GenericFilter的子类HttpFilter,该类不同的地方主要添加了doFilter(HttpServletRequst req, HttpServletResponse res, FilterChain chain)方法,由于容器回调的是doFilter(ServletRequest var1, ServletResponse var2, FilterChain chain))方法,所以其实现的doFilter(ServletRequest var1, ServletResponse var2, FilterChain chain)方法就是直接将参数类型强转为HttpServletRequest和HttpServletRespons后调用doFilter(HttpServletRequst req, HttpServletResponse res, FilterChain chain)方法,这就要求容器封装后传过来的参数的真实类型也必须是HttpServletRequest和HttpServletResponse(容器肯定根据协议是http协议真实创建的请求和响应对象都是HttpServletRequest和HttpServletResponse)。
@WebFilter注解可以通过注解来配置Filter而无需在web.xml中配置filter标签,使用方式在Filter上添加@WebFilter(name=“filterName”, value/urlPatterns={"/servlet/a", “/servlet/b”}, servletNames={“servlet1”, “servlet2”}, initParams={@WebInitParam(name=“cc”, value = “b”), @WebInitParam(name=“abee”, value = “b”)}, dispatcherTypes={DispatcherType.REQUEST(默认), DispatcherType.FORWARD})。注解方式无法指定Filter执行的现后顺序。
过滤链FilterChain确定了一次访问处理过程,其doFilter方法负责把请求的控制权交给下一个过滤器,如果已经是最后一个过滤器,那么就访问Servlet。当过滤链的doFilter返回时就把控制权交给了上一个Filter。所以过滤器的doFilter中,当需要拦截请求,不需要将请求移交到下一个处理器时,只要不调用过滤链的doFilter就可以了,如果希望后面处理之后的结果前面的处理器接着处理,那么过滤链的doFilter就是一个分界线。
// Filter接口类,所有的Filter必须是其子类
public interface Filter {
// 创建filter后立即调用
void init(FilterConfig filterConfig) throws ServletException;
// 过滤方法
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
// 容器关闭前回调该方法
void destroy();
}
我们可以自定义基础过滤器Filter,拦截请求与拦截响应分开,如下定义:
public abstract BaseFilter extern HttpFilter {
protected boolean preHandler(HttpServletRequest req, HttpServletResponse rsp) {
return false;
}
protected void postHandler(HttpServletRequest req, HttpServletResponse rsp) {
}
public void doFilter(HttpServletRequest req, HttpServletResponse rsp, FilterChain chain) throws IOException, ServletException {
if (preHandler(req, rsp)) {
return;
}
chain.doFilter(req, rep);
postHandler(req, rsp);
}
}
这样我们的自定义Filter就继承BaseFilter,只需要重写preHandler(如果不往后执行,返回true)和postHandler方法。
监听器
监听器用于监听ServletContext、HttpSession和ServletRequest等对象的创建与销毁,以及监听这些域对象中属性发生修改的事件。所有的监听类都继承自EventListener,这是个标记接口,没有任何方法。常用的有6个监听器,这6个监听器都需要注册,另外有2个监听器不需要注册,它们是在session绑定解绑或活化钝化时调用。
需要注册的监听器可以在web.xml中注册,也可以用@WebListener注解。
// 监听ServletContext的创建和销毁事件,通过监听事件可以获得ServletContext
public interface ServletContextListener extends EventListener {
// 一般在容器启动时创建ServletContext,优先于Filter和Servlet的初始化
void contextInitialized(ServletContextEvent sce);
// 一般在容器关闭时销毁ServletContext
void contextDestroyed(ServletContextEvent sce);
}
// 监听ServletContext的attribute事件
public interface ServletContextAttributeListener extends EventListener {
// 当调用ServletContext的setAttribute设置键值对时,如果key不存在,该方法回调
void attributeAdded(ServletContextAttributeEvent event);
// 当调用ServletContext的setAttribute设置键值对时,如果key已存在,该方法回调
void attributeReplaced(ServletContextAttributeEvent event);
// 当调用ServletContext的removeAttribute删除键值对时,如果key在删除前存在,该方法回调,不存在不会回调
void attributeRemoved(ServletContextAttributeEvent event);
}
// 监听HttpSession创建和销毁事件,通过监听事件可以可以获得HttpSession
public interface HttpSessionListener extends EventListener {
// 一般不带sessionid或者带无效sessionid来的时后容器创建HttpSession
void sessionCreated(HttpSessionEvent se);
// 一般超时或执行session.invalidate()时销毁HttpSession
void sessionDestroyed(HttpSessionEvent se);
}
// 监听HtpSession的attribute变化事件
public interface HttpSessionAttributeListener extends EventListener {
void attributeAdded(HttpSessionBindingEvent event);
void attributeRemoved(HttpSessionBindingEvent event);
void attributeReplaced(HttpSessionBindingEvent event);
}
// 监听ServletRequst创建和销毁事件,通过监听事件可以获得ServletRequest
public interface ServletRequestListener extends EventListener {
// 请求到来时由容器创建
void requestInitialized(ServletRequestEvent sre);
// 响应客户端后销毁请求
void requestDestroyed(ServletRequestEvent sre);
}
// 监听ServletRequest的attribute变化事件,RequestAttribute常用来在服务器各个组件之间传递数据
public interface ServletRequestAttributeListener extends EventListener {
void attributeAdded(ServletRequestAttributeEvent srae);
void attributeRemoved(ServletRequestAttributeEvent srae);
void attributeReplaced(ServletRequestAttributeEvent srae);
}
ServletContainerInitializer
ServletContainerInitializer机制在容器启动时,给第三方包一个机会完成初始化。在Servlet容器启动过程中(如Tomcat启动过程中),会解析所有jar包(也包括当前项目目录)中META-INF/services/javax.servlet.ServletContainerInitializer文件中的全类名(每行可配置一个全类名,该类应该实现了ServletContainerInitializer接口),然后根据全类名以反射方式创建实例,并调用其onStartup(Set<Class<?>> set, ServletContext servletContext)方法。
在调用onStartup(…)方法之前需要构造其实参。对于一个web应用,servletContext是全局唯一的,通过该参数可以对web配置进行一系列的操作(如添加Servlet,详见ServletContext)。自己实现的ServletContainerInitializer可以被@HandlesTypes标记,而该注解的value属性是一个Class列表,该列表中所有Class的子类组成的集合(包括类的子类、接口的实现类、接口的子接口等,但不包括扩Class本身,如@HandlesTypes(A.class),set参数中包含包含A的子类,但不会包含类A),就是onStartup(…)方法的第一个参数。
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException;
}
请求和响应
当客户端发送请求给容器时,容器会根据请求内容组装一个请求结构体HttpServletRequest和一个默认的响应结构体HttpServletResponse,并将他们传送给应用程序来处理,然后应用程序的所有操作就是根据请求结构体来操作响应结构体,最后由容器将处理后的响应发送给客户端。
请求头可以是标准规范的,这些请求头服务器可以识别解析处理,也可以自定义一些请求头,供应用程序解析。常用的标准请求头有Accept(客户端希望服务端返回的mime类型,可以有多个)、Accept-Charset、Accept-Encoding(浏览器可处理的格式、压缩方式)、Accept-Language、Content-Length、Authorization、Connection、Cookie、Host、Referer(发出此次请求的页面)、User-Agent(代表着浏览器本身的信息,如是火狐、google还是ie,手机端浏览器等)。
响应头可以是标准规范的,这些响应头浏览器应该能够解析处理,也可以自定义一些请求头,供前端页面解析。常用的标准响应头有Allow、Cache-Control、Connection、Content-Disposition、Content-Encoding、Content-Language、Content-Length、Content-Type(响应文档的mime类型)、Expires、Location、Refresh、Set-Cookie。
public interface ServletRequest {
public Object getAttribute(String name);
public Enumeration<String> getAttributeNames();
public void setAttribute(String name, Object o);
public void removeAttribute(String name);
public String getCharacterEncoding();
public String getContentType();
public int getContentLength();
public long getContentLengthLong();
public ServletInputStream getInputStream() throws IOException;
public BufferedReader getReader() throws IOException;
public String getParameter(String name);
public Enumeration<String> getParameterNames();
public String[] getParameterValues(String name);
public Map<String, String[]> getParameterMap();
public String getProtocol();
public int getServerPort();
public String getScheme();
public String getRemoteAddr();
public String getRemoteHost();
public int getRemotePort();
public String getLocalName();
public String getLocalAddr();
public int getLocalPort();
public String getServerName();
public Locale getLocale();
public Enumeration<Locale> getLocales();
public RequestDispatcher getRequestDispatcher(String path);
public DispatcherType getDispatcherType(); // 请求来源,值为DispatcherType.REQUEST(直接来源与客户端),DispatcherType.FORWARD(由上一个请求forward而来),DispatcherType.INCLUDE(由上一个请求include而来),DispatcherType.ERROR(由web.xml的error-page标签而来),DispatcherType.ASYNC()
public String getRealPath(String path);
public ServletContext getServletContext();
}
public interface HttpServletRequest extends ServletRequest {
// 获取请求cookie
public Cookie[] getCookies();
public long getDateHeader(String name);
public String getHeader(String name);
public Enumeration<String> getHeaders(String name);
public Enumeration<String> getHeaderNames();
public int getIntHeader(String name);
public String getMethod();
public String getPathInfo();
public String getPathTranslated();
public String getContextPath();
public String getQueryString();
public String getRemoteUser();
public String getRequestedSessionId();
public String getRequestURI();
public StringBuffer getRequestURL();
public String getServletPath();
public HttpSession getSession(boolean create);
// 获取session
public HttpSession getSession();
public String changeSessionId();
public boolean isRequestedSessionIdValid();
public boolean isRequestedSessionIdFromCookie();
public boolean isRequestedSessionIdFromURL();
public boolean isRequestedSessionIdFromUrl();
public Collection<Part> getParts() throws IOException, ServletException;
public Part getPart(String name) throws IOException, ServletException;
public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException;
}
public interface ServletResponse {
public Locale getLocale();
public void setLocale(Locale loc);
public String getCharacterEncoding();
public void setCharacterEncoding(String charset);
public String getContentType();
public void setContentType(String type);
public ServletOutputStream getOutputStream() throws IOException;
public PrintWriter getWriter() throws IOException;
public void setContentLength(int len);
public void setContentLengthLong(long len);
public void setBufferSize(int size);
public int getBufferSize();
public void flushBuffer() throws IOException;
public void resetBuffer();
public boolean isCommitted();
public void reset();
}
// 本类还定义了标准的HTTP响应码
public interface HttpServletResponse extends ServletResponse {
public void addCookie(Cookie cookie);
public void sendError(int sc) throws IOException;
public void sendError(int sc, String msg) throws IOException;
public int getStatus();
public void setStatus(int sc);
// 重定向到一个新的地址,其实就是将状态码设置为302,并设置响应头Location为location
public void sendRedirect(String location) throws IOException;
// 响应头
public boolean containsHeader(String name);
public String getHeader(String name);
public Collection<String> getHeaderNames();
public Collection<String> getHeaders(String name);
public void setDateHeader(String name, long date);
public void addDateHeader(String name, long date);
public void setHeader(String name, String value);
public void addHeader(String name, String value);
public void setIntHeader(String name, int value);
public void addIntHeader(String name, int value);
}
public class Cookie implements Cloneable, Serializable {
public Cookie(String name, String value);
// 该cookie的解释
public void setComment(String purpose);
public String getComment();
// 域名与路径,请求会根据域名和路径来决定携带哪些cookie
public void setDomain(String domain);
public String getDomain();
public void setPath(String uri);
public String getPath();
// 过期时间
public void setMaxAge(int expiry);
public int getMaxAge();
// 若为true只有在安全协议如https协议才能携带本cookie
public void setSecure(boolean flag);
public boolean getSecure();
public String getName();
public String getValue();
public void setValue(String newValue);
// 决定是否可以用js获取获取该cookie
public void setHttpOnly(boolean isHttpOnly);
public boolean isHttpOnly();
}
// 不带sessionid的请求都会创建新的session,这会加大服务器的负担,所以在不需要session时可以禁用session,也就是禁用创建sssion
public interface HttpSession {
public String getId();
public ServletContext getServletContext();
// 创建事件
public long getCreationTime();
// 最后访问事件
public long getLastAccessedTime();
// 最大过期时间
public void setMaxInactiveInterval(int interval);
public int getMaxInactiveInterval();
// 直接让session过期
public void invalidate();
// attribute操作
public Enumeration<String> getAttributeNames();
public Object getAttribute(String name);
public void setAttribute(String name, Object value);
public void removeAttribute(String name);
// 绑定和解绑
public String[] getValueNames();
public Object getValue(String name);
public void putValue(String name, Object value);
public void removeValue(String name);
}
重定向和分发
重定向是指浏览器请求时服务器响应客户端,响应码为302,外加一个url,客户端接收到该请求后重新访问新的url地址。实现重定向的第一种方法为设置响应码为302,同时设置响应头Location为新的url;第二种方法为调用响应的sendRedirect(String location)方法。
分发是服务器内的跳转。forward方式, 通过request.getRequestDispatcher("/abc/a.jsp").forward(request, response)跳转, 返回给前端的响应不包含跳转前的响应。include方式, 通过request.getRequestDispatcher("/abc/a.jsp").include(request, response)跳转, 返回给前端的响应包含跳转前的响应。