1. Servlet简介

Servlet服务器端的重要组件,直译为服务的小程序,属于动态资源用来处理请求,服务器接收到请求后会调用Servlet处理请求

Servlet的主要作用

 接收请求

 处理请求

完成响应

例如

当我们要完成一个登录功能时用户会将输入的用户名和密码POST请求的形式发送到服务器,但是服务器本身并不具有能力来读取用户发送的用户名和密码也就不可能对用户名和密码进行验证,所以当服务器收到这类请求需要将请求转一个Servlet处理。

 Servlet:
           狭义上的Servlet:仅仅指的是这个servlet接口
           广义的Servlet:不仅包含该Servlet接口,还包含该Servlet的接口的子类!
        
         Servlet是JavaWeb部分的三大组件:
           JavaWeb部分三大组件:Servlet、Filter、Listener,这三个组件都需要交给web服务器管理!

2. HelloServlet

步骤

a) 创建动态WEB项目WEB_Servlet

b) 项目下创建包com.atguigu.web.servlet

c) 下创建一个类HelloServlet实现javax.servlet.Servlet接口

d) HelloServletservice()方法加入一行打印语句System.out.println(“hello”);

e) WEB-INF目录下的web.xml文件中注册映射Servlet

<servlet>

<servlet-name>HelloServlet</servlet-name>

<servlet-class>com.atguigu.web.servlet.HelloServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>HelloServlet</servlet-name>

<url-pattern>/HelloServlet</url- pattern >

</servlet-mapping>
View Code

f) 启动服务器,在浏览器中访问http://localhost:8080/WEB_Servlet/HelloServlet

具体代码

类:com.atdongruan.web.servlet.HelloServlet

public class HelloServlet implements Servlet {

@Override

public void init(ServletConfig config) throws ServletException {}

@Override

public ServletConfig getServletConfig() {return null;}

@Override

public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {System.out.println("hello");}

@Override

public String getServletInfo() {return null;}

@Override

public void destroy() {}

}

web.xml配置文件

<?xmversion="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">

<servlet>

<servlet-name>HelloServlet</servlet-name>

<servlet-class>com.atdongruan.web.servlet.HelloServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>HelloServlet</servlet-name>

<url-pattern>/HelloServlet</url-pattern>

</servlet-mapping>

</web-app>

web.xml文件详解

  因为创建好的Servlet需要由Servlet容器调用,而Servlet容器并不能知道我们所创建的Servlet的存在,所以需要在web.xml文件中进行注册。

  <servlet></servlet>用于注册servlet

  <servlet-name>用于设置servlet的名字,在服务器中关于servlet的一配置都需要servlet-name来进行配置

  <servlet-class>用于设置servlet的全类名,用于创建servlet的实例(反射

  仅仅注册是远远不够的,因为Servlet是用来处理客户发送的请求的,所以还需要为Servlet映射一个请求地址。

  <servlet-mapping>用于映射需要servlet处理的请求地址

 <servlet-name>servlet的名字,<servlet><name>对应关系

 <url-pattern>需要servlet处理的请求地址

总结:

    自定义一个Servlet:
              1).自定义一个类实现Servlet接口,实现接口中的方法
              2).将自定义的servlet类在web.xml文件中配置一下! 

 <!-- 使用servlet标签配置我们自定义的servlet -->
                  <servlet>
                    <!-- 在servlet-name该标签中,我们可以配置一个servletName,
                                 建议和servlet类的简类名一致 ,该servletName是我们自己使用-->
                    <servlet-name>HelloServlet</servlet-name>
                    <!-- 使用servlet-class标签配置自定义servlet类的全类名,
                        这个全类名是给服务器使用的,服务器会使用这个全类名创建该类所对应的servlet对象
                     -->
                    <servlet-class>com.neuedu.servlet.HelloServlet</servlet-class>
                  </servlet>
                  
                  <!-- 使用servlet-mapping标签配置servlet映射 -->
                  <servlet-mapping>
                    <servlet-name>HelloServlet</servlet-name>
                    <!-- 该url-pattern 是用来请求映射url地址的,也就说
                        该标签中的内容是给浏览器访问使用的!
                     -->
                    <url-pattern>/Hello</url-pattern>
                  </servlet-mapping>
View Code

 

3. Servlet接口

3.1 Servlet生命周期

生命周期,以人举例的话,人的出生到死亡称为一个人的生命周期,在人的整个生命中有很多的阶段,总的来说可以大致分为三个阶段出生、工作死亡。每一阶段都有每一阶段要做的事。对于我们的Servlet也是一样。

Servlet的生命周期指的是Servlet由实例化到被销毁的过程同样也被分为三个阶段:实例化、处理请求、被销毁。而每个阶段我们都有对应的方法来实现响应的功能,在实例化阶段需要调用init()方法来做初始化操作处理请求阶段调用service()方法处理请求,销毁对象之前调用destroy()释放资源等操作。

Servlet生命周期相关方法

          1)构造器:servlet对象不是在tomcat服务器启动的时候创建的,而是在我们第一次
                      访问该servlet的时候调用,该构造器只会被执行一次,说明该Servlet是单例的!
                      但是是多线程的!
                      
             2)init方法:在我们第一次访问该Servlet时候调用【在构造器被调用之后紧接着被调用】
                        该方法只会被调用一次
                        
            
             3)service方法:每次我们访问该Servlet的时候,这个service方法都会被调用一次!
                          所以我们写的业务逻辑代码,主要就写在这个方法中!
            
             4)destroy方法:该方法会在项目被卸载的时候【服务器关闭】会被调用一次!

3.1.1 Servlet初始化

服务器会在Servlet第一次处理请求、或服务器启动时创建Servlet实例默认是在第一处理请求时进行实例化的。

对于每个Servlet,服务器只会创建一个Servlet实例以我们HelloServlet为例,当我们通过浏览器访问http://localhost:8080/WEB_Servlet/HelloServlet服务器会根据路径/HelloServlet从配置文件中找到url-pattern/HelloServletservlet-mapping然后servlet-mapping找到servlet-nameHelloServlet。接下来找到servlet-nameHelloServlet<servlet>。最后获得servlet的全类名,通过全类名创建类的实例这个实例会放到一个集合中这一过程后容器再也不会对该servlet实例化操作而是直接从集合中获取servlet实例处理请求。

Servlet实例化后会立即调用public void init(ServletConfig config)方法。这里主要做一些获取配置信息在处理请求前需要做的准备工作

init()方法Servlet的整个生命周期中只会被调用一次。

init(ServletConfig config)方法被调用时,容器传递一个ServletConfig对象作为参数,该对象可以获取Servlet相关的配置信息。

4. Servlet相关接口

ServletRequestServletResponse

  ServletRequest由容器创建并传递到service()方法中,容器所创建的对象实际上是  HttpServletRequest,所以开发中我们都会ServletRequest强转HttpServletRequest

  HttpServletRequest方法

  String getParameter(String paramName):获取指定请求参数的值;

  String getMethod():获取请求方法,例如GETPOST

  String getHeader(String name):获取指定请求头的值;

 void setCharacterEncoding(String encoding):设置请求体的编码!因为GET请求没有请求体,所以这个方法只只对POST请求有效。当调用request.setCharacterEncoding(“utf-8”)之后,再通过getParameter()方法获取参数值时,那么参数值都已经通过了转码,即转换成了UTF-8编码。所以,这个方法必须在调用getParameter()方法之前调用!

  HttpServletResponse的方法

  PrintWriter getWriter():获取字符响应流,使用该流可以向客户端输出响应信息。例如response.getWriter().print(“<h1>Hello JavaWeb!</h1>”)

  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.atguigu.com”),表示3秒后自动刷新到http:// www.atguigu.com

  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):向客户端发送状态码,以及错误消息。例如给客户端发送404response(404, “您要查找的资源不存在!”)

 

ServletConfig

  ServletConfig对象对应web.xml文件中的一个<servlet>元素,例如:想要获取元素中的<servlet-name>那么可以使用servletConfig.getServletName()方法来获取。

  ServletConfig对象同样有容器创建,然后作为参数传递init()方法,可以在init()方法中使用。

  ServletConfig的方法:

  String getServletName():获取Servletweb.xml文件中的配置名称,即<servlet-name>指定的名称;

   ServletContext getServletContext():用来获取ServletContext对象,ServletContext会在后面讲解;

  String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;

  Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称;

  web.xmlservlet配置初始化参数

<servlet>

<servlet-name>HelloServlet</servlet-name>

<servlet-class>省略</servlet-class>

<init-param>

<param-name>username</param-name>

<param-value>root</param-value>

</init-param>

</servlet>

  每一个init-param表示一个初始化参数,通过getInitParameter(String name)可以根据param-name的值获取到param-value值。

  每个servlet中可以配置多个init-paramservlet只能获取自身的初始化参数,而不能获得其他servlet的初始化参数。

 总结:

  代表:代表当前servet的配置信息,每个servlet对应唯一的一个ServletConfig对象
                 <servlet>
                    <servlet-name>BServlet</servlet-name>
                    <servlet-class>com.neuedu.servlet.BServlet</servlet-class>
                  </servlet>
            获取:该是由Tomcat服务器创建,然后通过参数的形式传递给init方法,
                   我们只需要在init方法中使用就可以!
            功能:
              1)获取当前servlet的名称[getServletName()]
              2)获取当前servlet的初始化参数[getInitParameter("username")]
              3)获取Servletcontext[getServletContext()]

ServletContext:
               代表:ServletContext代表的是当前web应用!
               获取 :通过ServletConfig对象获取[getServletContext()]
               功能:
                  1)获取整个web应用的初始化参数【getServletContext()】
                  2)可以做为javaweb部分的四大域对象之一使用
                  3)可以获取当前项目的名称【getContextPath()】
                  4)可以获取文件的绝对路径[getRealPath("/index.html")]

注:   
        路径:
           虚拟路径:http://localhost:8080/servlet01/BServlet
           真实路径:是指的在本地磁盘上的具体路径(详见8.6)

Throwable
            算术异常类:ArithmeticExecption
            空指针异常类:NullPointerException
            类型强制转换异常:ClassCastException
            数组下标越界异常:ArrayIndexOutOfBoundsException
            文件未找到异常:FileNotFoundException
            操作数据库异常:SQLException

5. GenericServlet

在显示开发中我们如果直接实现servlet接口功能也是可以正常实现的,但是所面临的问题是如果我直接实现Servlet接口,那接口中的所有方法都必须要实现,但是这些方法有的我们用不到,而有的方法实现起来很麻烦而且没给servlet实现的代码都差不多于是我们就需要一个抽象类来帮助我们实现servlet接口,实现一些通用的方法,而我们只需要继承GenericServlet就可以了,这样的好处是我们不在需要重写全部方法而只需要重写必须的和我们需要的方法就可以

代码:

 

public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable{

    private transient ServletConfig config;

    public GenericServlet() { }

    public void destroy() {}

    public String getInitParameter(String name) {

return getServletConfig().getInitParameter(name);

    }

    public Enumeration getInitParameterNames() {

return getServletConfig().getInitParameterNames();

    }   

    public ServletConfig getServletConfig()  {return config;  }

    public ServletContext getServletContext() {

return getServletConfig().getServletContext();

    }

    public String getServletInfo() { return ""; }

    public void init(ServletConfig config) throws ServletException {

this.config = config;

this.init();

    }

    public void init() throws ServletException {}

    public void log(String msg) {

getServletContext().log(getServletName() + ": "+ msg);

    }

    public void log(String message, Throwable t) {

getServletContext().log(getServletName() + ": " + message, t);

    }

    public abstract void service(ServletRequest req, ServletResponse res)

throws ServletException, IOException;

    public String getServletName() {  return config.getServletName(); }

}
View Code

 

 

 

GenericServlet有两个重载的init()方法由于ServletConfig对象是在容器调用init(ServletConfig config)方法是传过来的,只能在该方法中使用所以我们在类中定义了一个类型为ServletConfig的对象而在init(ServletConfig config)方法中对该变量进行赋值。这样就会有一个问题存在,如果子类在继承父类时重写了该方法,那赋值操作将不会被调用,这时如果使用ServletConfig方法将导致空指针异常所以在这类中又定义了一个init()方法,这个方法将在init(ServletConfig config)方法中被调用,并且是在config对象赋值之后调用,初始化的操作我们可以通过重写config()方法来完成,这样做既保证ServletConfig的赋值又可以正常做初始化操作。

这种将固定的代码编写到一个方法中,而将有可能发生变化的代码交给子类编写实际是一种叫做模板模式的设计模式,而init()这种没有方法体,而被父类方法调用需要子类重写的方法称为钩子方法。

GenericServlet同时还实现了ServletConfig接口,实现这个接口的好处是我们直接通过this可以调用ServletConfig对象的方法了而这些方法实际调用的都是,成员变量config的方法。

6. HttpServlet

HttpServletGenericServlet子类,他是Tomcat中专门处理HttpServletServlet(实际Tomcat只处理Http类型的请求)所以在一般情况下我们都会通过继承HttpServlet来实现servlet

HttpServletGenericServlet一样都是一个抽象类,也就是不能对它们进行实例化,与GenericServlet不同HttpServlet没有任何抽象方法,所以需要我们根据实际需求来重写它的方法。

GenericServletservice(ServletRequest req, ServletResponse res)方法是一个抽象方法,必须要由子类实现,而在HttpServlet中已经被实现,HttpServlet实现的方法中会被强制转换成HttpServletRequestHttpServletResponse(不用担心会出现类型转换异常,因为传过来的对象本身就是该类型)转换类型之后会调用HttpServlet中的一个重载的service(HttpServletRequest req, HttpServletResponse resp)方法也就是说如果我们要重写service()方法不必再去重写GenericServletservice()方法了,而可以直接重写HttpServlet重载的service()方法就可以了。

service(HttpServletRequest req, HttpServletResponse resp)方法它的请求类型进行判断,然后根据请求类型的不同再去调用不同的方法post类型的请求回去调用doPost()get请求回去调用doGet()delete请求回去调用doDelete()以此类推。但是实际应用中我们只会使用到doPost()doGet()所以一般情况下我们不会去重写service()方法而是去重写更简单的doGet()或者doPost()

7. Servlet扩展

问题1Servlet的构造器调用了几次?

这个问题实际上很容易测试,只需要在Servlet的中写一个无参构造器,方法中写一个打印语句,然后Servlet发送请求,会发现打印语句仅仅输出了一次,由此证明构造器只调用了一次。上边我们也说过,Servlet单实例,而调用构造器就是用来创建实例的所以构造器只会被调用一次

问题2Servlet线程安全的吗?

由于Servlet是单实例的,所以当容器调用service方法处理请求时以多线程的方式调用的,但是因为性能问题所以在这方法中并没有考虑同步的问题,所以Servlet不是线程安全的,但是这样做的好处是性能较好。

由于Servlet不是线程安全的,所以尽量不要在使用Servlet处理请求时操作变量,因为有可能会出现同步的问题。(实际应用中只有非常高的并发的情况下有可能出现这个问题,虽然如此但还是尽量不要那么做)。

问题3Servlet实例只能在第一次请求时被创建吗?

一般情况Servlet会在第一次收到请求时被创建,所以当我们第一次访问某个Servlet平时慢一些这种我们称为第一次惩罚。

如果希望在服务器启动时就创建Servlet的实例可以在web.xml进行配置,在servlet标签中还有一个load-on-startup标签,这个标签需要一个大于等于0的整数作为参数,当配置了这个属性后,该Servlet将会在服务器启动时就被创建,且值越小创建的时机越早。

 

问题4url-pattern映射的规则是什么?

url-pattern配置Servlet映射的地址,他的配置规则如下:

  精确匹配

 当前项目指定URL必须完全

 /path/ServletName

  只有URLhttp://localhost:8080/项目名/path/ServletName

  此也可知/代表的项目根目录而在html/代表的是服务器根目录

  路径匹配:

  当前项目指定路径URL地址

  如:/path/*

  URL为:http://localhost:8080/项目名/path/任意值

  全匹配:

  当前项目所有的URL地址

  如:/*

  所有URL可以

  后缀匹配

  当前项目指定后缀URL地址

  *.action

  URLhttp://localhost:8080/项目名/任意值. action

优先级:

  1、精确匹配

  2、路径匹配

  3、全匹配

  4、后缀匹配

  5、还有一种“/”这种也是全部匹配,优先级最低

关于*

  “*”就是通配符匹配任意字符

  “*”只能出现前面和后面,不能出现在中间

  如:/*.action错误

   “*只能出现一次

  如:*/*错误

  “*不能单独出现

  如:* 错误

问题5web.xml文件仅仅是看到的那么简单吗?

web.xml文件整个项目中的配置文件,非常重要。但是纵观我们项目下的web.xml文件似乎内容不多,那如此重要的文件为什么只配置的这么少的内容呢?实际上在Tomcat还有一个总的web.xml文件,就在%CATALINA_HOME%/conf目录

重要配置

DefaultServlet用于处理静态资源默认Servlet

<servlet>

<servlet-name>default</servlet-name>

<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>

<init-param>

<param-name>debug</param-name>

<param-value>0</param-value>

</init-param>

<init-param>

<param-name>listings</param-name>

<param-value>false</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>default</servlet-name>

  <url-pattern>/</url-pattern>

</servlet-mapping>

JspServlet用于处理JSPServlet

<servlet>

<servlet-name>jsp</servlet-name>

<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>

<init-param>

<param-name>fork</param-name>

<param-value>false</param-value>

</init-param>

<init-param>

<param-name>xpoweredBy</param-name>

   <param-value>false</param-value>

 </init-param>

 <load-on-startup>3</load-on-startup>

</servlet>

 

<servlet-mapping>

  <servlet-name>jsp</servlet-name>

  <url-pattern>*.jsp</url-pattern>

</servlet-mapping>

 

<servlet-mapping>

  <servlet-name>jsp</servlet-name>

  <url-pattern>*.jspx</url-pattern>

</servlet-mapping>

session过期时间

<session-config>

        <session-timeout>30</session-timeout>

    </session-config>

MIME类型

<mime-mapping>

<extension>123</extension>

  <mime-type>application/vnd.lotus-1-2-3</mime-type>

 </mime-mapping>

以下省略。。。。。。

 

8. ServletContext

8.1 ServletContext简介

每个WEB应用服务器都会为其创建一个ServletContext对象,项目启动时ServletContext对象被创建,项目停止或重新加载时ServletContext对象销毁

ServletContext对象主要作用就是Servlet之间共享数据加载WEB应用的配置信息,还记得ServletConfig对象可以获取到每个Servlet的配置信息吧,我们的ServletContext可以或整个WEB应用的配置信息。

8.2 获取ServletContext

Serlet接口中,可以通过init方法中的ServletConfig调用getServletContext()方法来获得ServletContext对象。

GenericServletHttpServlet可以直接调用getServletContext()方法。(实际也是ServletConfiggetServletContext()方法

HttpSession对象getServletContext()方法同样也可以获取。

8.3 域对象

 ServletContextJavaWeb四个域对象之一

  PageContext

 ServletRequest

  HttpSession

 ServletContext

域对象主要用来存储传递数据,每个域对象的内部都有一个map用来存储对象

读取数据的方法

  void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“key”, “value”),在ServletContext中保存了一个域属性,域属性名称为key,域属性的值为value

  Object getAttribute(String name):用来获取ServletContext中的数据;

  void removeAttribute(String name):用来移除ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;

  Enumeration getAttributeNames():获取当前域中所有属性的名称;

8.4 获取当前应用的初始化参数

ServletConfig功能类似,ServletContext也可以获取初始化参数,这不过config获取的是当前Servlet的而context获得的是整个Web应用的;

由于整个应用中都拥有同一个ServletContext所以所有Servlet都能获得相同的初始化参数。

web.xml配置初始化参数标签中创建context-param元素,通过元素中param-nameparam-value标签分别配置keyvalue每一context-param代表一条键值对的初始化参数。

  <context-param>

   <param-name>username</param-name>

   <param-value>root</param-value>

  </context-param>

  <context-param>

   <param-name>password</param-name>

   <param-value>1234</param-value>

  </context-param>

</web-app>

8.5 获取项目根目录

项目根目录就是端口号后边的第一个路径。http://localhost:8080/hello/HelloServlet,红色部分/hello就是项目的根目录这样的目的是,因为在项目开发时或交付后,项目名很有可能被修改,也就是红色部分会变成其他内容,当项目根目录修改后,页面中很多资源的路径会失效,所以需要动态获取根目录。

主要通过servletContext. getContextPath()来获取

通过HttpServletRequest对象也可以获得该值。

8.6 获取资源流和真实路径(物理路径)

web项目中,我们访问服务器上的资源一般都是使用URL地址或者相对路径,但是有时我们会需要获取服务器中的文件流,或者获取文件的物理地址(比如在上传下载文件时)

获取文件流

  InputStream in = getServletContext().getResourceAsStream("/1.jpg");

  该方法会获取项目根目录1.jpg输入

获取文件真实路径

 String realPath = getServletContext().getRealPath("/1.jpg");

  该方法获得项目根目录下1.jpg的路径地址

 注意:给方法并不会判断文件是否存在

9. 请求和响应

9.1 请求和响应的过程

 

9.2 HttpServletResponse对象

HttpServletResponse对象封装了服务器响应给客户端的信息,该对象由服务器创建,在Servlet处理请求时,服务器会调用Servletservice方法并将HttpServletResponse对象作为参数传递。所以,我们可以直接在service方法中使用该对象。一般我们习惯简称他为response

response主要功能有:

  设置响应头信息

  response.setHeader("Refresh", "3;URL=http://www.baidu.com")

  response.setContentType("text/html;charset=UTF-8");

  设置状态

  response.sendError(404,"访问的资源未找到");

  设置响应体

  response.getWriter().print("<h1>Hello World</h1>");

  重定向

  response.sendRedirect("index.html");

9.3 HttpServletRequest对象

HttpServletRequest对象封装了客户端发送给服务器的信息,该对象服务器创建,在Servlet处理请求时,服务器会调用Servletservice方法并将HttpServletRequest对象作为参数传递。所以,我们可以直接在service方法中使用该对象。一般我们习惯简称他为request

request主要功能有:

  获取请求参数

  String username = request.getParameter("username");

 在请求域中读数据

  request.setAttribute("key1", "value1");

  String key1 = (String) request.getAttribute("key1");

  获取项目名

  String contextPath = request.getContextPath();

  转发请求

  request.getRequestDispatcher("/index.html").forward(request, response);

9.4 转发和重定向

9.4.1 转发

转发通过request对象发起的,通过request对象获取一个RequestDispatcher对象,通过RequestDispatcherforward方法发起转发。

转发是在服务器内部进行的

 整个过程浏览器只发送了一个请求

 浏览器不能知道转发行为的发生

 由于在服务器内部进行,所以转发以项目路径为根目录输入地址时不需要输入项目

转发是一次请求,所以request的数据可以共享

转发只能转发到应用内部的资源,而不能转发到其他应用

9.4.2 重定向

重定向是通过response对象发起的,通过responsesendRedirect()方法进行重定向

重定向是在浏览器中进行的:

  整个过程中,浏览器发送了两次请求。

  浏览器知道转发行为的发生。

  由于在浏览器端进行,重定向的路径是以服务器目录为根目录,所以输入地址时需要输入项目名。

重定向是两次请求,不能共享request中的数据。

重定向不只限定于内部资源,可以重定向到任意web资源。

总结:

 对比:
            浏览器请求次数   地址栏是否发生变化    发生在服务器端/浏览器端   浏览器能否感知
       
       转发    1                   否                    服务器端             否
       重定向  2                   是                    浏览器端             是

转发靠request实现

重写向靠response实现

9.5 路径问题

通常我们访问一个web应用地址格式如下:http://localhost:8080/MyWeb/HelloServlet

http://localhost:8080 一部分我们称它为服务器的根目录

/MyWeb 这一部分我们称它为项目的根目录

/HelloServlet 这一部分是我们Servlet的映射地址

绝对路径和相对路径

  绝对路径:使用“/ ”开头的路径称为决定路径绝对路径表示从根目录开始寻找资源。

 相对路径:不使用“ / ”开头的路径称为相对路径相对路径表示从当前资源所在目录开始寻找资源

9.5.1 服务器端路径

服务器端路径,主要指在Servlet中使用转发的路径

服务器的根目录的是项目的根目录,也就是我们的项目名。

例如,我们现在访问如下地址的Servlet

 http://localhost:8080/MyWeb/hello/HelloServlet

  HelloServlet中调用转发方法

  request.getRequestDispatcher("/index.html").forward(request, response);

路径地址处如果加了“ / ”相当于由项目根目录开始寻找资源

也就相当于将请求转发到如下地址:

  http://localhost:8080/MyWeb/index.html

  request.getRequestDispatcher("index.html").forward(request, response);

在路径地址处如果不加“/ ”相当于由当前项目所在目录开始寻找资源

也就相当于将请求转发到如下地址:

  http://localhost:8080/MyWeb/hello/index.html

在实际应用中,由于我们的资源(ServletJSP所在的位置有可能会发生变动,所以通常我们会使用绝对路径。

 

9.5.2 客户端路径

客户端路径,主要是值在页面中引用外部资源,以及在Servlet中做重定向操作时的路径。

客户端路径的根目录的是我们tomcat的服务器的根目录,也就是项目名前面那段路径

1:我们现在访问如下地址的Servlet

 http://localhost:8080/MyWeb/hello/HelloServlet

  HelloServlet中调用重定向方法

  response.sendRedirect("/index.html");

  路径地址处如果加了“ / ”相当于由项目根目录开始寻找资源

  也就相当于将请求重定向到如下地址:

http://localhost:8080/ index.html

但是这个地址明显不是我们想要的,所以重定向使用绝对路径时必须要加入项目的名字如下:

  response.sendRedirect("/MyWeb/index.html");

  如此请求将会重定向到http://localhost:8080/MyWeb/ index.html

  response.sendRedirect("index.html");

在路径地址处如果不加“/ ”相当于由当前项目所在目录开始寻找资源

也就相当于将请求重定向到如下地址:

  http://localhost:8080/MyWeb/hello/index.html

2:在MyWeb项目中form.html页面,目录结构如下:

  webapps/MyWeb/hello/form.html

  现在我在form.html中创建超链接访问/hello/HelloServlet

  连接格式如下:

  <a href=”/HelloServlet”>HelloServlet</a>

使用绝对路径,网页重定向的根目录相同,都是服务器的根目录

因此点击超链接后会访问如下地址

  http://localhost:8080 /HelloServlet

  这个地址明显不对,所以应该从项目名开始写起

 正确如下:

  <a href=”/MyWeb/hello/HelloServlet”>HelloServlet</a>

 点击后访问地址:

http://localhost:8080 /MyWeb/hello/HelloServlet

  <a href=”HelloServlet”>HelloServlet</a>

使用相对路径,从当前html所在目录开始寻找资源,也就是从/MyWeb/hello/开始。

因此点击超链接后会访问如下地址:

  http://localhost:8080 /MyWeb/hello/HelloServlet

总结:


    路径问题:
    
     相对路径:相对路径指的是相对于当前文件所在目录的路径!
       http://localhost:8080/servlet01/
       http://localhost:8080/servlet01/html/a.html
       html/a.html
      
        局限性
        绝对路径:绝对路径是以/开头表示的
    
        浏览器解析,/代表http://localhost:8080/【服务器的根目录】<a>
        服务器解析时,/代表 http://localhost:8080/项目名/ [项目的根目录]
  
    常见的路径:
      url-pattern  、 转发的路径
                   这两个路径都是由服务器端解析,/代表项目的根目录
      重定向的路径、 页面中的路径
                 这两个路径都是由浏览器端解析,/代表服务器的根目录
    
     <base>标签
         <!-- base标签由一个href属性,这个属性可以做为我们在页面中相对路径的前缀
            也就是说:我们在当前页面中写的相对路径,它会默认以base标签中的href
             属性值做为前缀,
             所以base标签可以让我们以使用相对路径的方式来使用绝对路径!
             base标签仅仅对相对路径起作用,对绝对路径不起作用!
         -->
    <base  href="http://localhost:8080/servlet01/html/"/>

 十、解决tomcat端口被占用:Port 8005 required by Tomcat v7.0 Server at localhost is already in use

问题提示8005端口被占用

首先:在cmd下,输入  netstat   -ano|findstr  8005 (什么端口号被占用就输入什么端口号),回车

再输入 taskkill  /pid  2044 /f   回车 这里的2044对应上图的2044

完成后,重启一下eclipse

十一、乱码原因

   Http协议进行通信的时候是基于请求和响应的,传输的内容我们称之为报文!
     Http协议会按照一定的规则将报文编码,然后在读取的时候再使用响应的解码格式进行解码!
     这个一定的规则指的就是字符集:ASCII,iso8859-1,gbk,gb2312,utf-8
    
     编码:将字符按照字符集转化为二进制格式
     解码:将二进制代码按照响应的字符集转化为字符!
    
     乱码的根本原因:就是编码和解码指定的字符集不一致造成的!
    
     解决方案:统一编码和解码的字符集,统一成utf-8字符集!
    
    请求报文:
       1).浏览器编码---------->服务器解码
          浏览器是按照给定页面的编码格式进行编码的,所以只需要设置网页的编码格式为utf-8就可以了!
          
          get请求:
            解码:使用的是tomcat默认的解码格式,默认是iso8859-1;
            我们在server.xml文件中设置:
            <Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
            
          post请求:
            解码:使用的是tomcat默认的解码格式,默认是iso8859-1;
            在服务器端我们使用的是:req.setCharacterEncoding("utf-8");来设置解码格式
            注意:req.setCharacterEncoding("utf-8")一定要设置在第一次获取请求参数之前!
     
    响应编码
       服务器编码------------->浏览器解码
      
      第一种解决方案:
              resp.setCharacterEncoding("utf-8");
              resp.setHeader("Content-Type", "text/html;charset=utf-8");
      第二种解决方案:
              resp.setHeader("Content-Type", "text/html;charset=utf-8");
      第三种方案:
                resp.setContentType("text/html;charset=utf-8");