diff --git a/README.md b/README.md
index d9f5178e..afade8b5 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,8 @@
-# Java-Interview
-本仓库会持续更新,欢迎Star给一个鼓励!
-Java 面试必会 直通BAT
+- 本仓库会持续更新,欢迎Star给一个鼓励!
+- 本github最初的版本是一份word文档,目前只是把word刚刚搬上来了,但是有些图片、排版还没来得急整理,看起来可能还是有点困难
+- 所以可以先关注一下我的公众号,在我的公众号[程序员乔戈里]后台回复 **888** 获取这个github仓库的PDF版本,左侧有导航栏,方便大家阅读。
-最近上班有点忙,会抽时间持续更新本仓库。
-
-java面试需要的各个方向和面试题会持续更新,可以先关注一波
-
-我的公众号 **程序员乔戈里**
-
-整理了这个仓库java的面试题和答案面试题和答案的pdf的可以在我的公众号后台回复 面经 自己耗时一个月整理的面试题和答案,后续也会持续更新,
-
-
->觉得文章不错的欢迎关注我的WX公众号:**程序员乔戈里**
-我是**百度**后台开发工程师,哈工大计算机本硕,专注分享技术干货/编程资源/求职面试/成长感悟等,关注送5000G编程资源和自己整理的一份帮助不少人拿下java的offer的面经附答案,免费下载CSDN资源。
+>github必须md格式才能看得舒服些,花了很多时间找word转md的工具,找了几款不太好用,于是自己手动把word改成md格式,后来发现有些重复性工作可以写个程序处理,就写了个程序,把word中的标题、代码都变成md格式,虽然能处理不少,但是还是需要人工校对,还有图片需要上传,真的超级费事,要搞吐了。。。各位也别抱怨我的github格式不好了,毕竟也还没完全处理完,体谅一下~

diff --git a/docs/1.md b/docs/1.md
deleted file mode 100644
index 8b137891..00000000
--- a/docs/1.md
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git "a/docs/Java\345\237\272\347\241\200\345\255\246\344\271\240.md" "b/docs/Java\345\237\272\347\241\200\345\255\246\344\271\240.md"
new file mode 100644
index 00000000..43b7f2a1
--- /dev/null
+++ "b/docs/Java\345\237\272\347\241\200\345\255\246\344\271\240.md"
@@ -0,0 +1,24 @@
+# Java
+
+Oracle JDK有部分源码是闭源的,如果确实需要可以查看OpenJDK的源码,可以在该网站获取。
+
+http://grepcode.com/snapshot/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/
+
+http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/73d5bcd0585d/src
+上面这个还可以查看native方法。
+
+### JDK&JRE&JVM
+
+JDK(Java Development Kit)是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE、Java工具(编译、开发工具)和Java核心类库。
+Java Runtime Environment(JRE)是运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库。
+JVM是Java Virtual Machine(Java虚拟机)的缩写,是整个java实现跨平台的最核心的部分,能够运行以Java语言写作的软件程序。
+
+
+
+JDK包含JRE和Java编译、开发工具;
+JRE包含JVM和Java核心类库;
+运行Java仅需要JRE;而开发Java需要JDK。
+
+### 跨平台
+
+字节码是在虚拟机上运行的,而不是编译器。换而言之,是因为JVM能跨平台安装,所以相应JAVA字节码便可以跟着在任何平台上运行。只要JVM自身的代码能在相应平台上运行,即JVM可行,则JAVA的程序员就可以不用考虑所写的程序要在哪里运行,反正都是在虚拟机上运行,然后变成相应平台的机器语言,而这个转变并不是程序员应该关心的。
diff --git "a/docs/Java\351\233\206\345\220\210.md" "b/docs/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230\345\217\212\347\255\224\346\241\210.md"
similarity index 100%
rename from "docs/Java\351\233\206\345\220\210.md"
rename to "docs/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230\345\217\212\347\255\224\346\241\210.md"
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..685fceb5
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,7 @@
+
+- 本github最初的版本是一份word文档,目前只是把word刚刚搬上来了,但是有些图片、排版还没来得急整理,看起来可能还是有点困难
+- 所以可以先关注一下我的公众号,在我的公众号后台回复 **888** 获取这个github仓库的PDF版本,左侧有导航栏,方便大家阅读。
+
+>github必须md格式才能看得舒服些,花了很多时间找word转md的工具,找了几款不太好用,于是自己手动把word改成md格式,后来发现有些重复性工作可以写个程序处理,就写了个程序,把word中的标题、代码都变成md格式,虽然能处理不少,但是还是需要人工校对,还有图片需要上传,真的超级费事,要搞吐了。。。各位也别抱怨我的github格式不好了,毕竟也还没完全处理完,体谅一下~
+
+
diff --git "a/docs/\344\270\200\343\200\201Java\345\237\272\347\241\200.md" "b/docs/\344\270\200\343\200\201Java\345\237\272\347\241\200.md"
index 84174c7a..bd0e0eac 100644
--- "a/docs/\344\270\200\343\200\201Java\345\237\272\347\241\200.md"
+++ "b/docs/\344\270\200\343\200\201Java\345\237\272\347\241\200.md"
@@ -1,3 +1,8 @@
+- 本github最初的版本是一份word文档,目前只是把word刚刚搬上来了,但是有些图片、排版还没来得急整理,看起来可能还是有点困难
+- 所以可以先关注一下我的公众号,在我的公众号后台回复 **888** 获取这个github仓库的PDF版本,左侧有导航栏,方便大家阅读。
+
+
+
# Java
- Oracle JDK有部分源码是闭源的,如果确实需要可以查看OpenJDK的源码,可以在该网站获取。
- http://grepcode.com/snapshot/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/
diff --git "a/docs/\344\270\203\343\200\201JavaWeb.md" "b/docs/\344\270\203\343\200\201JavaWeb.md"
new file mode 100644
index 00000000..e17dbbe8
--- /dev/null
+++ "b/docs/\344\270\203\343\200\201JavaWeb.md"
@@ -0,0 +1,4028 @@
+# JavaWeb
+# 三层模型 MVC
+- 1、MVC设计模式
+
+- MVC设计模式
+
+- MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
+- MVC模式最早为Trygve Reenskaug提出,为施乐帕罗奥多研究中心(Xerox PARC)的Smalltalk语言发明的一种软件设计模式。
+- MVC可对程序的后期维护和扩展提供了方便,并且使程序某些部分的重用提供了方便。而且MVC也使程序简化,更加直观。
+- 控制器Controller:对请求进行处理,负责请求转发;
+- 视图View:界面设计人员进行图形界面设计;
+- 模型Model:程序编写程序应用的功能(实现算法等等)、数据库管理;
+
+- 注意,MVC不是Java的东西,几乎现在所有B/S结构的软件都采用了MVC设计模式。但是要注意,MVC在B/S结构软件并没有完全实现,例如在我们今后的B/S软件中并不会有事件驱动!
+
+- 2、JavaWeb与MVC
+- JavaWeb的经历了JSP Model1、JSP Model1二代、JSP Model2三个时期。
+- 2.1 JSP Model1第一代
+- JSP Model1是JavaWeb早期的模型,它适合小型Web项目,开发成本低!Model1第一代时期,服务器端只有JSP页面,所有的操作都在JSP页面中,连访问数据库的API也在JSP页面中完成。也就是说,所有的东西都耦合在一起,对后期的维护和扩展极为不利。
+
+
+- 2.2 JSP Model1第二代
+- JSP Model1第二代有所改进,把业务逻辑的内容放到了JavaBean中,而JSP页面负责显示以及请求调度的工作。虽然第二代比第一代好了些,但还让JSP做了过多的工作,JSP中把视图工作和请求调度(控制器)的工作耦合在一起了。
+
+
+
+- 2.3 JSP Model2
+- JSP Model2模式已经可以清晰的看到MVC完整的结构了。
+- JSP:视图层,用来与用户打交道。负责接收用来的数据,以及显示数据给用户;
+- Servlet:控制层,负责找到合适的模型对象来处理业务逻辑,转发到合适的视图;
+- JavaBean:模型层,完成具体的业务工作,例如:开启、转账等。
+
+
+- JSP Model2适合多人合作开发大型的Web项目,各司其职,互不干涉,有利于开发中的分工,有利于组件的重用。但是,Web项目的开发难度加大,同时对开发人员的技术要求也提高了。
+
+# 5.1 JavaWeb经典三层框架
+- 我们常说的三层框架是由JavaWeb提出的,也就是说这是JavaWeb独有的!
+- 所谓三层是表述层(WEB层)、业务逻辑层(Business Logic),以及数据访问层(Data Access)。
+- WEB层:包含JSP和Servlet等与WEB相关的内容;
+- 业务层:业务层中不包含JavaWeb API,它只关心业务逻辑;
+- 数据层:封装了对数据库的访问细节;
+
+- 注意,在业务层中不能出现JavaWeb API,例如request、response等。也就是说,业务层代码是可重用的,甚至可以应用到非Web环境中。业务层的每个方法可以理解成一个万能,例如转账业务方法。业务层依赖数据层,而Web层依赖业务层!
+
+
+
+# Web服务器
+- Web服务器的作用是接收客户端的请求,给客户端作出响应。
+- 对于JavaWeb程序而已,还需要有Servlet容器,Servlet容器的基本功能是把动态资源转换成静态资源。
+- 我们需要使用的是Web服务器和Servlet容器,通常这两者会集于一身。下面是对JavaWeb服务器:
+- Tomcat(Apache):当前应用最广的JavaWeb服务器;
+- JBoss(Redhat红帽):支持JavaEE,应用比较广;EJB容器
+- GlassFish(Orcale):Oracle开发JavaWeb服务器,应用不是很广;
+- Resin(Caucho):支持JavaEE,应用越来越广;
+- Weblogic(Oracle):收费;支持JavaEE,适合大型项目;
+- Websphere(IBM):收费;支持JavaEE,适合大型项目;
+
+- 支持JavaEE是EJB容器
+
+# Servlet
+# 5.2 定义
+- Servlet是JavaWeb的三大组件之一,它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
+- 接收请求数据;
+- 处理请求;
+- 完成响应。
+- 例如客户端发出登录请求,或者输出注册请求,这些请求都应该由Servlet来完成处理!Servlet需要我们自己来编写,每个Servlet必须实现javax.servlet.Servlet接口。
+# 5.3 生命周期
+- 生命周期方法:
+- void init(ServletConfig):出生之后(1次);
+- void service(ServletRequest request, ServletResponse response):每次处理请求时都会被调用;
+- void destroy():临死之前(1次);
+
+- 特性:
+- 单例,一个类只有一个对象;当然可能存在多个Servlet类!
+- 多线程的,所以它的效率是高的!(不是线程安全的)
+
+- Servlet类由我们来写,但对象由服务器来创建,并且由服务器来调用相应的方法。
+## Servlet的出生
+- 服务器会在Servlet第一次被访问时创建Servlet,或者是在服务器启动时创建Servlet。如果服务器启动时就创建Servlet,那么还需要在web.xml文件中配置。也就是说默认情况下,Servlet是在第一次被访问时由服务器创建的。
+- 而且一个Servlet类型,服务器只创建一个实例对象,例如在我们首次访问http://localhost:8080/helloservlet/helloworld时,服务器通过“/helloworld”找到了绑定的Servlet名称为cn.itcast.servlet.HelloServlet,然后服务器查看这个类型的Servlet是否已经创建过,如果没有创建过,那么服务器才会通过反射来创建HelloServlet的实例。当我们再次访问http://localhost:8080/helloservlet/helloworld时,服务器就不会再次创建HelloServlet实例了,而是直接使用上次创建的实例。
+- 在Servlet被创建后,服务器会马上调用Servlet的void init(ServletConfig)方法。请记住, Servlet出生后马上就会调用init()方法,而且一个Servlet的一生。这个方法只会被调用一次。
+- 我们可以把一些对Servlet的初始化工作放到init方法中!
+## Servlet服务
+- 当服务器每次接收到请求时,都会去调用Servlet的service()方法来处理请求。服务器接收到一次请求,就会调用service() 方法一次,所以service()方法是会被调用多次的。正因为如此,所以我们才需要把处理请求的代码写到service()方法中!
+## Servlet的销毁
+- Servlet是不会轻易离去的,通常都是在服务器关闭时Servlet才会离去!在服务器被关闭时,服务器会去销毁Servlet,在销毁Servlet之前服务器会先去调用Servlet的destroy()方法,我们可以把Servlet的临终遗言放到destroy()方法中,例如对某些资源的释放等代码放到destroy()方法中。
+- Servlet接口相关类型
+- 在Servlet接口中还存在三个我们不熟悉的类型:
+- ServletRequest:service() 方法的参数,它表示请求对象,它封装了所有与请求相关的数据,它是由服务器创建的;
+- ServletResponse:service()方法的参数,它表示响应对象,在service()方法中完成对客户端的响应需要使用这个对象;
+- ServletConfig:init()方法的参数,它表示Servlet配置对象,它对应Servlet的配置
+- ServletRequest和ServletResponse
+- ServletRequest和ServletResponse是Servlet#service() 方法的两个参数,一个是请求对象,一个是响应对象,可以从ServletRequest对象中获取请求数据,可以使用ServletResponse对象完成响应。
+- ServletRequest和ServletResponse的实例由服务器创建,然后传递给service()方法。如果在service() 方法中希望使用HTTP相关的功能,那么可以把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse。这也说明我们经常需要在service()方法中对ServletRequest和ServletResponse进行强转,这是很心烦的事情。不过后面会有一个类来帮我们解决这一问题的。
+- HttpServletRequest方法:
+- String getParameter(String paramName):获取指定请求参数的值;
+- String getMethod():获取请求方法,例如GET或POST;
+- String getHeader(String name):获取指定请求头的值;
+- void setCharacterEncoding(String encoding):设置请求体的编码!
+- 因为GET请求没有请求体,所以这个方法只对POST请求有效。当调用
+- request.setCharacterEncoding(“utf-8”)之后,再通过getParameter()方法获取参数值时,那么参数值都已经通过了转码,即转换成了UTF-8编码。所以,这个方法必须在调用getParameter()方法之前调用!
+- HttpServletResponse方法:
+- PrintWriter getWriter():获取字符响应流,使用该流可以向客户端输出响应信息。例如response.getWriter().print(“
Hello JavaWeb!
”);
+- ServletOutputStream getOutputStream():获取字节响应流,当需要向客户端响应字节数据时,需要使用这个流,例如要向客户端响应图片;
+- void setCharacterEncoding(String encoding):用来设置字符响应流的编码,例如在调用setCharacterEncoding(“utf-8”);之后,再response.getWriter()获取字符响应流对象,这时的响应流的编码为utf-8,使用response.getWriter()输出的中文都会转换成utf-8编码后发送给客户端;
+- void setHeader(String name, String value):向客户端添加响应头信息,
+- 例如setHeader(“Refresh”, “3;url=http://www.itcast.cn”),表示3秒后自动刷新到http://www.itcast.cn;
+- void setContentType(String contentType):该方法是setHeader(“content-type”, “xxx”)的简便方法,即用来添加名为content-type响应头的方法。content-type响应头用来设置响应数据的MIME类型,例如要向客户端响应jpg的图片,那么可以setContentType(“image/jepg”),如果响应数据为文本类型,那么还要同时设置编码,例如setContentType(“text/html;chartset=utf-8”)表示响应数据类型为文本类型中的html类型,并且该方法会调用setCharacterEncoding(“utf-8”)方法;
+- void sendError(int code, String errorMsg):向客户端发送状态码,以及错误消息。例如给客户端发送404:response(404, “您要查找的资源不存在!”)。
+-
+
+- ServletConfig
+- Servlet的配置信息,即web.xml文件中的元素。
+- 一个ServletConfig对象对应着一个servlet元素的配置信息(servlet-name,servlet-class)
+
+
+- getServletName获取的是的功能
+- getServletContext获取的是Servlet上下文对象
+
+- ServletConfig对象对应web.xml文件中的元素。例如你想获取当前Servlet在web.xml文件中的配置名,那么可以使用servletConfig.getServletName()方法获取!
+
+- ServletConfig对象是由服务器创建的,然后传递给Servlet的init()方法,你可以在init()方法中使用它!
+- String getServletName():获取Servlet在web.xml文件中的配置名称,即指定的名称;
+- ServletContext getServletContext():用来获取ServletContext对象;
+- String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;
+- Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称;
+- 在元素中还可以配置初始化参数:
+
+ One
+ cn.itcast.servlet.OneServlet
+
+ paramName1
+ paramValue1
+
+
+ paramName2
+ paramValue2
+
+
+
+- 在OneServlet中,可以使用ServletConfig对象的getInitParameter()方法来获取初始化参数,例如:
+- String value1 = servletConfig.getInitParameter(“paramName1”);//获取到paramValue1
+# 5.4 实现Servlet的方式
+- 实现Servlet有三种方式:
+- 实现javax.servlet.Servlet接口;
+- 继承javax.servlet.GenericServlet类;
+- 继承javax.servlet.http.HttpServlet类;
+- 通常我们会去继承HttpServlet类来完成我们的Servlet
+- GenericServlet
+- GenericServlet概述
+
+- GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet。下面是GenericServlet类的源代码:
+- GenericServlet.java
+public abstract class GenericServlet implements Servlet, ServletConfig,
+ java.io.Serializable {
+ private static final long serialVersionUID = 1L;
+ private transient ServletConfig config;
+ public GenericServlet() {}
+ @Override
+ public void destroy() {}
+ @Override
+ public String getInitParameter(String name) {
+ return getServletConfig().getInitParameter(name);
+ }
+ @Override
+ public Enumeration getInitParameterNames() {
+ return getServletConfig().getInitParameterNames();
+ }
+ @Override
+ public ServletConfig getServletConfig() {
+ return config;
+ }
+ @Override
+ public ServletContext getServletContext() {
+ return getServletConfig().getServletContext();
+ }
+ @Override
+ public String getServletInfo() {
+ return "";
+ }
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ this.config = config;
+ this.init();
+ }
+public void init() throws ServletException {}
+这个init方法是为了拓展init而设置的,子类可以重写这个无参数的init方法
+ public void log(String msg) {
+ getServletContext().log(getServletName() + ": " + msg);
+ }
+ public void log(String message, Throwable t) {
+ getServletContext().log(getServletName() + ": " + message, t);
+ }
+ @Override
+ public abstract void service(ServletRequest req, ServletResponse res)
+ throws ServletException, IOException;
+ @Override
+ public String getServletName() {
+ return config.getServletName();
+ }
+}
+
+- GenericServlet的init()方法
+- 在GenericServlet中,定义了一个ServletConfig实例变量,并在init(ServletConfig)方法中把参数ServletConfig赋给了实例变量。然后在该类的很多方法中使用了实例变量config。
+- 如果子类覆盖了GenericServlet的init(StringConfig)方法,那么this.config=config这一条语句就会被覆盖了,也就是说GenericServlet的实例变量config的值为null,那么所有依赖config的方法都不能使用了。如果真的希望完成一些初始化操作,那么去覆盖GenericServlet提供的init()方法,它是没有参数的init()方法,它会在init(ServletConfig)方法中被调用。
+- 实现了ServletConfig接口
+- GenericServlet还实现了ServletConfig接口,所以可以直接调用getInitParameter()、getServletContext()等ServletConfig的方法。
+
+- HttpServlet
+- HttpServlet概述
+- HttpServlet类是GenericServlet的子类,它提供了对HTTP请求的特殊支持,所以通常我们都会通过继承HttpServlet来完成自定义的Servlet。
+- HttpServlet覆盖了service()方法
+- HttpServlet类中提供了service(HttpServletRequest,HttpServletResponse)方法,这个方法是HttpServlet自己的方法,不是从Servlet继承来的。在HttpServlet的service(ServletRequest,ServletResponse)方法中会把ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse,然后调用
+- service(HttpServletRequest,HttpServletResponse)方法,这说明子类可以去覆盖service(HttpServletRequest,HttpServletResponse)方法即可,这就不用自己去强转请求和响应对象了。
+- 其实子类也不用去覆盖service(HttpServletRequest,HttpServletResponse)方法,因为HttpServlet还要做另一步简化操作,下面会介绍。
+
+- HttpServlet.java
+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 class BServlet extends HttpServlet {
+ public void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ System.out.println("hello doPost()...");
+ }
+}
+
+- Servlet细节
+- 不要在Servlet中创建成员!创建局部变量即可!
+- 可以创建无状态成员!
+- 可以创建有状态的成员,但状态必须为只读的!(只有get,没有set)
+- Servlet与线程安全
+- 因为一个类型的Servlet只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求,那么Servlet是否为线程安全的呢?答案是:“不是线程安全的”。这说明Servlet的工作效率很高,但也存在线程安全问题!
+- 所以我们不应该在Servlet中创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。
+
+- 让服务器在启动时就创建Servlet(很少这么做)
+- 默认情况下,服务器会在某个Servlet第一次收到请求时创建它。也可以在web.xml中对Servlet进行配置,使服务器启动时就创建Servlet。
+
+ hello1
+ cn.itcast.servlet.Hello1Servlet
+ 0
+
+
+ hello1
+ /hello1
+
+
+ hello2
+ cn.itcast.servlet.Hello2Servlet
+ 1
+
+
+ hello2
+ /hello2
+
+
+ hello3
+ cn.itcast.servlet.Hello3Servlet
+ 2
+
+
+ hello3
+ /hello3
+
+
+- 在元素中配置元素可以让服务器在启动时就创建该Servlet,其中元素的值必须是大于等于的整数,它的使用是服务器启动时创建Servlet的顺序。上例中,根据的值可以得知服务器创建Servlet的顺序为Hello1Servlet、Hello2Servlet、Hello3Servlet。
+-
+- 是的子元素,用来指定Servlet的访问路径,即URL。
+- 它必须是以“/”开头!
+1)- 可以在中给出多个,例如:
+
+ AServlet
+ /AServlet
+ /BServlet
+
+
+- 那么这说明一个Servlet绑定了两个URL,无论访问/AServlet还是/BServlet,访问的都是AServlet。
+
+2)- 还可以在中使用通配符,所谓通配符就是星号“*”,星号可以匹配任何URL前缀或后缀,使用通配符可以命名一个Servlet绑定一组URL,例如:
+
+```
+/servlet/*:/servlet/a、/servlet/b,都匹配/servlet/*;
+```
+
+- *.do:/abc/def/ghi.do、/a.do,都匹配*.do;
+
+```
+/*:匹配所有URL;
+```
+
+
+
+```
+请注意,通配符要么为前缀,要么为后缀,不能出现在URL中间位置,也不能只有通配符。例如:/*.do就是错误的,因为星号出现在URL的中间位置上了。*.*也是不对的,因为一个URL中最多只能出现一个通配符。
+```
+
+- 注意,通配符是一种模糊匹配URL的方式,如果存在更具体的,那么访问路径会去匹配具体的。例如:
+
+ hello1
+ cn.itcast.servlet.Hello1Servlet
+
+
+ hello1
+ /servlet/hello1
+
+
+ hello2
+ cn.itcast.servlet.Hello2Servlet
+
+
+ hello2
+ /servlet/*
+
+
+- 当访问路径为http://localhost:8080/hello/servlet/hello1时,因为访问路径即匹配hello1的,又匹配hello2的,但因为hello1的中没有通配符,所以优先匹配,即设置hello1。
+- web.xml文件的继承
+- 在${CATALINA_HOME}\conf\web.xml中的内容,相当于写到了每个项目的web.xml中,它是所有web.xml的父文件。
+- 每个完整的JavaWeb应用中都需要有web.xml,但我们不知道所有的web.xml文件都有一个共同的父文件,它在Tomcat的conf/web.xml路径。
+- conf/web.xml
+
+
+
+
+ default
+ org.apache.catalina.servlets.DefaultServlet
+
+ debug
+ 0
+
+
+ listings
+ false
+
+ 1
+
+
+
+ jsp
+ org.apache.jasper.servlet.JspServlet
+
+ fork
+ false
+
+
+ xpoweredBy
+ false
+
+ 3
+
+
+
+ default
+ /
+
+
+
+ jsp
+ *.jsp
+ *.jspx
+
+
+
+ 30
+
+
+
+
+ bmp
+ image/bmp
+
+
+ htm
+ text/html
+
+
+
+ index.html
+ index.htm
+ index.jsp
+
+
+
+- ServletContext(存取数据,获取资源)
+- 一个项目只有一个ServletContext对象!
+- 我们可以在N多个Servlet中来获取这个唯一的对象,使用它可以给多个Servlet传递数据!
+- 这个对象在Tomcat启动时就创建,在Tomcat关闭时才会死去!
+- ServletContext概述
+- 服务器会为每个应用创建一个ServletContext对象:
+- ServletContext对象的创建是在服务器启动时完成的;
+- ServletContext对象的销毁是在服务器关闭时完成的。
+
+- ServletContext对象的作用是在整个Web应用的动态资源之间共享数据!例如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值,这就是共享数据了。
+
+- 获取ServletContext
+- ServletConfig#getServletContext();
+- GenericServlet#getServletContext();
+- HttpSession#getServletContext()
+- ServletContextEvent#getServletContext()
+
+
+- 在Servlet中获取ServletContext对象:
+- 在void init(ServletConfig config)中:ServletContext context = config.getServletContext();,ServletConfig类的getServletContext()方法可以用来获取ServletContext对象;
+- 在GenericeServlet或HttpServlet中获取ServletContext对象:
+- GenericServlet类有getServletContext()方法,所以可以直接使用
+- this.getServletContext()来获取;
+-
+public class MyServlet implements Servlet {
+public void init(ServletConfig config) {
+ ServletContext context = config.getServletContext();
+}
+…
+}
+public class MyServlet extends HttpServlet {
+public void doGet(HttpServletRequest request, HttpServletResponse response) {
+ ServletContext context = this.getServletContext();
+}
+}
+- 域对象的功能
+- ServletContext是JavaWeb四大域对象之一:
+- PageContext;
+- ServletRequest;
+- HttpSession;
+- ServletContext;
+
+- 所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据,下面是ServletContext对象用来操作数据的方法:
+- void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“xxx”, “XXX”),在ServletContext中保存了一个域属性,域属性名称为xxx,域属性的值为XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;
+- Object getAttribute(String name):用来获取ServletContext中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)servletContext.getAttribute(“xxx”);,获取名为xxx的域属性;
+- void removeAttribute(String name):用来移除ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
+- Enumeration getAttributeNames():获取所有域属性的名称;
+- 获取应用初始化参数
+- Servlet也可以获取初始化参数,但它是局部的参数;也就是说,一个Servlet只能获取自己的初始化参数,不能获取别人的,即初始化参数只为一个Servlet准备!
+- 可以配置公共的初始化参数,为所有Servlet而用!这需要使用ServletContext才能使用!
+- 还可以使用ServletContext来获取在web.xml文件中配置的应用初始化参数!注意,应用初始化参数与Servlet初始化参数不同:
+- web.xml
+
+ ...
+ (为ServletContext设置的公共初始化参数)
+ paramName1
+ paramValue1
+
+
+ paramName2
+ paramValue2
+
+
+ ServletContext context = this.getServletContext();
+ String value1 = context.getInitParameter("paramName1");
+ String value2 = context.getInitParameter("paramName2");
+ System.out.println(value1 + ", " + value2);
+
+ Enumeration names = context.getInitParameterNames();
+ while(names.hasMoreElements()) {
+ System.out.println(names.nextElement());
+ }
+
+# 5.5 请求&响应
+
+- 服务器每次收到请求时,都会为这个请求开辟一个新的线程。
+- Response
+- 1、概述
+- response是Servlet#service方法的一个参数,类型为
+- javax.servlet.http.HttpServletResponse。在客户端发出每个请求时,服务器都会创建一个response对象,并传入给Servlet.service()方法。response对象是用来对客户端进行响应的,这说明在service()方法中使用response对象可以完成对客户端的响应工作。
+- response对象的功能分为以下四种:
+- 设置响应头信息;
+- 发送状态码;
+- 设置响应正文;
+- 重定向。
+- 2、响应正文
+- response是响应对象,向客户端输出响应正文(响应体)可以使用response的响应流,repsonse一共提供了两个响应流对象:
+- PrintWriter out = response.getWriter():获取字符流;
+- ServletOutputStream out = response.getOutputStream():获取字节流;
+- 当然,如果响应正文内容为字符(html),那么使用response.getWriter(),如果响应内容是字节(图片等),例如下载时,那么可以使用response.getOutputStream()
+- (ServletOutputStream)。
+- 注意,在一个请求中,不能同时使用这两个流!也就是说,要么你使用repsonse.getWriter(),要么使用response.getOutputStream(),但不能同时使用这两个流。不然会抛出IllegalStateException异常。
+- 字符响应流
+- 字符编码
+- 在使用response.getWriter()时需要注意默认字符编码为ISO-8859-1,如果希望设置字符流的字符编码为utf-8,可以使用response.setCharaceterEncoding(“utf-8”)来设置。这样可以保证输出给客户端的字符都是使用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()方法来手动刷新缓冲区;
+- 字节响应流
+- 将一张图片转为字节流写入到response中
+
+- 读取图片,使用commons-io包的方法。
+- byte[] image = IOUtils.toByteArray(new FileInputStream(this.getServletContext().getRealPath("/images/cat.jpeg")));
+- response.getOutputStream().write(image);
+
+- 3、设置响应头信息
+- 响应头是键值对
+- 可以使用response对象的setHeader()方法来设置响应头!使用该方法设置的响应头最终会发送给客户端浏览器!
+- response.setHeader(“content-type”, “text/html;charset=utf-8”):设置content-type响应头,该头的作用是告诉浏览器响应内容为html类型,编码为utf-8。而且同时会设置response的字符流编码为utf-8,即response.setCharaceterEncoding(“utf-8”);
+- response.setHeader("Refresh","5; URL=http://www.itcast.cn"):5秒后自动跳转到传智主页。
+
+- 4、设置状态码及其他方法
+- response.setContentType("text/html;charset=utf-8"):等同\于调用
+- response.setHeader(“content-type”, “text/html;charset=utf-8”);
+- response.setCharacterEncoding(“utf-8”):设置字符响应流的字符编码为utf-8;
+- response.setStatus(200):设置状态码;
+- response.sendError(404, “您要查找的资源不存在”):当发送错误状态码时,Tomcat会跳转到固定的错误页面去,但可以显示错误信息。
+- response.sendError(状态码)
+- 5、重定向
+ - 1)什么是重定向
+- 当你访问http://www.sun.com时,你会发现浏览器地址栏中的URL会变成http://www.oracle.com/us/sun/index.htm,这就是重定向了。
+- 重定向是服务器通知浏览器去访问另一个地址,即再发出另一个请求。
+
+ - 2)完成重定向
+- 响应码为200表示响应成功,而响应码为302表示重定向。所以完成重定向的第一步就是设置响应码为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");
+ }
+}
+
+- 上面代码的作用是:当访问AServlet后,会通知浏览器重定向到传智主页。客户端浏览器解析到响应码为302后,就知道服务器让它重定向,所以它会马上获取响应头Location,然发出第二个请求。
+
+ - 3)便捷的重定向方式sendRedirect
+public class AServlet extends HttpServlet {
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.sendRedirect("http://www.itcast.cn");
+ }
+}
+- response.sendRedirect()方法会设置响应头为302,以设置Location响应头。
+- 如果要重定向的URL是在同一个服务器内,那么可以使用相对路径,例如:
+public class AServlet extends HttpServlet {
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.sendRedirect("/hello/BServlet");
+ 注意是/项目名/路径(请求URI)
+ }
+}
+- 重定向的URL地址为:http://localhost:8080/hello/BServlet
+ - 4)重定向小结
+- 重定向是两次请求;
+- 重定向的URL可以是其他应用,不局限于当前应用;
+- 重定向的响应头为302,并且必须要有Location响应头;
+- 重定向就不要再使用response.getWriter()或response.getOutputStream()输出数据,不然可能会出现异常;
+-
+
+- Request
+- 1、概述
+- request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。
+
+- request的功能可以分为以下几种:
+- 封装了请求头数据;
+- 封装了请求正文数据,如果是GET请求,那么就没有正文;
+- request是一个域对象,可以把它当成Map来添加获取数据;
+- request提供了请求转发和请求包含功能。
+- 2、域方法
+- request是域对象!在JavaWeb中一共四个域对象,其中ServletContext就是域对象,它在整个应用中只创建一个ServletContext对象。request其中一个,request可以在一个请求中共享数据。
+- 一个请求会创建一个request对象,如果在一个请求中经历了多个Servlet,那么多个Servlet就可以使用request来共享数据。
+- 下面是request的域方法:
+- void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“xxx”, “XXX”),在request中保存了一个域属性,域属性名称为xxx,域属性的值为XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;
+- Object getAttribute(String name):用来获取request中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)request.getAttribute(“xxx”);,获取名为xxx的域属性;
+- void removeAttribute(String name):用来移除request中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
+- Enumeration getAttributeNames():获取所有域属性的名称;
+
+- 3、获取请求头数据
+- request与请求头相关的方法有:
+- String getHeader(String name):获取指定名称的请求头;
+- Enumeration getHeaderNames():获取多值头;
+- int getIntHeader(String name):获取值为int类型的请求头。
+- long getDateHeader(String name):获取值为long类型的请求头
+
+- 4、获取请求相关的其它方法
+- request中还提供了与请求相关的其他方法,有些方法是为了我们更加便捷的方法请求头数据而设计,有些是与请求URL相关的方法。
+- int getContentLength():获取请求体的字节数,GET请求没有请求体,没有请求体返回-1;
+- String getContentType():获取请求类型,如果请求是GET,那么这个方法返回null;如果是POST请求,那么默认为application/x-www-form-urlencoded,表示请求体内容使用了URL编码;
+- String getMethod():返回请求方法,例如:GET/POST
+- Locale getLocale():返回当前客户端浏览器的Locale。java.util.Locale表示国家和言语,这个东西在国际化中很有用;
+- String getCharacterEncoding():获取请求编码,如果没有setCharacterEncoding(),那么返回null,表示使用ISO-8859-1编码;
+- void setCharacterEncoding(String code):设置请求编码,只对请求体有效!注意,对于GET而言,没有请求体!!!所以此方法只能对POST请求中的参数有效!
+- String getContextPath():返回上下文路径(/项目名),例如:/hello
+- String getQueryString():返回请求URL中的参数,例如:name=zhangSan
+- String getRequestURI():返回请求URI路径,例如:/hello/oneServlet
+- StringBuffer getRequestURL():返回请求URL路径,例如:
+- http://localhost/hello/oneServlet,即返回除了参数以外的路径信息;
+- String getServletPath():返回Servlet路径,例如:/oneServlet
+- String getRemoteAddr():返回当前客户端的IP地址;
+- String getRemoteHost():返回当前客户端的主机名,但这个方法的实现还是获取IP地址;
+- String getScheme():返回请求协议,例如:http;
+- String getServerName():返回主机名,例如:localhost
+- int getServerPort():返回服务器端口号,例如:8080
+
+- 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());
+- =========================================================================
+- http://localhost:8080/Request&Response/RequestHeaderServlet?username=sxj&password=sxj
+- ==========================================================================
+- 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
+- 整个访问的路径就等于URL+QueryString
+- URL=协议名+主机名+端口号+URI
+- 案例:request.getRemoteAddr():封IP
+- 可以使用request.getRemoteAddr()方法获取客户端的IP地址,然后判断IP是否为禁用IP。
+ String ip = request.getRemoteAddr();
+ System.out.println(ip);
+ if(ip.equals("127.0.0.1")) {
+ response.getWriter().print("您的IP已被禁止!");
+ } else {
+ response.getWriter().print("Hello!");
+ }
+- 案例:防盗链
+- 可以使用request.getAttribute(“Referer”)如果不是当前页面,那么属于盗链,则跳转到当前页面
+- 如果是从地址栏直接输入URL,那么Referee返回的是null
+- String referer = request.getHeader("Referer");
+- System.out.println(referer);
+- if(referer == null || !referer.contains("localhost")){
+- response.sendRedirect("http://www.baidu.com");
+- }else{
+- response.getWriter().print("hello");//如果是从指定页面跳转来的
+- }
+
+- 5、获取请求参数
+- 最为常见的客户端传递参数方式有两种:
+- 浏览器地址栏直接输入:一定是GET请求;
+- 超链接:一定是GET请求;
+- 表单:可以是GET,也可以是POST,这取决与
+
+
+- 下面是使用request获取请求参数的API:
+- String getParameter(String name):通过指定名称获取参数值;
+
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ String v1 = request.getParameter("p1");
+ String v2 = request.getParameter("p2");
+ System.out.println("p1=" + v1);
+ System.out.println("p2=" + v2);
+ }
+
+ public void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ String v1 = request.getParameter("p1");
+ String v2 = request.getParameter("p2");
+ System.out.println("p1=" + v1);
+ System.out.println("p2=" + v2);
+ }
+
+- String[] getParameterValues(String name):当多个参数名称相同时,可以使用方法来获取;
+超链接
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ String[] names = request.getParameterValues("name");
+ System.out.println(Arrays.toString(names));
+ }
+- Enumeration getParameterNames():获取所有参数的名字;
+
+ public void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ Enumeration names = request.getParameterNames();
+ while(names.hasMoreElements()) {
+ System.out.println(names.nextElement());
+ }
+ }
+
+- Map getParameterMap():获取所有参数封装到Map中,其中key为参数名,value为参数值,因为一个参数名称可能有多个值,所以参数值是String[],而不是String。
+超链接
+ Map paramMap = request.getParameterMap();
+ for(String name : paramMap.keySet()) {
+ String[] values = paramMap.get(name);
+ System.out.println(name + ": " + Arrays.toString(values));
+ }
+p2: [v2, vv2]
+p1: [v1, vv1]
+
+- 示例:
+
+-
+- 点击这里,测试GET请求
+-
+-
+
+- //遍历Map
+
+```
+public class RequestParameterServlet extends HttpServlet {
+```
+
+
+```
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+```
+
+- Enumeration names = request.getParameterNames();
+- while(names.hasMoreElements()){
+- String name = names.nextElement();
+- System.out.println(name+"="+request.getParameter(name));
+- }
+- }
+
+-
+
+```
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+```
+
+- Map map = request.getParameterMap();
+- for(Entry entry:map.entrySet()){
+- System.out.println(entry.getKey()+"="+Arrays.toString(entry.getValue()));
+- }
+-
+- }
+- }
+
+- username=sxj
+- password=sxj
+- username=[23]
+- password=[123]
+- hobby=[eat, sleep]
+- String username = request.getParameter("username");
+- String password = request.getParameter("password");
+- String [] hobbies = request.getParameterValues("hobby");
+- System.out.println("username:"+username);
+- System.out.println("password:"+password);
+- System.out.println("hobbies:"+Arrays.toString(hobbies));
+- 6、请求转发和请求包含
+- 无论是请求转发还是请求包含,都表示由多个Servlet共同来处理一个请求。例如Servlet1来处理请求,然后Servlet1又转发给Servlet2来继续处理这个请求。
+
+ - 1)请求转发(常用)
+- 在AServlet中,把请求转发到BServlet:
+- 参数是Servlet路径(servlet-mapping中的url-pattern)
+- 相当于/项目名
+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);
+ }
+}
+public class BServlet extends HttpServlet {
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ System.out.println("BServlet");
+ }
+}
+Aservlet
+BServlet
+- 如果转发了,那么在原本请求的Servlet中设置的响应体是失效的。
+- 如果转发前,在原本请求的Servlet中设置的响应体超过24K,那么响应体仍有效(缓冲区溢出)。
+ - 2)请求包含(不常用)
+- 在AServlet中,把请求包含到BServlet:
+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.include(request, response);
+ }
+}
+public class BServlet extends HttpServlet {
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ System.out.println("BServlet");
+ }
+}
+Aservlet
+BServlet
+
+ - 3)请求转发与请求包含比较
+- 如果在AServlet中请求转发到BServlet,那么在AServlet中就不允许再输出响应体,即不能再使用response.getWriter()和response.getOutputStream()向客户端输出,这一工作应该由BServlet来完成;如果是使用请求包含,那么没有这个限制;
+- 请求转发虽然不能输出响应体,但还是可以设置响应头的,例如:response.setContentType(”text/html;charset=utf-8”);
+- 请求包含大多是应用在JSP页面中,完成多页面的合并;
+- 请求转发大多是应用在Servlet中,转发目标大多是JSP页面;
+
+ - 4)请求转发与重定向比较
+ - 1)请求转发是一个请求,而重定向是两个请求;
+- 请求转发后浏览器地址栏不会有变化,而重定向会有变化,因为重定向是两个请求;
+ - 2)请求转发的目标只能是本应用中的资源(给出Servlet路径),重定向的目标可以是其他应用(请求URI或者其他路径);
+ - 3)请求转发对AServlet和BServlet的请求方法是相同的,即要么都是GET,要么都是POST,因为请求转发是一个请求;
+- 重定向的第二个请求一定是GET;
+ - 4)请求转发效率更高;当需要地址栏发生变化时,需要使用重定向;需要在下一个Servlet中获取之前Servlet设置的域,需要使用转发
+-
+
+# Filter
+- 什么是过滤器
+- 过滤器JavaWeb三大组件之一,它与Servlet很相似!不过过滤器是用来拦截请求的,而不是处理请求的。
+- 当用户请求某个Servlet时,会先执行部署在这个请求上的Filter,如果Filter“放行”,那么会继承执行用户请求的Servlet;如果Filter不“放行”,那么就不会执行用户请求的Servlet。
+- 其实可以这样理解,当用户请求某个Servlet时,Tomcat会去执行注册在这个请求上的Filter,然后是否“放行”由Filter来决定。可以理解为,Filter来决定是否调用Servlet!当执行完成Servlet的代码后,还会执行Filter后面的代码。
+
+- 过滤器之hello world
+- 其实过滤器与Servlet很相似,我们回忆一下如果写的第一个Servlet应用!写一个类,实现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("Hello Filter");
+ }
+ public void destroy() {}
+}
+
+- 第二步也与Servlet一样,在web.xml文件中部署Filter:
+
+ helloFilter
+ cn.itcast.filter.HelloFilter
+
+
+ helloFilter
+ /*(/*表示拦截所有的访问请求)
+
+
+- 当用户访问index.jsp页面时,会执行HelloFilter的doFilter()方法!在我们的示例中,index.jsp页面是不会被执行的,如果想执行index.jsp页面,那么我们需要放行!
+ 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...");
+ }
+
+
+- 有很多同学总是错误的认为,一个请求在给客户端输出之后就算是结束了,这是不对的!其实很多事情都需要在给客户端响应之后才能完成!
+
+-
+
+- 过滤器的生命周期
+- init(FilterConfig):在服务器启动时会创建Filter实例,并且每个类型的Filter只创建一个实例,从此不再创建!在创建完Filter实例后,会马上调用init()方法完成初始化工作,这个方法只会被执行一次;
+- doFilter(ServletRequest req,ServletResponse res,FilterChain chain):这个方法会在用户每次访问“目标资源(pattern>index.jsp)”时执行,如果需要“放行”,那么需要调用FilterChain的doFilter(ServletRequest,ServletResponse)方法,如果不调用FilterChain的doFilter()方法,那么目标资源将无法执行;
+- destroy():服务器会在创建Filter对象之后,把Filter放到缓存中一直使用,通常不会销毁它。一般会在服务器关闭时销毁Filter对象,在销毁Filter对象之前,服务器会调用Filter对象的destory()方法。
+- 单例,同Servlet一致
+- FilterConfig
+- Filter接口中的init()方法的参数类型为FilterConfig类型。它的功能与ServletConfig相似,与web.xml文件中的配置信息对应。下面是FilterConfig的功能介绍:
+- ServletContext getServletContext():获取ServletContext的方法;
+- String getFilterName():获取Filter的配置名称;与元素对应;
+- String getInitParameter(String name):获取Filter的初始化配置,与元素对应;
+- Enumeration getInitParameterNames():获取所有初始化参数的名称。
+
+
+- FilterChain
+- doFilter()方法的参数中有一个类型为FilterChain的参数,它只有一个方法:doFilter(ServletRequest,ServletResponse)。
+- 前面我们说doFilter()方法的放行,让请求流访问目标资源!但这么说不严谨,其实调用该方法的意思是,“我(当前Filter)”放行了,但不代表其他人(其他过滤器)也放行。
+- 如果当前过滤器是最后一个过滤器,那么调用chain.doFilter()方法表示执行目标资源,而不是最后一个过滤器,那么chain.doFilter()表示执行下一个过滤器的doFilter()方法。
+- 多个过滤器执行顺序
+- 一个目标资源可以指定多个过滤器,过滤器的执行顺序是在web.xml文件中的部署顺序:
+
+ myFilter1
+ cn.itcast.filter.MyFilter1
+
+
+ myFilter1
+ /index.jsp
+
+
+ myFilter2
+ cn.itcast.filter.MyFilter2
+
+
+ myFilter2
+ /index.jsp
+
+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);//放行,执行MyFilter2的doFilter()方法
+ 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...");
+ }
+}
+
+ This is my JSP page.
+ index.jsp
+ <%System.out.println("index.jsp"); %>
+
+
+- 当有用户访问index.jsp页面时,输出结果如下:
+filter1 start...
+filter2 start...
+index.jsp
+filter2 end...
+filter1 end...
+- AFilter B Filter CFilter
+- 有点像是递归,调用filterchain的doFilter方法会放行给下一个过滤器,这个doFilter前面的代码的执行顺序是A-B-C,而后面的代码的执行顺序是C-B-A
+
+- 四种拦截方式
+- 我们来做个测试,写一个过滤器,指定过滤的资源为b.jsp,然后我们在浏览器中直接访问b.jsp,你会发现过滤器执行了!
+- 但是,当我们在a.jsp中request.getRequestDispathcer(“/b.jsp”).forward(request,response)时,就不会再执行过滤器了!也就是说,默认情况下,只能直接访问目标资源才会执行过滤器,而forward执行目标资源,不会执行过滤器!
+public class MyFilter extends HttpFilter {
+ public void doFilter(HttpServletRequest request,
+ HttpServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ System.out.println("myfilter...");
+ chain.doFilter(request, response);
+ }
+}
+
+ myfilter
+ cn.itcast.filter.MyFilter
+
+
+ myfilter
+ /b.jsp
+
+
+ b.jsp
+
+ a.jsp
+ <%
+ request.getRequestDispatcher("/b.jsp").forward(request, response);
+ %>
+