Servlet详解及搭建教程

来源:互联网 发布:朗诵软件手机版 编辑:程序博客网 时间:2024/06/06 12:54

Servlet详解及搭建教程

前言:

    静态网页:新闻、百科,无论谁去看都一样的网页,服务器保存HTML并返回HTML。
    动态网页:淘宝、论坛、微博,每个人看到的内容可以不同的网页,服务器保存一个对象(Servlet),用它来动态拼一个HTML。
     在Java里这个对象叫Servlet(Servlet用来动态拼接HTML)
     Servlet:Sun推出的用于在服务器端处理HTTP协议的组件(组件:满足规范的一套API。所以说,servlet是一套具有处理http协议的API)。用来处理来自浏览器的请求及发送响应。并且Servlet就是动态的拼接了HTML页面。

      Servlet是什么? Servlet是实现了Java Servlet接口(API)运行在WEB引用服务器上的JAVA程序。与普通的JAVA程序不同,他是位于WEB服务器内部(Tomcat、JBoss)的服务器端的JAVA应用程序。可以处理HTTP协议的请求。

                             这里写图片描述


1、Servlet与Servlet容器

      Servelt与普通的Java程序不同的地方在于:Servlet可以处理来自浏览器或者HTTP客户端的HTTP请求。但,前提条件是:Servlet对象需要布置到Servlet容器中(如:Tomcat、JBoss、weblogic等等),换句话说,Servlet的运行需要Servlet容器支持。

1.1、Servlet的生命周期

                                   这里写图片描述

1.1.1、实例化

      当Servlet容器启动的时或者第一次接收到HTTP请求的时候(在Servlet配置文件load-on-startup子元素中设置),Servlet容器将加载Servlet类,并实例化。

1.1.2、初始化

      Servlet容器通过Servlet对象调用init()方法,完成Servlet对象全局性的初始工作。

1.1.3、调用方法

      Servlet容器通过Servlet对象调用service()方法(或者doget、dopost方法)处理不同类型的HTTP请求,在处理完成后做出响应。

1.1.4、摧毁

      当WEB应用被终止,或Servlet容器终止运行,或Servlet容器重新装载Servlet新实例时,Servlet容器会先调用Servlet的destroy()方法,在destroy()方法中可以释放掉Servlet所占用的资源。
      简单来说:Servlet对象在调用destroy()方法后释放资源。

2、举例Tomcat和Servlet的关系

      Tomcat 是Web应用服务器,是一个Servlet/JSP容器。Tomcat 作为Servlet容器(将浏览器发送过来的文本信息打包成Request对象。调用Servlet对象处理Request对象),负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户。而Servlet是一种运行在支持Java语言的服务器上的组件。

      从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。但是在上面这个图中,并不知道是如何转变的,只知道浏览器发送过来的请求也就是request,我们响应回去的就用response。忽略了其中的细节,现在就来探究一下。

                             这里写图片描述

WEB服务器:只有与浏览器通讯功能,不处理逻辑 Java服务器:处理请求逻辑,如Servlet。
JavaWeb服务器:集成web服务器与web服务器的应用程序,如TomCat。
Servlet容器:提供servlet对象运行环境,管理对象(Tomcat)
Servlet对象:实现了Servlet接口的类(或者继承HttpServlet类)

      2.1、Tomcat将http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象(交给Servlet对象处理),所有的HTTP头数据读可以通过request对象调用对应的方法查询到。

      2.2、Servlet同时会要响应的信息封装为HttpServletResponse类型的response对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本格式发送给浏览器。

      2.3、Java Servlet API 是Servlet容器(tomcat)和servlet对象之间的接口,它定义了serlvet的各种方法,还定义了Servlet容器传送给Servlet的对象类,其中最重要的就是ServletRequest和ServletResponse。所以说我们在编写servlet时,需要实现Servlet接口(或实现HttpServlet类),按照其规范进行操作。

3、Servlet工作流程:

     Servlet作为Web服务器的补充功能在运行时需要受到Servlet容器(Tomcat)的管理,其运行的流程如下:
一、浏览器依据IP建立与容器(服务器:Tomcat)的连接
二、浏览器将请求数据打包(打包成文本格式)
三、容器(Tomcat)解析请求数据包,创建并封装成HttpServletRequest。
四、容器(Tomcat)依据请求资源路径(Servlet配置文件:web.xml)找到Servlet创建对象
五、容器(Tomcat)将request和response对象作为参数传入Service方法,并调用
六、容器(Tomcat)将响应数据打包(打包成HttpServletResonse对象) 发给浏览器
七、浏览器取出结果,生成Html页面

                             这里写图片描述

4、编写一个Servlet实验

     4.1、创建一个JavaWeb项目,添加JavaEE支持
     4.2、创建一个TimeServlet类,并继承HttpServlet(Servlet对象:实现Servlet接口的类)

package Test;import java.io.IOException;import java.io.PrintWriter;import java.text.SimpleDateFormat;import java.util.Date;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class TestForTimeServlet extends HttpServlet {       private static final long serialVersionUID = 1L;    //1、初始化方法    @Override    public void init() {        System.out.println("初始化了Servlet-------------");    }    //2、能处理get、post请求    @Override    public void service(HttpServletRequest request, HttpServletResponse response)                throws ServletException, IOException {        Date date=new Date();        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        String nowTime=sdf.format(date);        PrintWriter pw=response.getWriter();        pw.println("<p>"+nowTime+"<p>");        pw.close();    }    //3、销毁Servlet    @Override    public void destroy() {        System.out.println("销毁了Servlet--------------");    }   }

     4.3、在web.xml中配置TimeServlet,为什么需要配置?

<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5"     xmlns="http://java.sun.com/xml/ns/javaee"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  <display-name></display-name>   <welcome-file-list>    <welcome-file>index.jsp</welcome-file>  </welcome-file-list>  <servlet>        <servlet-name>nowTime</servlet-name>                    <!--第三步-->        <servlet-class>Test.TestForTimeServlet</servlet-class>  <!--第四步-->  </servlet>  <servlet-mapping>        <servlet-name>nowTime</servlet-name>   <!--第二步-->        <url-pattern>/nowTime</url-pattern>    <!--第一步-->  </servlet-mapping> </web-app>

4.4、Servlet处理流程:

     浏览器发送一个请求:http://localhost:8080/TimeServlet/nowTime

     localhost: 本机IP
     8080:服务器端口号
     TimeServlet:项目名称
      nowTime:请求路径名(url-pattern)

第一步:Servlet容器接受到浏览器发送的请求,加载web.xml配置文件,实例化一个Servlet对象
第二步:根据web.xml配置文件,根据 配置的Servlet路径调用对应的Servlet对象
第三步:根据Servlet处理结果,将respone对象转为一定格式的文本,传到浏览器
第四步:浏览器解析得到的文本格式,展示页面。

     4.5、结果如下图所示:

                  这里写图片描述



5、使用向导(wizard)创建Servlet

      这个就相对简单了,web.xml不用我们手动配置,工具直接帮我们自动配置了。

5.1、右击项目,在new选项中有直接新建servlet的选项

       这里写图片描述



5.2、配置MyServlet类中的信息

       这里写图片描述


5.3、配置web.xml中的servlet信息

                                   这里写图片描述


5.4、查看MyServle01类中的代码和web.xml:
        其中的配置跟手动的配置是一样的,只是用图形化界面,让我们更方便的创建servlet而产生的。

6、创建Servlet的原理

     在开始这部分内容之前,先明确一个信息:ServletRequest、ServletRespone、ServletConfig、ServletContext这四个对象是由Servlet容器创建的,他们封装来了什么信息看下文介绍。

     一个项目只有一个ServletContext,每一个Servlet对应一个ServletConfig

6.1、servlet的生命周期是什么?

     实例化:Servlet容器创建一个Servlet对象
     初始化:调用init方法初始化Servlet对象的参数
     调用方法:调用service方法处理浏览器的请求
     摧毁:当浏览器关闭调用destory方法释放Servlet资源

               这里写图片描述

load-on-startup表示Servlet容器实例化Servlet对象、调用init()方法的顺序。
load-on-startup>=0: 表示服务器(tomcat)启动时就加载并初始化这个servlet;
load-on-startup<0或未指定时: 表示只有在第一次请求的服务器(tomcat)才在该servlet调用初始化函数。
正值越小: servlet的优先级越高,应用启动时就越先加载。
值相同时: 容器就会自己选择顺序来加载。

                         这里写图片描述



6.2、为什么创建的Servlet是继承自HttpServlet,而不是直接实现Servlet接口?

这里写图片描述

     查看源码,HttpServlet的继承结构。HttpServlet继承GenericServlet。懂的人立马就应该知道,GenericServlet(通用Servlet)的作用是什么?总的来说就是实现Servlet接口的方法,简化编写servlet的步骤。具体下面详解:
            

public abstract class HttpServlet extends GenericServlet    implements Serializable

    GenericServlet的继承结构,实现了Servlet接口和ServletConfig接口:
            

public abstract class GenericServlet    implements Servlet, ServletConfig, Serializable

    Servlet接口一共有五个方法:

                                这里写图片描述

    init:初始化方法
    service:处理get、或者Post请求
    destroy:释放Servlet资源的方法
    getServletConfig:方法来获取ServletConfig对象。

     ServletConfig接口的四个方法:(在ServletConfig接口中使用ctrl+o可以打开)

                                这里写图片描述

    getServletName( ):获得Servlet的名字,在web.xml中配置。
    getInitParameter():获得Servlet的初始化参数,在web.xml中配置。
    getInitParameterNames():获得Servlet的Enumeration类型初始化参数。
    getServletContext():获得ServletContext对象。该对象封装的方法如下:

                                这里写图片描述

    其中ServletContext对象是servlet上下文对象,功能有很多,获得了ServletContext对象,就能获取大部分我们需要的信息,比如获取servlet的路径,等方法。
    到此,就知道了Servlet接口中的内容和作用,总结起来就是,三个生命周期运行的方法,获取ServletConfig,而通过ServletConfig又可以获取到ServletContext对象。而GenericServlet实现了Servlet接口后,也就说明我们可以直接继承GenericServlet,就可以使用上面我们所介绍Servlet接口中的那几个方法了,能拿到ServletConfig,也可以拿到ServletContext,不过那样太麻烦,不能直接获取ServletContext,所以GenericServlet除了实现Servlet接口外,还实现了ServletConfig接口,那样,就可以直接获取ServletContext了。

    GenericServlet方法详解:

                                这里写图片描述

    看上图,GenericServlet就是实现Servlet和ServletConfig接口所实现的方法,这很正常,但是我们可以发现,init方法有两个,一个是带有参数ServletConfig的,一个有无参的方法,为什么这样设计?这里需要知道其中做了什么事情,来看看这两个方法分别做了什么事?如下面的源码:

 private transient ServletConfig config; public void init(ServletConfig config) throws ServletException {        this.config = config;        init();    } public void init() throws ServletException { } public ServletConfig getServletConfig() {        return config;    }

    init(ServletConfig config):将Servlet的配置对象(ServletConfig)传入进来了,这个 对象封装的是Servlet对象初始化的一些参数。
     init( ):没有参数传入,当我们继承GenericServlet类的时候就可以重写这类中的逻辑。

6.3、Servlet的生命周期中,可以看出,执行的是service方法,为什么我们就只需要写doGet和doPost方法呢?

 private transient ServletConfig config; public void init(ServletConfig config) throws ServletException {        this.config = config;        init();    } public void init() throws ServletException { } public ServletConfig getServletConfig() {        return config;    }


    第一:首先看init(ServletConfig config)方法,因为只有init(ServletConfig config)中带有ServletConfig对象,为了方便能够在其他地方也能直接使用ServletConfig对象,而不仅仅局限在init(ServletConfig config)方法中,所以创建一个私有的成员变量config,在init(ServletConfig config)方法中就将其赋值给config,然后通过getServletConfig()方法就能够获取ServletConfig对象了。

    第二:但是在init(ServletConfig config)中,还调用了一个init()方法,并且这个init()方法是空的,什么读没有,这是为什么呢?这个原因是为了防止一件事情,当我们需要在init方法中做一点别的事情,我们想到的方法就是继承GenericS ervlet并且重写了init(ServletConfig config)方法,这样依赖,就破坏了原本在GenericServlet类中init(ServletConfig config)写的代码了,也就是在GenericServlet类中的成员变量config会一直是null,无法得到赋值,因为被重写了,就不会在执行GenericServlet中init(ServletConfig config)方法中的代码。要想赋值,就必须在重写的init(ServletConfig config)方法中调用父类的init(ServletConfig config)方法,也就是super.init(ServletConfig config),这样一来,就很不方便,怕有时候会忘了写这句代码,所以在GenericServlet类中增加一个init()方法,以后需要在init方法中需要初始化别的数据,只需要重写init()这个方法,而不需要去覆盖init(ServletConfig config)这个方法,这样设计,就好很多,不用在管init(ServletConfig config)这个其中的内容了。也不用出现其他的问题。
   
    这里写图片描述

    这个抽象方法,说明在GenericServlet类中并没有实现该内容,那么我们想到的是,在它上面肯定还有一层,也就是还有一个子类继承它,实现该方法,要是让我们自己写的Servlet继承GenericServlet,需要自己写service方法,那岂不是累死,并且我们可以看到,service方法中的参数还是ServletRequest,ServletResponse。并没有跟http相关对象挂钩,所以我们接着往下面看。
           
    HttpServlet类详解:继承了GenericServlet类,通过我们上面的推测,这个类主要的功能肯定是实现service方法的各种细节和设计。并且通过类名可以知道,该类就跟Http挂钩了。

这里写图片描述

    关注service(HttpServletRequest req, HttpServletResponse resp)方法
和service(ServletRequest req, ServletResponse res)方法。

   service(ServletRequest req, ServletResponse res)方法:

 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);    }

    该方法中就做一件事情,就是将ServletRequest和ServletResponse这两个对象强转为HttpServletRequest和HttpServletResponse对象。
    为什么能这样转?
    首先要知道req、res是什么类型,通过打印System.out.println(req),可以知道,req实际上的类型是org.apache.catalina.connector.RequestFacade。                         

Tomcat中的源码:          

这里写图片描述

这里写图片描述

    通过图可以得知,req的继承结构:RequestFacade、httpServletRequest、ServletRequest,我们知道本身req是ServletRequest,那么从继承结构上看,它也可以看成HttpServletRequest,也可以看成ServletRequest,所以强转为HttpServletRequest是可以的。

  转换为httpServletRequest和HttpServletResponse对象之后,在调用service(HttpServletRequest req, HttpServletResponse resp)方法。

    service(HttpServletRequest req, HttpServletResponse resp):这个方法就是判断浏览器过来的请求方式是哪种,每种的处理方式不一样,我们常用的就是get,post,并且,我们处理的方式可能有很多的内容,所以,在该方法内会将get,post,put,delete,Options等其他5种请求方式提取出来,变成单个的方法,然后我们需要编写servlet时,就可以直接重写doGet或者doPost方法就行了,而不是重写service方法,更加有针对性。所以这里就回到了我们上面编写servlet时的情况,继承httpServlet,而只要重写两个方法,一个doGet,一个doPost,其实就是service方法会调用这两个方法中的一个(看请求方式)。

 protected void service(HttpServletRequest req, HttpServletResponse resp)        throws ServletException, IOException{        String method = req.getMethod();        if(method.equals("GET")){            long lastModified = getLastModified(req);            if(lastModified == -1L){                doGet(req, resp);            } else{                long ifModifiedSince = req.getDateHeader("If-Modified-Since");                if(ifModifiedSince < (lastModified / 1000L) * 1000L){                    maybeSetLastModified(resp, lastModified);                    doGet(req, resp);                } else{                    resp.setStatus(304);                }            }        } else if(method.equals("HEAD"))        {            long lastModified = getLastModified(req);            maybeSetLastModified(resp, lastModified);            doHead(req, resp);        } else if(method.equals("POST"))            doPost(req, resp);        else if(method.equals("PUT"))            doPut(req, resp);        else if(method.equals("DELETE"))            doDelete(req, resp);        else if(method.equals("OPTIONS"))            doOptions(req, resp);        else if(method.equals("TRACE")){            doTrace(req, resp);        } else{            String errMsg = lStrings.getString("http.method_not_implemented");            Object errArgs[] = new Object[1];            errArgs[0] = method;            errMsg = MessageFormat.format(errMsg, errArgs);            resp.sendError(501, errMsg);        }    }

查看源码:我们可以知道为什么service(HttpServletRequest req, HttpServletResponse resp)可以同时处理Get或者Post请求。
  
                    

7、四大类ServletConfig对象,ServletContext对象、ServletRequest对象,ServletResponse对象

7.1、ServletConfig对象:

获取途径:getServletConfig();
功能:上面大概提及了一下,能得到四个东西

这里写图片描述

   getServletName():获取servlet的名称,也就是我们在web.xml中配置的servlet-name
 getServletContext():获取ServletContext对象,该对象的作用看下面讲解
 getInitParameter(String name):获取在servlet中初始化参数的值。这里注意与全局初始化参数的区分。这个获取的只是在该servlet下的初始化参数。

这里写图片描述

    getInitParameterNames( ):获取在Servlet中所有初始化参数的名字,也就是key值,可以通过key值,来找到各个初始化参数的value值。注意返回的是枚举类型 。

    枚举类型:是一个被命名的整型常数的集合,枚举在日常生活中很常见。 

这里写图片描述

    输出结果:  

这里写图片描述

注意:在上面我们所分析的源码过程中,我们就知道,其实可以不用先获得ServletConfig,然后在获取其各种参数,可以直接使用其方法,比如上面我们用的ServletConfig().getServletName();可以直接写成getServletName();而不用在先获取ServletConfig();了,原因就是在GenericServlet中,已经帮我们获取了这些数据,我们只需要直接拿就行。  

7.2、ServletContext对象

获取途径:getServletContext();
getServletConfig().getServletContext();

这里写图片描述

      这两种获取方式的区别就跟上面的解释一样,第一种是直接拿,在GenericServlet中已经帮我们用getServletConfig().getServletContext();拿到了ServletContext。我们只需要直接获取就行了,第二种就相当于我们自己在获取一遍,两种都是一样的。

      功能:tomcat为每个web项目创建一个ServletContext实例,tomcat在启动时创建,服务器关闭时销毁,在一个web项目中共享数据,管理web项目资源,为整个web配置公共信息等,通俗点讲,就是一个web项目,就只存在一个ServletContext实例,每个Servlet都可以访问到它。

7.2.1、web项目中共享数据:

这里写图片描述

        setAttribute(String name, Object obj) :在整个web项目范围内存放内容,以便让在web项目中所有的servlet读能访问到。
   getAttribute(String name) :通过指定名称获得内容
   removeAttribute(String name) :通过指定名称移除内容

这里写图片描述

这里写图片描述

这里写图片描述

7.2.2、web项目中初始化参数 (全局初始化参数,每个Servlet中都能获取到该初始化值):

getInitPatameter(String name):通过指定名称获取初始化值

web.xml 配置 整个web项目的初始化:  

这里写图片描述
这里写图片描述

输出结果:

这里写图片描述

7.2.3、获取web项目资源

     获取web项目下指定资源的路径:getServletContext().getRealPath(“/WEB-INF/web.xml”)
这里写图片描述

输出结果: 

这里写图片描述

    获取web项目下指定资源的内容,返回的是字节输入流:
    InputStream getResourceAsStream(java.lang.String path)
              
这里写图片描述

    输出内容截图一部分: 

这里写图片描述

    指定路径下的所有内容(文件及其文件夹):getResourcePaths(java.lang.Stringpath)  

这里写图片描述


输出结果:
  
这里写图片描述

这里写图片描述

7.3、ServletRequest对象

  我们知道,ServletRequest就是将请求文本封装而成的对象,所以通过request能获得请求文本中的所有内容:请求头、请求体、请求行 。
  
这里写图片描述


7.3.1、请求行内容的获取:

这里写图片描述

这里写图片描述



7.3.2、请求头的获取:随便百度一个东西,然后查看的请求头,包括以下这些内容,稍作了解。

这里写图片描述

String getHeader(java.lang.String name) 获得指定头内容String

这里写图片描述
这里写图片描述

long getDateHeader(java.lang.String name)        获得指定头内容Dateint getIntHeader(java.lang.String name)            获得指定头内容intEnumeration getHeaders(java.lang.String name)   获得指定名称所有内容


7.3.3、请求体的获取 – 请求参数的获取,分两种,一种get请求,一种post请求:

     get请求参数:http://localhost:8080/test01/MyServlet?username=jack&password=1234
当form表单没有设置method属性,默认为请求方式为get,浏览器会将页面中所有带name的参数值以URL传参的方式传到后台。

post请求参数: <form method="post"><input type="text" name="username">String request.getParameter(String  name) 获得指定名称,一个请求参数值。String[] request.getParameterValues(String  name) 获得指定名称,所有请求参数值。例如:checkboxselect

Map<String , String[]> request.getParameterMap() 获得所有的请求参数

注意:浏览器的编码格式为UTF-8,而Tomcat的配置文件server.xml中为Servlet设置的编码格式文ISO-8859-1(只读取、响应页面req、res编码),所以需要做编码格式转换。

req.setCharacterEncoding("utf-8"); res.setContentType("text/html;charset=utf-8");

                这里写图片描述

   浏览器在解析res发送过来的信息会按照后台发送过来的编码格式解析。
 

7.3.4、request 请求转发:

     request.getRequestDispatcher(String path).forward(request,response); 

     path:转发后跳转的页面,这里不管用不用”/”开头,都是以web项目根开始,因为这是请求转发,请求转发只局限与在同一个web项目下使用,所以这里一直都是从web项目根下开始的。

web项目根:    开发:G:\Workspaces\test01\WebRoot\..    运行时:D:\java\tomcat\apache-tomcat-7.0.53\webapps\test01\..web站点根:    运行时:D:\java\tomcat\apache-tomcat-7.0.53\webapps\..

    从这里可以看出,web项目根就是从该web项目名开始,所以我们请求转发时,只需要接着项目名后面需要访问的路径写就行了.

    特点:浏览器中url不会改变,也就是浏览器不知道服务器做了什么,是服务器帮我们跳转页面的,并且在转发后的页面,能够继续使用原先的request,因为是原先的request,所以request域中的属性都可以继续获取到。

7.4、ServletSesponse对象

这里写图片描述

    常用的一个方法:response.setHeader(java.lang.String name, java.lang.String value) 设置指定的头,一般常用。例如:设置每隔3秒就自动刷新一次:

response.setHeader("Refresh",3);

这里写图片描述

这里写图片描述

这里写图片描述

    这样可以看到现在时间的秒数,会发现每隔三秒就会自动刷新一次页面。这个最重要的一个就是重定向,其他的一些操作都被封装到response对象中了,重点讲解重定向 

方式一:手动方案response.setStatus(302);  //状态码302就代表重定向response.setHeader("location","http://www.baidu.com");                方式二:使用封装好的,通过response.sendRedirect("http://www.baidu.com");

  特点:服务器告诉浏览器要跳转的页面,是浏览器主动去跳转的页面,浏览器知道,也浏览器的地址栏中url会变,是浏览器重新发起一个请求到另外一个页面,所以request是重新发起的,跟请求转发不一样。
  注意:response.sendRedirect(path);  //
  

  第二种:response.sendRedirect("MyServlet01");//没有使用”/”开头,说明是从web项目根开始,那么就无需写test01了。
  
  重定向没有任何局限,可以重定向web项目内的任何路径,也可以访问别的web项目中的路径,并且这里就用”/”区分开来,如果使用了”/”开头,就说明我要重新开始定位了,不访问刚才的web项目,自己写项目名,如果没有使用”/”开始,那么就知道是访问刚才那个web项目下的servlet,就可以省略项目名了。就是这样来区别。

原创粉丝点击