TENCHIANG / blog

issue blog
10 stars 1 forks source link

Servlet学习笔记 #66

Open TENCHIANG opened 4 years ago

TENCHIANG commented 4 years ago

Servlet学习笔记

JSP本质上也是Servlet,因为最终会编译成Servlet

Apache Tomcat Versions

Tomcat Servlet JSP EL WebSocket JASIC
10 5.0 3.0 4.0 2.0 2.0
9 4.0 2.3 3.0 1.1 1.1

手动编译Servlet

javac \
-classpath tomcat/lib/servlet-api.jar \
-d ./classes/src/org/example/Hello.java

javax.servlet

javax.servlet.http

Servlet不限定只能使用HTTP协议,所以单独拎出来

HttpServlet.service()

这就是为什么要覆写doGet、doPost等方法的原因(但别覆写HttpServlet.service本身),因为请求来时,会先判断是什么HTTP动词,然后再调用对应的方法(设计模式的Template Method模式)

protected void service (HttpServletRequest req, HttpServletResponse res) 
throws ServletException, IOException {
    String method = req.getMethod(); // 取得请求的方法
    if (method.equals(METHOD_GET)) {
        // ...
        doGet(req, res);
        // ...
    } else if (method.equals(METHOD_POST)) {
        // ...
        doPost(req, res);
        // ...
    } else if (method.equals(METHOD_PUT)) {
        // ...
    }
}

@WebServlet:用标注定义Servlet

Servlet3.0(JavaEE6.0)之后,可以使用标注(Annotation)来告诉Web容器关于Servlet的一些信息,Servlet3.0之前用的是web.xml

@WebServlet("/hello")
public class Hello extends HttpServlet {
// ...
}

只要Servlet上设置@WebServlet标注,容器就会自动读取其中的信息:如果请求的URI是/hello,则由Hello的实例提供服务。

@WebServlet还可以提供更多的信息

@WebServlet(
    name="hello", // 指定Servlet的名称 默认为 类名
    urlPatterns={"/hello"}, // 指定URI 默认为 /类名
    loadOnStartup=1 // 程序启动时就初始化该Servlet类 默认为 -1 表示不会主动初始化
)
public class Hello extends HttpServlet {
// ...
}

loadOnStartup

Servlet类默认不会主动初始化(-1),只有在请求需要调用某个Servlet服务时,才将对应Servlet类载入、实例化、初始化,然后才真正的开始处理请求。 loadOnStartup如果设置大于-1的值,则表示程序启动后就初始化Servlet类。数字代表初始化的顺序,容器从较小的数字开始初始化,如果数字相同,容器实现厂商则可以自行规定(标准未定义)。

用web.xml定义Servlet

Servlet3.0(JavaEE6.0)之前,是在WebContent/WEB-INF/web.xml文件定义的(现在也可以)。

用eclipse创建web.xml

假设项目名为FirstServlet,在右边Project Explorer,展开项目根节点,右键Deployment Descriptor: FirstServlet节点,在弹出的菜单里点击Generate Deployment Descriptor Stub,就会生成web.xml,里面默认内容为:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
  <display-name>FirstServlet</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

这样的文件叫部署描述文件(Deployment Description, DD文件)。因为创建项目的时候,Dynamic web module version选择的是4.0(Servlet4.0),XSD文件为web-app_4_0.xsd,version为4.0。

<display-name>代表Web应用程序的名称,Tomcat默认会使用应用程序目录(项目名)作为环境根目录(Context root)localhost:8080/FirstServlet/,也可以在META-INF/context.xml中设置环境根目录。

用eclipse设置环境根目录:右键项目,选择Properties,在Web Project Settings中设置环境根目录。

Servlet4.0的<default-context-path>建议默认环境根目录:为啥叫建议,因为得考虑向下兼容,容器实现可以不理会这个设置。

web.xml可以覆盖Servlet中的标注,所以可以用标注做默认值,web.xml做更新值

<servlet>
    <servlet-name>Hello</servlet-name>
    <servlet-class>org.example.Hello</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<serlvet-mapping>
    <servlet-name>Hello</servlet-name>
    <url-pattern>/helloUser</url-pattern>
</serlvet-mapping>

如果<load-on-startup>数字相同,则按照在web.xml的书写顺序来初始化Servlet。

<url-pattern>设置的逻辑名称(Logical Name)/helloUser覆盖了标注的/hello,所以必须得用/helloUser才能访问到该Servlet。

无论是标注还是xml,URI都只是逻辑名称,最终会由Web容器对应到实际处理请求的程序实体名称(Physical Name)或文件,甚至可以设置伪装后的名称,如/hello.jsp。

<servlet-name>:设置Servlet的名称 <servlet-class>:设置实体类名称(编译好的Servlet在WEB-INF/classes即可运行) <url-pattern>:设置逻辑名称

文件组织和部署

部署在Web容器上的文件组织与IDE开发环境下的不太一样, 要求把WEB-INFWebContent中弄出来, 位于程序根目录下

再把整个项目放在tomcat的webapps文件下, 然后运行bin/startup命令启动就可以运行啦

实际上的部署会把项目打包成war(Web Archive)文件, 可以用JDK的jar命令来生成

cd FirstServlet
jar cvf ../FirstServlet.war *

或者使用eclipse, 右键项目, Export, WAR file把项目导出成war文件. war文件本质就是打包好的zip文件, 放在tomcat/webapps目录下, 下次启动时, 容器检测到有war文件, 会将其解压, 并载入web程序

URI模式设置

requestURI = contextPath + servletPath + pathInfo

requestURI

contextPath(环境路径)

servletPath(Servlet路径)

一旦决定是哪个是哪个Web应用程序来处理请求,接下来就是那个具体Servlet的事情了,Servlet必须设置URI模式:

pathInfo(路径信息)

可以通过HttpServletRequest.getPathInfo()获取(注意:路径信息不包括请求参数)

urlPatterns为"/servlet/*"的实例

访问http://localhost:8080/FirstServlet/servlet/yy

requestURI: /FirstServlet/servlet/yy
contextPath: /FirstServlet
servletPath: /servlet
pathInfo: /yy

HttpServletMapping

Servlet4.0中,HttpServletRequest新增了getHttpServletMapping()方法,可以取得javax.servlet.http.HttpServletMapping对象

HttpServletMapping对象

举个例子