前言
??MVC 模式是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
Controller:对请求进行处理,负责请求转发; View:界面设计人员进行图形界面设计; Model: 程序编写程序应用的功能(实现算法等) 、数据库管理。
??MVC不是Java的东西,几乎所有B/S结构的软件都采用了MVC设计模式。 ??JavaWeb的三层:
WEB层:包含JSP和Servlet等与WEB相关的内容; 业务层:业务层中不包含JavaWeb API,它只关心业务逻辑; 数据层:封装了对数据库的访问细节。
??Web服务器的作用是接收客户端的请求,给客户端作出响应。对于JavaWeb程序而言,还需要有Servlet容器,Servlet容器的基本功能是把动态资源转换成静态资源。我们需要使用的是Web服务器和Servlet容器,通常这两者会集于一 身。 ??一些JavaWeb服务器: Tomcat、Weblogic等。
一、Servlet
??Servlet是JavaWeb的三大组件之一,它的作用是处理请求,服务器会把接收到的请求交给 Servlet 来处理,在 Servlet 中通常需要:接收请求数据、处理请求、完成响应。
1.1 Servlet的生命周期
??生命周期相关的方法:
void init(ServletConfig)
void service(ServletRequest request, ServletResponse response)
void destroy()
??Servlet的特点:
单例 ,一个具体的Servlet类只有一个对象(通过反射来创建对象),当然可能存在多个Servlet类;多线程的 ,所以它的效率是高的,不是线程安全的。
??Servlet类由开发者来写,但对象由服务器来创建,并且由服务器来调用相应的方法。
1.1.1 init
??服务器会在Servlet第一次被访问时创建Servlet,或者是在服务器启动时创建Servlet。如果服务器启动时就创建Servlet,那么还需要在web.xml文件中配置。也就是说默认情况下,Servlet是在第一次被访问时由服务器创建的。 ??在 Servlet 被创建后,服务器会马上调用 Servlet 的void init(ServletConfig) 方法,并且整个生命周期只调用一次。 ??ServletConfig:Servlet的配置信息,即web.xml文件中的<servlet> 。一个ServletConfig对象,对应着一个servlet元素的配置信息(servlet-name,servlet-class)。 ??ServletConfig中的方法: ??看一个<servlet> 的例子:
<servlet>
<servlet-name>One</servlet-name>
<servlet-class>cn.itcast.servlet.OneServlet</servlet-class>
<init-param>
<param-name>paramName1</param-name>
<param-value>paramValue1</param-value>
</init-param>
<init-param>
<param-name>paramName2</param-name>
<param-value>paramValue2</param-value>
</init-param>
</servlet>
??<init-param> 代表初始化参数。 ??ServletConfig中4个方法的解释:
String getServletName()
ServletContext getServletContext()
String getInitParameter(String name)
Enumeration getInitParameterNames()
1.1.2 service
??当服务器每次接收到请求时,都会去调用 Servlet 的service() 方法来处理请求。服务器接收到一次请求,就会调用service()方法一次。 ??ServletRequest和ServletResponse是service()方法的两个参数,代表请求对象和响应对象。开发者可以从ServletRequest对象中获取请求数据,可以使用ServletResponse对象完成响应。 ??ServletRequest和ServletResponse的实例由服务器创建,然后传递给service()方法。如果在service()方法中希望使用HTTP相关的功能,那么可以把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse。 ??HttpServletRequest中的方法:
String getParameter(String paramName)
String getMethod()
String getHeader(String name)
void setCharacterEncoding(String encoding)
??HttpServletResponse中的方法:
PrintWriter getWriter()
ServletOutputStream getOutputStream()
void setCharacterEncoding(String encoding)
void setHeader(String name, String value)
void setContentType(String contentType)
void sendError(int code, String errorMsg)
1.1.3 destroy
??在服务器被关闭时,服务器会去销毁Servlet,在销毁Servlet之前服务器会先去调用Servlet的destroy() 方法,该方法内可以写一些释放资源之类的代码。 ??一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁。即destroy方法,该方法被调用的两个常见场景:
- 1、关闭tomcat的时候。
- 2、
server.xml中将reloadable配置为"true" 就表示有任何类发生的更新,web应用会自动重启,当web应用自动重启的时候,destroy()方法就会被调用 。
1.2 实现Servlet的相关类
??实现 Servlet 有三种方式:
实现javax.servlet.Servlet接口; 继承javax.servlet.GenericServlet类; 继承 javax.servlet.http.HttpServlet类。
??通常会去继承HttpServlet类来完成自定义Servlet。
1.2.1 GenericServlet
??GenericServlet是Servlet接口的实现类,在GenericServlet源码中,可以看下init相关的方法:
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {}
??在GenericServlet中,定义了一个ServletConfig实例变量,并在init(ServletConfig)方法中把参数ServletConfig赋给了实例变量。然后在该类的很多方法中使用了实例变量config。 ??如果子类希望完成一些初始化操作,那么应该去覆盖GenericServlet的提供的init()方法,它是没有参数的init()方法,它会在init(ServletConfig)方法中被调用。 ??GenericServlet还实现了ServletConfig接口,所以可以直接调用getInitParameter()、getServletContext()等ServletConfig中的方法。
1.2.2 HttpServlet
??HttpServlet类是GenericServlet的子类,它提供了对HTTP请求的特殊支持,所以通常都会通过继承HttpServlet来完成自定义的Servlet。 ??HttpServlet类中提供了service(HttpServletRequest,HttpServletResponse)方法,这个方法是HttpServlet自己的方法,不是从Servlet继承来的。 ??在HttpServlet的service(ServletRequest,ServletResponse)方法中,会把 ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse,然后调用service(HttpServletRequest,HttpServletResponse)方法。相关源码:
public abstract class HttpServlet extends GenericServlet {
protected void service(HttpServletRequest req, HttpServletResponse
resp) throws ServletException, IOException {
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
}
- doGet()和doPost()
??在 在 HttpServlet 的 的 service(HttpServletRequest,HttpServletResponse) 方法会去判断当前请求是GET还是POST,如果是GET请求,那么会去调用本类的 doGet()方法,如果是POST请求会去调用doPost()方法,因此在子类中去覆盖doGet()或doPost()方法即可。示例:
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("hello doGet()...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("hello doPost()...");
}
}
1.3 ServletContext
??一个Web应用只有一个ServletContext对象,该对象在Tomcat启动时就创建,在Tomcat关闭时被销毁。 ??ServletContext对象的作用是在整个Web应用的动态资源之间共享数据。比如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值。 ??常见的获取ServletContext的方式:
ServletContext context = config.getServletContext();
ServletContext context = this.getServletContext()
1.3.1 域对象的功能
??JavaWeb四大域对象:PageContext、ServletRequest、HttpSession、ServletContext。 ??所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据。 ??ServletContext对象用来操作数据的方法:
void setAttribute(String name, Object value)
Object getAttribute(String name)
void removeAttribute(String name)
Enumeration getAttributeNames()
1.3.2 获取初始化参数
??Servlet也可以获取初始化参数,但它是局部的参数,即一个Servlet只能获取自己的初始化参数。 ??使用ServletContext可以获取全局(比如在web.xml文件中配置的全局参数)的初始化参数。示例:
<web-app ...>
<context-param>
<param-name>paramName1</param-name>
<param-value>paramValue1</param-value>
</context-param>
</web-app>
ServletContext context = this.getServletContext();
String value1 = context.getInitParameter("paramName1");
1.4 Servlet的一些特点
1.4.1 线程不安全
??由于Servlet是单例的,当多个用户访问Servlet的时候,服务器会为每个用户创建一个线程。当多个用户并发访问Servlet共享资源的时候就会出现线程安全问题。 ??原则:
- 1、如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制
synchronized (对象){} 。 - 2、如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义,这样不会存在线程安全问题。
- 3、
避免使用实例变量 ??线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量(尽量都定义局部变量),那么该Servlet就是线程安全的。
1.4.2 Servlet的创建顺序
??默认情况下,服务器会在某个Servlet第一次收到请求时创建它。也可以在web.xml中对Servlet进行配置,使服务器启动时就创建Servlet。示例:
<servlet>
<servlet-name>hello1</servlet-name>
<servlet-class>cn.itcast.servlet.Hello1Servlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello1</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>hello2</servlet-name>
<servlet-class>cn.itcast.servlet.Hello2Servlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>hello3</servlet-name>
<servlet-class>cn.itcast.servlet.Hello3Servlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello3</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
??在<servlet> 元素中配置<load-on-startup> 元素,可以让服务器在启动时就创建该 Servlet。其中<load-on-startup> 元素的值必须是大于等于0的整数,它表示服务器启动时创建Servlet 的顺序。正数的值越小,启动时加载该servlet的优先级越高。如果为负数,则容器启动时不会加载该servlet,只有该servlet被访问时才会加载。 ??上面例子中,根据<load-on-startup> 的值可以得知服务器创建Servlet的顺序为Hello1Servlet 、Hello2Servlet 、Hello3Servlet。
1.4.3 路径匹配
??<url-pattern> 是<servlet-mapping> 的子元素,用来指定Servlet的访问路径,必须是以“/”开头。 ??可以在<servlet-mapping> 中给出多个<url-pattern> ,示例:
<servlet-mapping>
<servlet-name>AServlet</servlet-name>
<url-pattern>/AServlet</url-pattern>
<url-pattern>/BServlet</url-pattern>
</servlet-mapping>
??上述配置表示一个Servlet绑定了两个URL,无论访问/AServlet是/BServlet,访问的都是AServlet。 ??也可以在<url-pattern> 中使用通配符,所谓通配符就是星号“*”,星号可以匹配任何URL前缀或后缀,例如:
<url-pattern>/servlet/*<url-patter>
<url-pattern>*.do</url-pattern>
<url-pattern>/*<url-pattern>
??配通配符是一种模糊匹配URL的方式,如果存在更具体的<url-pattern> ,那么访问路径会去匹配具体的<url-pattern> 。例如:
<servlet>
<servlet-name>hello1</servlet-name>
<servlet-class>cn.itcast.servlet.Hello1Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello1</servlet-name>
<url-pattern>/servlet/hello1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>hello2</servlet-name>
<servlet-class>cn.itcast.servlet.Hello2Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
??当访问http://localhost:8080/hello/servlet/hello1 时,因为访问路径即匹配hello1的<url-pattern> ,又匹配hello2的<url-pattern> ,但因为hello1的<url-pattern> 中没有通配符,所以优先匹配,即设置 hello1。
1.4.4 web.xml文件的继承
??在${CATALINA_HOME}\conf\web.xml 中的内容,相当于写到了每个项目的web.xml中,它是所有web.xml的父文件。 ??每个完整的JavaWeb应用中都需要有web.xml,所有的web.xml文件都有一个共同的父文件,即Tomcat的conf/web.xml。
1.5 Servlet的相关问题
1.5.1 tomcat容器是如何创建servlet类实例
??当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件。 ??然后,对xml文件进行解析,并读取servlet注册信息。 ??最后,将每个应用中注册的servlet类都进行加载,并通过 反射的方式实例化。
1.5.2 Servlet的使用流程
??1)Tomcat容器中通过web.xml加载所有的Servlet。 ??2)用户在浏览器输入不同的地址,向Tomcat容器请求资源。 ??3)Tomcat容器根据地址首先在容器内找到应用对应的项目。 ??4)Tomcat容器再根据地址去web.xml找到相应的servlet地址。 ??5)Tomcat容器根据找到的servlet地址去web.xml找到相应的Servlet类,并实例化。 ??6)Tomcat容器实例化相应的Servlet,首先调用init方法。 ??7)Tomcat容器实例化相应的Servlet,首先调用service方法处理用户请求,比如post或者是get。 ??8)Servlet处理完成之后,现将数据给Tomcat容器,Tomcat容器再把处理结果给浏览器客户端。 ??9)Tomcat容器调用servlet实例的destory方法销毁这个实例。
二、Request
??request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给 service()方法,这说明在service()方法中可以通过request对象来获取请求数据。 ??request 的功能可以分为以下几种:
- 封装了请求头数据;
- 封装了请求正文数据,如果是GET请求,那么就没有正文;
- request是一个域对象,可以把它当成Map,来添加获取数据;
- request提供了请求转发和请求包含功能。
2.1 Request中的方法
- 1、域方法
??request是域对象。一个请求会创建一个request对象,如果在一个请求中经历了多个Servlet ,那么多个Servlet就可以使用request来共享数据。 ?? request的域方法:
void setAttribute(String name, Object value)
Object getAttribute(String name)
void removeAttribute(String name)
Enumeration getAttributeNames()
- 2、获取请求头数据
??request中,与请求头相关的方法有:
String getHeader(String name)
Enumeration getHeaderNames()
int getIntHeader(String name)
long getDateHeader(String name)
int getContentLength()
String getContentType()
String getMethod()
Locale getLocale()
String getCharacterEncoding()
void setCharacterEncoding(String code)
String getContextPath()
String getQueryString()
String getRequestURI()
StringBuffer getRequestURL()
String getServletPath()
String getRemoteAddr()
String getRemoteHost()
String getScheme()
String getServerName()
int getServerPort()
??图示: ??URL=协议名+主机名+端口号+URI 。 ??示例:
System.out.println("IP:"+request.getRemoteAddr());
System.out.println("请求方式:"+request.getMethod());
System.out.println("User-Agent请求头:"+request.getHeader("User-Agent"));
System.out.println("协议名:"+request.getScheme());
System.out.println("主机名:"+request.getServerName());
System.out.println("端口号:"+request.getServerPort());
System.out.println("项目:"+request.getContextPath());
System.out.println("Servlet路径:"+request.getServletPath());
System.out.println("请求参数:"+request.getQueryString());
System.out.println("URI:"+request.getRequestURI());
System.out.println("URL:"+request.getRequestURL());
??结果:
IP:127.0.0.1 请求方式:GET User-Agent请求头:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 协议名:http 主机名:localhost 端口号:8080 项目:/Request&Response Servlet路径:/RequestHeaderServlet 请求参数: username=sxj&password=sxj URI:/Request&Response/RequestHeaderServlet(RequestURI) URL:http://localhost:8080/Request&Response/RequestHeaderServlet
2.2 GET和POST
??最为常见的客户端传递参数方式:
- 1、Get请求
??浏览器地址栏直接输入URL和超链接都是Get请求。如果不指定名method,则也是Get请求。 ??表单提交:可以是GET,也可以是POST,这取决于<form> 的method属性值。
??GET和POST请求的简单比较:
| GET | POST |
---|
请求参数的位置 | 请求参数放在URL地址后面,以?的方式来进行拼接 | 请求参数放在HTTP请求体中 | 安全性 | 请求参数会在浏览器的地址栏中显示,所以不安全 | 请求参数不会显示浏览器的地址栏,相对安全 | 参数长度是否有限制 | 请求参数长度限制长度在1K之内 | 请求参数长度没有限制 | 是否可以设置编码格式 | 没有请求体,无法通过 request.setCharacterEncoding()来设置参数的编码 | 可以设置 | 用途 | 一般用来获取数据 | 一般用来提交数据 | 速度 | 快 | 慢 |
??无论是GET,还是POST请求,都可以使用相同的 API 来获取请求参数。请求参数有一个key一个value的,也有一个key多个value的。示例:
String getParameter(String name)
String[] getParameterValues(String name)
Enumeration getParameterNames()
Map getParameterMap()
2.3 转发
??在 AServlet 中,把请求转发到BServlet,参数是Servlet路径,示例:
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("AServlet");
RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
rd.forward(request, response);
}
}
三、Response
??在客户端发出每个请求时,服务器都会创建一个response对象,并传入给 Servlet.service()方法。response 对象是用来对客户端进行响应的,这说明在service()方法中使用response对象可以完成对客户端的响应工作。 ??response对象的功能分为以下四种:设置响应头信息、发送状态码、设置响应正文、重定向。
3.1 响应正文
??response是响应对象,向客户端输出响应正文(响应体)可以使用response的响应流,repsonse一共提供了两个响应流对象:
PrintWriter out = response.getWriter();
ServletOutputStream out = response.getOutputStream();
??如果响应正文内容为字符(html) ,那么使用 response.getWriter() ;如果响应内容是字节(图片等) ,例如下载时,那么可以使用 response.getOutputStream()。 ??在一个请求中,不能同时使用这两个流 。即要么你使用repsonse.getWriter(),要么使用response.getOutputStream(),但不能同时使用这两个流 ,不然会出抛出IllegalStateException异常。
- 字符响应流
??使用response.getWriter()时,默认字符编码为ISO-8859-1,如果希望输出给客户端的字符都是使用UTF-8编码,可以使用response.setCharaceterEncoding(“utf-8”)来设置。 ??但如果仅使用上述方法,客户端浏览器并不知道响应数据是什么编码的。如果希望通知客户端使用UTF-8格式来解读响应数据,那么还是使用response.setContentType("text/html;charset=utf-8") 方法比较好。因为这个方法不只会调用response.setCharaceterEncoding(“utf-8”),还会设置content-type响应头,客户端浏览器会使用 content-type 头来解读响应数据。 ??response.getWriter()是PrintWriter 类型,所以它有缓冲区,缓冲区的默认大小为 8KB。即在响应数据没有输出8KB之前,数据都是存放在缓冲区中,而不会立刻发送到客户端。当 Servlet 执行结束后,服务器才会去刷新流,使缓冲区中的数据发送到客户端。 ??如果希望响应数据马上发送给客户端:
- 向流中写入大于 8KB 的数据;
- 调用 response.flushBuffer()方法来手动刷新缓冲区。
3.2 响应头和状态码
??响应头是键值对的形式。可以使用response对象的setHeader()方法来设置响应头。使用该方法设置的响应头最终会发送给客户端浏览器,示例:
response.setHeader("content-type", "text/html;charset=utf-8")
response.setHeader("Refresh","5; URL=http://www.itcast.cn")
??设置状态码的相关方法:
response.setContentType("text/html;charset=utf-8")
response.setCharacterEncoding(“utf-8”)
response.setStatus(200)
3.3 重定向
??重定向是服务器通知浏览器去访问另一个地址,即再发出另一个请求。 ??响应码为200,表示响应成功。响应码为302,表示重定向。 ??因为重定向是通知浏览器发出第二个请求,所以浏览器需要知道第二个请求的URL,所以完成重定向的第二步是设置Location头,指定第二个请求的URL地址。示例:
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setStatus(302);
response.setHeader("Location", "http://www.itcast.cn");
}
}
??sendRedirect是一种简单的重定向方式:
response.sendRedirect("http://www.itcast.cn");
response.sendRedirect("/hello/BServlet");
??重定向的特点:
- 重定向是两次请求;
- 重定向的URL可以是其他应用,不局限于当前应用;
- 重定向的响应头为302,并且必须要有 Location 响应头;
- 重定向就不要再使用response.getWriter()或response.getOutputStream() 输出数据 ,不然可能会出现异常。
3.4 转发和重定向的区别
- 1、实际发生位置不同,地址栏不同
??转发是由服务器进行跳转的 ,在转发的时候,浏览器的地址栏是没有发生变化的,浏览器是不知道该跳转的动作。实现转发只是一次的http请求,一次转发中request和response对象都是同一个。这也解释了,为什么可以使用request作为域对象进行Servlet之间的通讯。 ??重定向是由浏览器进行跳转的 ,进行重定向跳转的时候,浏览器的地址会发生变化的。实现重定向的原理是由response的状态码和Location头组合而实现的。这是由浏览器进行的页面跳转实现重定向会发出两个http请求,request域对象是无效的,因为它不是同一个request对象。 - 2、用法不同
?? 给服务器(转发)用的直接从资源名开始写,给浏览器(重定向)用的要把应用名写上 。 - 3、能够去往的URL的范围不一样
??转发是服务器跳转只能去往当前web应用的资源。 ??重定向是服务器跳转,可以去往任何的资源。 - 4、传递数据的类型不同
??转发的request对象可以传递各种类型的数据,包括对象 。 ??重定向只能传递字符串 。 - 5、跳转的时间不同
??转发时:执行到跳转语句时就会立刻跳转 。 ??重定向:整个页面执行完之后才执行跳转 。 - 6、从效率来说
??转发:高。 ??重定向:低。
??转发是带着转发前的请求的参数的。重定向是新的请求 。
四、Filter
??过滤器是Java Web三大组件之一,它与Servlet很相似。不过过滤器是用来拦截请求的 ,而不是处理请求的。 ??当用户请求某个Servlet时,会先执行部署在这个请求上的Filter,如果Filter“ 放行” ,那么会继承执行用户请求的Servlet;如果Filter不 “ 放行”,那么就不会执行用户请求的Servlet。 ??Filter简单使用示例:
public class HelloFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("filter start...");
chain.doFilter(request, response);
System.out.println("filter end...");
}
public void destroy() {}
}
??在web.xml文件中配置Filter:
<filter>
<filter-name>helloFilter</filter-name>
<filter-class>cn.itcast.filter.HelloFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>helloFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
??在经过上面的配置后,在访问项目中的任何一个url时,都会经过HelloFilter。
4.1 过滤器的生命周期
??过滤器也是单例的。
4.1.1 init(FilterConfig)
??在服务器(如Tomcat)启动时建会创建Filter实例,并且每个类型的Filter只创建一个实例 。在创建完Filter实例后,会马上调用init()方法完成初始化工作,这个方法只会被执行一次。 ??FilterConfig的功能与ServletConfig相似,与web.xml文件中的配置信息对应。常用方法:
ServletContext getServletContext()
String getFilterName()
String getInitParameter(String name)
Enumeration getInitParameterNames()
4.1.2 doFilter(ServletRequest req,ServletResponse res,FilterChain chain)
??过滤方法,如果需要“放行”,那么需要调用FilterChain的doFilter(ServletRequest,ServletResponse)方法,如果不调用FilterChain的doFilter() 方法,那么目标资源将无法执行。 ??FilterChain的doFilter() 方法的含义:
- 如果当前过滤器是最后一个过滤器,那么调用chain.doFilter()方法表示执行目标资源;
- 如果不是最后一个过滤器,那么 chain.doFilter() 表示执行下一个过滤器的 doFilter() 方法。
4.1.3 destroy()
??服务器会在创建Filter对象之后,把Filter放到缓存中一直使用,通常不会销毁它,一般会在服务器(如Tomcat)关闭时销毁Filter对象。在销毁Filter对象之前,服务器会调用Filter对象的destory()方法。
4.2 多个过滤器的执行顺序
??一个目标资源可以指定多个过滤器在 ,过滤器的执行顺序是在 web.xml 文件中的部署顺序。示例:
<filter>
<filter-name>myFilter1</filter-name>
<filter-class>cn.itcast.filter.MyFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter1</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
<filter>
<filter-name>myFilter2</filter-name>
<filter-class>cn.itcast.filter.MyFilter2</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter2</filter-name>
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
public class MyFilter1 extends HttpFilter {
public void doFilter(HttpServletRequest request, HttpServletResponse response,FilterChain chain) throws IOException, ServletException {
System.out.println("filter1 start...");
chain.doFilter(request, response);
System.out.println("filter1 end...");
}
}
public class MyFilter2 extends HttpFilter {
public void doFilter(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws IOException, ServletException {
System.out.println("filter2 start...");
chain.doFilter(request, response);
System.out.println("filter2 end...");
}
}
??当有用户访问index.jsp页面时,输出结果:
filter1 start… filter2 start… index.jsp filter2 end… filter1 end…
4.3 过滤器的应用场景
- 1、执行目标资源之前做预处理工作
??例如设置编码,这种通常都会放行,只是在目标资源执行之前做一些准备工作。几乎是所有的Sevlet中,都需要写request.setCharacterEndoing() 可以把它放入到 一个 Filter 中。 - 2、实现URL级别的权限认证
??例如校验当前用户是否已经登录,或者用户IP是否已经被禁用。 - 3、在目标资源执行后,做一些后续的特殊处理工作
??例如把目标资源输出的数据进行处理。
五、Cookie
5.1 Cookie简介
??网页之间的交互是通过HTTP协议传输数据的,而Http协议是无状态的协议 。 ??无状态的协议是什么意思呢?一旦数据提交完后,浏览器和服务器的连接就会关闭,再次交互的时候需要重新建立新的连接。服务器无法确认用户的信息,于是W3C就提出了:给每一个用户都发一个通行证,无论谁访问的时候都需要携带通行证,这样服务器就可以从通行证上确认用户的信息。通行证就是Cookie。 ??Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie) 。当客户端向服务器发出请求时,会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端。
- 1、Cookie规范
?? HTTP的Cookie规范:
- Cookie大小上限为4KB;
- 一个服务器最多在客户端浏览器上保存20个Cookie;
- 一个浏览器最多保存300个Cookie。
??一些浏览器可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为 8KB,最多可保存500个Cookie等。 ??不同浏览器之间是不共享 Cookie 的 。
- 2、Cookie与HTTP头
??Cookie 是通过HTTP请求头和响应头在客户端和服务器端传递的。 ??在客户端发送给服务器端的请求头,Cookie格式:Cookie: a=A; b=B; c=C 。即多个 Cookie 用分号分开;个 一个 Cookie 对应多个键值对 ??在服务器端发送给客户端的响应头中,一个Cookie对象一个Set-Cookie,一个键值对:Set-Cookie: a=A 、Set-Cookie: b=B 、Set-Cookie: c=C 。比如可以用 response.addHeader("Set-Cookie","a=A"); 发送一个 Cookie。 ??获取Cookie的方式:
getCookies()
addCookie(Cookie cookie)
- 3、Cookie的覆盖
??如果服务器端发送重复的Cookie,那么会覆盖原有的 Cookie,例如客户端的第一个请求服务器端发送的Cookie是Set-Cookie: a=A 。第二请求服务器端发送的是Set-Cookie: a=AA ,那么客户端只留下一个 Cookie,即a=AA 。
5.2 Cookie中的属性
- 1、HttpOnly
??如果Cookie中设置了HttpOnly属性,那么通过JS脚本将无法读取到Cookie信息,这样能有效的防止XSS攻击,窃取Cookie内容,这样就增加了Cookie的安全性,即便是这样,也不要将重要信息存入 cookie。 ??Servlet3.0支持setHttpOnly(boolean httpOnly) 。 ??非Servlet3.0的JAVAEE项目也可以通过设置Header进行设置: response.setHeader("Set-Cookie", "cookiename=value;Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly") 。 - 2、max-age
??expires/Max-Age 字段为此Cookie超时时间。若设置其值为一个时间,那么当到达此时间后,此Cookie失效。不设置的话默认值是Session,意思是Cookie会和Session一起失效。 ??当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此Cookie失效。 - 3、secure
??设置是否只能通过https来传递此cookie。
5.3 Cookie的生命周期
??所谓生命周期就是Cookie在客户端的有效时间,可以通过setMaxAge(int) 来设置Cookie的有效时间。示例:
- cookie.setMaxAge(-1)
??Cookie的maxAge属性的默认值就是-1,表示只在浏览器内存中存活。一旦关闭浏览器窗口,那么Cookie就会消失。 - cookie.setMaxAge(60*60)
??表示Cookie对象可存活1小时 。当生命大于0时,浏览器会把Cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,Cookie也会存活1小时。 - cookie.setMaxAge(0)
??0是一个特殊的值,它表示Cookie被作废。也就是说,如果原来浏览器已经保存了这个Cookie,那么可以通过Cookie的setMaxAge(0) 来删除这个Cookie。无论是在浏览器内存中,还是在客户端硬盘上都会删除这个 Cookie。
5.4 Cookie的路径
??现在有Web应用A,向客户端发送了10个Cookie,这就说明客户端无论访问应用A的哪个Servlet 都会把这10个Cookie包含在请求中!但是也许只有AServlet需要读取请求中的Cookie,而其他Servlet根本就不会获取请求中的Cookie。这说明客户端浏览器有时发送这些Cookie是多余的。 ??可以通过设置Cookie的path来指定浏览器,在访问什么样的路径时,包含什么样的Cookie。
- 1、Cookie路径与请求路径的关系
??客户端浏览器保存的3个Cookie的路径示例:
a: /cookietest; b: /cookietest/servlet; c: /cookietest/jsp;
??浏览器请求的URL:
A: http://localhost:8080/cookietest/AServlet; B: http://localhost:8080/cookietest/servlet/BServlet; C: http://localhost:8080/cookietest/jsp/a.jsp;
??请求A时,会在请求中包含 a;请求B时,会在请求中包含 a、b;请求 C 时,会在请求中包含 a、c. ??请求路径如果包含了Cookie路径,那么会在请求中包含这个Cookie 。 ??1)A请求的URL包含了“/cookietest”,所以会在请求中包含路径为“/cookietest”的 Cookie。 ??2)B请求的URL包含了“/cookietest”,以及“/cookietest/servlet”,所以请求中包含路径为“/cookietest”和“/cookietest/servlet”两个Cookie。 ??3)C请求的URL包含了“/cookietest”,以及“/cookietest/jsp”,所以请求中包含路径为“/cookietest”和“/cookietest/jsp”两个Cookie。
- 2、设置Cookie的路径
??设置Cookie的路径需要使用setPath()方法,例如: cookie.setPath("/cookietest/servlet"); 。 ??默认路径是访问资源的上一级路径 。 ??如果没有设置Cookie的路径,那么Cookie路径的默认值当前访问资源所在路径( 上一级),例如:
- 访问http://localhost:8080/cookietest/AServlet时,添加的Cookie默认路径为/cookietest;
- 访问 http://localhost:8080/cookietest/servlet/BServlet时,添加的Cookie默认路径为
/cookietest/servlet; - 访问http://localhost:8080/cookietest/jsp/BServlet时,添加的Cookie默认路径为/cookietest/jsp。
5.5 Cookie的domain
??Cookie的domain属性,可以让网站中二级域共享Cookie 。 ??比如一些网址:
http://www.baidu.com http://zhidao.baidu.com http://news.baidu.com http://tieba.baidu.com
??假如希望在这些主机之间共享Cookie(例如在www.baidu.com中响应的Cookie,可以在news.baidu.com请求中包含)。这是主机的问题,即域名的问题。处理这一问题其实很简单,只需要下面两步:
- 1、设置Cookie的path为"/",示例:
cookie.setPath("/") ; - 2、设置Cookie的domain为".baidu.com",示例:
cookie.setDomain(".baidu.com") 。
5.6 Cookie保存中文
??Cookie的name和value都不能使用中文,如果希望在Cookie中使用中文,那么需要先对中文进行URL编码,然后把编码后的字符串放到Cookie中。 ??向客户端响应中添加 Cookie:
String name = URLEncoder.encode("姓名", "UTF-8");
String value = URLEncoder.encode("张三", "UTF-8");
Cookie c = new Cookie(name, value);
c.setMaxAge(3600);
response.addCookie(c);
??从客户端请求中获取 Cookie:
response.setContentType("text/html;charset=utf-8");
Cookie[] cs = request.getCookies();
if(cs != null) {
for(Cookie c : cs) {
String name = URLDecoder.decode(c.getName(), "UTF-8");
String value = URLDecoder.decode(c.getValue(),"UTF-8");
String s = name + ": " + value + "<br/>";
response.getWriter().print(s);
}
}
六、Session
6.1 HttpSession概述
??javax.servlet.http.HttpSession 接口表示一个会话,可以把一个会话内需要共享的数据保存到HttSession对象中。 ??会话:会话范围是某个用户从首次访问服务器开始,到该用户关闭浏览器结束。 ??获取HttpSession对象的方式:
HttpSession request.getSesssion()
HttpSession request.getSession(boolean)
??HttpSession和HttpServletRequest 、ServletContext相似,都是域对象。 ??HttpServletRequest :一个请求创建一个request对象,所以在同一个请求中可以共 享request,例如一个请求从AServlet转发到BServlet,那么AServlet和BServlet可以共享request域中的数据。 ??ServletContext :一个应用只创建一个ServletContext对象,所以在ServletContext中的数据可以在整个应用中共享,只要不关闭服务器,那么ServletContext 中的数据就可以共享。 ??HttpSession :一个会话创建一个HttpSession对象,同一会话中的多个请求中可以共享Session中的数据。
??Session的域方法:
void setAttribute(String name, Object value)
Object getAttribute(String name)
void removeAttribute(String name)
Enumeration getAttributeNames()
6.2 Session的实现原理
??当首次使用 session 时,服务器端要创建Session,Session是保存在服务器端,而给客户端的Session的id(一个Cookie中保存了sessionId) 。客户端带走的是 sessionId,而数据 是保存在 session 中。 ??当客户端再次访问服务器时,在请求中会带上sessionId,而服务器会通过 sessionId找到对应的Session,而无需再创建新的 session。 ??如果Servlet中没有创建Session,那么响应头中就不会有sessionid的Cookie。但是所有的JSP页面中,Session可以直接使用,也就是说每个JSP页面都会自动获取Session,无论是否使用,访问JSP页面一定会带回来一个sessionid。
6.3 Session的失效时间
??Session 保存在服务器,而sessionId通过Cookie发送给客户端,但这个Cookie的生命不是-1,即只在浏览器内存中存在,也就是说如果用户关闭了浏览器,那么这个Cookie就丢失了。 ??当用户再次打开浏览器访问服务器时,就不会有 sessionId 发送给服务器,那么服务器会认为你没有session,所以服务器会创建一个session,并在响应中把sessionId中的Cookie中发送给客户端。 ??当一个 session 长时间没人使用的话,服务器会把session删除。在这个时长在Tomcat中,配置是30分钟,可以在${CATALANA}/conf/web.xml 找到这个配置,当然也可以在自己的web.xml中覆盖这个配置:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
6.4 Session其他常用API
String getId()
int getMaxInactiveInterval()
void setMaxInactiveInterval(int interval)
long getCreationTime()
long getLastAccessedTime()
void invalidate()
boolean isNew()
6.5 设置过期时间的方式
??一般Web系统都需要控制session自动失效的时间,从而控制用户访问系统超时。设置Session失效有以下三种方式:
- 1、在主页面或者公共页面中配置
??示例:session.setMaxInactiveInterval(3600); ,参数单位是秒,即在没有活动1小时后,Session将失效。注意:这里Session设置的时间是根据服务器来计算的,而不是客户端。所以如果是在调试程序,应该是修改服务器端时间来测试,而不是客户端。 - 2、较通用的设置Session失效时间的方法是在项目的
web.xml 中设置。 - 3、直接在应用服务器中设置,例如:若容器是tomcat,可以在
tomcat目录下conf/web.xml 中设置。
??如果上述三个地方如果都设置了,优先级从高到低:(1)>(2)>(3)。
6.6 Session和Cookie的区别
| Session | Cookie |
---|
存储方式 | 可以存储任何类型的数据 | 只能存储字符串,如果要存储非ASCII字符串还要对其编码 | 隐私安全 | 存储在服务器,安全性高 | 存储在浏览器,安全性低 | 有效期 | 保存在服务器中,设置maxInactiveInterval属性值来确定Session的有效期。并且Session依赖于名为JSESSIONID的Cookie,该Cookie默认的maxAge属性为-1。如果关闭了浏览器,该Session虽然没有从服务器中消亡,但也就失效了 | 保存在硬盘中,只需要设置maxAge属性为比较大的正整数,即使关闭浏览器,Cookie还是存在的 | 对服务器的负担 | 保存在服务器的,每个用户都会产生一个Session,如果是并发访问的用户非常多,是不能使用Session的,Session会消耗大量的内存 | 保存在客户端的,不占用服务器的资源 | 是否能跨域名 | 只在当前的域名内有效,不可跨域名 | 可以设置domain属性来实现跨域名 | 大小限制 | 没有大小限制,和服务器的内存大小有关 | 有大小限制,以及浏览器在存Cookie的个数也有限制 |
|