本文的知识点:
- servlet快速入门
- servlet生命周期
- 生命周期详解
- 注解配置方式
- Servlet继承结构
- Servlet路径配置
1. Servlet快速入门
下载
servlet的jar包-> 创建lib文件夹并设置为依赖资源文件夹-> 实现servlet接口的方法-> 配置servlet-> 访问配置的url
1.1 下载jar包
推荐在Maven资源网站上下载,非常的全面。
1.2 创建lib并且设置为依赖资源文件夹
1.3 实现servlet方法
package cn.web.servlet;
import javax.servlet.*;
import java.io.IOException;
public class demo1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("hello servlet!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
1.4 配置servlet
在web.xml的<web-app>标签内添加:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>cn.web.servlet.demo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
</web-app>
注意:servlet-name要对应上,servlet-class为实现的servlet类,url-pattern为我们访问该servlet的url
1.5 访问'http://localhost:8080/demo1'
我们会发现浏览器中是空白的,因为我们没有写相应的页面,我们只是在实现servlet时写了service方法,我们查看控制台发现:
终端输出了四次
hello servlet!,因为我访问了四次。
到这里,servlet快速上手就已经完成啦!
2. Servlet生命周期
2.1 试验代码
package cn.web.servlet;
import javax.servlet.*;
import java.io.IOException;
public class demo2 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init....");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service...");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("destroy...");
}
}
2.2 web.xml也要修改
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>cn.web.servlet.demo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>demo2</servlet-name>
<servlet-class>cn.web.servlet.demo2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo2</servlet-name>
<url-pattern>/demo2</url-pattern>
</servlet-mapping>
</web-app>
2.3 控制台效果
2.4 总结
servlet 生命周期从初始化,提供服务到最终销毁分别对应的是:
初始化:
init(默认情况下只在第一次访问时创建)提供服务:
service(每次访问都会执行)销毁:
destroy(服务器关闭才会执行)
3. 生命周期详解
3.1 init方法
修改web.xml <load-on-startup>可以指定第一次访问创建(负数)或者服务器启动创建(0或者正数)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>cn.web.servlet.demo1</servlet-class>
<load-on-startup>-6</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>demo2</servlet-name>
<servlet-class>cn.web.servlet.demo2</servlet-class>
<load-on-startup>6</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>demo2</servlet-name>
<url-pattern>/demo2</url-pattern>
</servlet-mapping>
</web-app>
因为init只执行一次,所以能够说明servlet是一个单例,会遇到问题:
- 多个用户同时访问,会出现线程安全问题
- 尽量定义局部变量,如果定义了成员变量,最好不要有修改它的功能
3.2 service方法
每次访问都执行一次
3.3 destroy方法
只有服务器正常关闭才会执行,并且会在服务器关闭前执行(因为如果已经关闭了,那么就没法执行该方法了)
4. 注解配置方式
4.1 注解配置解释
servlet3.0以上才可以使用注解配置
在类的开头上一行注解配置
@WebServlet(urlPatterns = "url路径")优势:不再需要在
web.xml里去写配置项,方便快捷
package cn.web.servlet;
import javax.jws.WebService;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet(urlPatterns = "/demo1")
public class demo1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init1....");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service1...");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("destroy1...");
}
}
4.2 注解配置和xml配置对比
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>cn.web.servlet.demo1</servlet-class>
<load-on-startup>-6</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
等价于
@WebServlet(urlPatterns = "url路径")
4.3 配置项详情(WebServlet.class源码)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package javax.servlet.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
String name() default "";
String[] value() default {};
String[] urlPatterns() default {};
int loadOnStartup() default -1;
WebInitParam[] initParams() default {};
boolean asyncSupported() default false;
String smallIcon() default "";
String largeIcon() default "";
String description() default "";
String displayName() default "";
}
5. Servlet继承结构
我们在实现
Servlet接口的时候,总是需要实现init、destroy等方法,即使我们并不需要在这些方法中实现什么功能,我们仅仅是需要在service方法中实现我们的主要功能。这个时候就有以下方便开发的子类来供我们使用。
Servlet(接口) ----> GenericServlet(抽象类) ----> HttpServlet(抽象类)
注:我们常使用HttpServlet
5.1 GenericServlet
GenericServlet继承自Servlet,对其他方法进行了空实现,只将service()方法作为抽象方法,之后定义Servlet时继承GenericServlet,实现Service()方法即可。
- 部分源码:
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameter(name);
}
}
public Enumeration<String> getInitParameterNames() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameterNames();
}
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.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) {
this.getServletContext().log(this.getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletName();
}
}
}
注:我们可以看到,源码中将其他部分的方法都进行了空实现,比如
destroy()等,但是把主要的service()方法进行了抽象,后期继承的话只需要实现service()方法即可。
5.2 HttpServlet
HttpServlet继承自GenericServlet,是对HTTP协议的一种封装,后期这个最常用的类,定义继承自HttpServlet然后实现doGet/doPost方法即可。
- 部分源码:
public abstract class HttpServlet extends GenericServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
this.service(request, response);
} else {
throw new ServletException("non-HTTP request or response");
}
}
}
注:我们可以看到,
HttpServlet实现了service()方法,并且根据请求的具体方法(GET/POST)来进行不同方法的选择。
6. Servlet路径配置
- 一个
servlet可以配置多个路径:@WebServlet({"/xxx","/yyy"})- 路径定义规则:
/XXX/XXX/YYY*.XXX
6.1 配置多个路径
@WebServlet({"/demo3","/test"})
public class demo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo3....get....");
resp.getWriter().write("here is demo3 get()!");
}
}
- 输入路径
http://localhost:8080/demo3 - 输入路径
http://localhost:8080/test
注:可以发现访问同一个资源
6.2 路径配置规则
路径定义规则: -
/XXX-/XXX/YYY-*.XXX
@WebServlet({"/demo3","/demo3/test","*.xxx"})
public class demo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo3....get....");
resp.getWriter().write("here is demo3 get()!");
}
}
-
输入地址:
http://localhost:8080/demo3 -
输入地址:
http://localhost:8080/demo3/test
- 输入地址:
http://localhost:8080/asdasd.xxx