目录
什么是Servlet
用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类
Servlet工作模式
- 客户端发送请求至服务器
- 服务器启动并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器
- 服务器将响应返回客户端
Servlet API
Servlet API 包含以下4个Java包:
1.javax.servlet 其中包含定义servlet和servlet容器之间契约的类和接口。
2.javax.servlet.http 其中包含定义HTTP Servlet 和Servlet容器之间的关系。
3.javax.servlet.annotation 其中包含标注servlet,Filter,Listener的标注。它还为被标注元件定义元数据。
4.javax.servlet.descriptor,其中包含提供程序化登录Web应用程序的配置信息的类型。
Servlet工作原理
用户请求致使Servlet容器调用Servlet的Service()方法,并传入一个ServletRequest对象和一个ServletResponse对象。ServletRequest对象和ServletResponse对象都是由Servlet容器(例如TomCat)封装好的,并不需要程序员去实现,程序员可以直接使用这两个对象。
对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。
servlet接口中的方法
init(ServletConfig config)
ServletConfig getServletConfig()
service(ServletRequest req,ServletResponse res)
String getServletInfo()
destroy( )
使用IDEA开发一个带有servlet的web
第一步:基础搭建
1.创建一个新工程
2.创建一个新模块
2.1模块右击---->Add Framework Support..(添加框架支持)------>选择Web Application
(一个符合Servlet规范的webpp目录结构。)
第二步:编写Servlet
1.编写Servlet
1.1这个时候发现Servlet.class文件没有。怎么办?
File --> Project Structrue --> Modules---->Dependeencies--->添加Library或者JAR
2.实现jakarta.servlet.Servlet接口中的5个方法。
3.在Servlet当中的service方法中编写业务代码(我们这里连接数据库了。)
4.在WEB-INF目录下新建了一个子目录:lib 并且将连接数据库的驱动jar包放到lib目录下。
第三步:在web.xml文件中完成StudentServlet类的注册
<?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>studentServlet</servlet-name>
<servlet-class>com.bjpowernode.Student</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>studentServlet</servlet-name>
<url-pattern>/servlet/student</url-pattern>
</servlet-mapping>
</web-app>
第四步:给一个html页面,在HTML页面中编写一个超链接
- student.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--这里的项目名是 /xmm ,无法动态获取,先写死--> <a href="/xmm/servlet/student">student list</a> </body> </html>
第五步:让IDEA工具去关联Tomcat服务器。
1.Add Configuration
2.左上角加号,点击Tomcat Server --> local
3.Deployment(点击这个用来部署webapp),继续点击加号,部署即可。修改 Application context为:/xmm
第六步:启动tom服务器,浏览器输入地址
打开浏览器,在浏览器地址栏上输入:http://localhost:8080/xmm/student.html
注意:前端发送请求要加项目名
Servlet生命周期
1.网站中所有的Servlet接口实现类的实例对象,只能由Http服务器负责创建。
开发人员不能手动创建Servlet接口实现类的实例对象
2.在默认的情况下,用户第一次请求的时候,自动创建Servlet接口实现类对象。
(调用无参构造方法)
3.关于Servlet类中方法的调用次数?
- 构造方法只执行一次。
- init方法只执行一次
- service方法:用户发送一次请求则执行一次,发送N次请求则执行N次。
- destroy方法只执行一次
init(): 执行相应的初始化工作。调用这个方法时,Servlet容器会传入一个ServletConfig对象进来从而对Servlet对象进行初始化。
service(): 第一次请求时,Servlet容器会先调用init( )方法初始化一个Servlet对象出来,然后会调用它的service( )方法进行工作,但在后续的请求中,Servlet容器只会调用service方法了。
destroy(): 当要销毁Servlet时,Servlet容器就会调用这个方法
getServletInfo(): 这个方法会返回Servlet的一段描述,可以返回一段字符串。
getServletConfig(): 这个方法会返回由Servlet容器传给init( )方法的ServletConfig对象。
说明:
- 用户在发送第一次请求的时候Servlet对象被实例化(AServlet的构造方法被执行了。并且执行的是无参数构造方法。)
- AServlet对象被创建出来之后,Tomcat服务器马上调用了AServlet对象的init方法。
- 用户发送第一次请求的时候,init方法执行之后,Tomcat服务器马上调用AServlet对象的service方法。
- 用户在发送第二次,或者第三次,或者第四次请求的时候,Servlet对象并没有新建,还是使用之前创建好的Servlet对象,直接调用该Servlet对象的service方法.
GenericServlet
- MyServlet直接实现接口,会将该接口中所有方法实现
public class MyServlet 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 { } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
2.我们进行改进
我们让抽象类GenericServlet把不需要的方法实现即可,让service变成抽象类。
public abstract class GenericServlet implements Servlet { //ServletConfig servletConfig形参,我们不知道传进来的是什么 @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException; @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
public class MyServlet extends GenericServlet{ @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { } }
1.思考第一个问题MyServlet还能使用GenericServlet类中的init方法吗?
可以。会执行GenericServlet类中的init方法。
2.思考第二个问题:init方法是谁调用的?
Tomcat服务器调用的。
3.思考第三个问题:init方法中的ServletConfig对象是谁创建的?是谁传过来的?
都是Tomcat干的。
4.Tomcat服务器先创建了ServletConfig对象,然后调用init方法,将ServletConfig对象传给了 init方法。
package javax.servlet; import java.io.IOException; import java.util.Enumeration; public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable { //版本号 private static final long serialVersionUID = 1L; private transient ServletConfig config; /* 此构造方法啥也不做,因为Servlet的所有初始化任务都是由init方法完成的。 */ public GenericServlet() { // NOOP } @Override public void destroy() { // NOOP by default } @Override public String getInitParameter(String name) { return getServletConfig().getInitParameter(name); } @Override public Enumeration<String> 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 { /*该方法是形参,tomcat将实参传进来,然后通过this.config=config 把局部变量变为全局变量。*/ this.config = config; //Tomcat首先调用init()有参方法,然后调用init()无参方法。 this.init(); } /*目的是让子类重写init()方法的无参构造方法。*/ public void init() throws ServletException { // NOOP by default } 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(); } }
package GenericServlet; import javax.servlet.*; import java.io.IOException; public class MyServlet extends GenericServlet{ //我们需要在MyServlet中重写init方法 //子类重写第二个(无参的)就可 public void init(){ System.out.println("MyServlet init() method execute!"); } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { //我想在MyServlet子类中使用Servlet对象怎么办? ServletConfig config=this.getServletConfig(); } }
ServletRequset接口
Servlet容器对于接受到的每一个Http请求,都会创建一个ServletRequest对象,并把这个对象传递给Servlet的Sevice( )方法。其中,ServletRequest对象内封装了关于这个请求的许多详细信息。
ServletResponse接口
javax.servlet.ServletResponse接口表示一个Servlet响应,在调用Servlet的Service( )方法前,Servlet容器会先创建一个ServletResponse对象,并把它作为第二个参数传给Service( )方法。ServletResponse隐藏了向浏览器发送响应的复杂过程。
ServletConfig
- 什么是ServletConfig?
-
- ServletConfig是一个接口。是Servlet规范中的一员。
- ServletConfig对象中封装了标签中的配置信息。(web.xml文件中servlet的配置信息)
- 一个Servlet对应一个ServletConfig对象。
- Servlet对象是Tomcat服务器创建,并且ServletConfig对象也是Tomcat服务器创建。并且默认
- 情况下,他们都是在用户发送第一次请求的时候创建。
- Tomcat服务器调用Servlet对象的init方法的时候需要传一个ServletConfig对象的参数给init方法。
- ServletConfig接口的实现类是Tomcat服务器给实现的。(Tomcat服务器说的就是WEB服务器。)
- ServletConfig接口有哪些常用的方法?
public String getInitParameter(String name); // 通过初始化参数的name获取value public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的name public ServletContext getServletContext(); // 获取ServletContext对象 public String getServletName(); // 获取Servlet的name
以上方法在Servlet类当中,都可以使用this去调用。因为GenericServlet实现了ServletConfig接口。
ServletContext(应用域)
- 一个ServletContext对象通常对应的是一个web.xml文件。
- 只要在同一个webapp当中,只要在同一个应用当中,所有的Servlet对象都是共享同一个ServletContext对象的。
- ServletContext对象在服务器启动阶段创建,在服务器关闭的时候销毁。这就是ServletContext对象的生命周期。ServletContext对象是应用级对象。
- ServletContext是一个接口,Tomcat服务器对ServletContext接口进行了实现。
- ServletContext对象的创建也是Tomcat服务器来完成的。启动webapp的时候创建的 。
ServletContext接口中有哪些常用的方法?
public String getInitParameter(String name); // 通过初始化参数的name获取value
public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的name
<!--以上两个方法是ServletContext对象的方法,这个方法获取的是什么信息?是以下的配置信息-->
<context-param>
<param-name>pageSize</param-name>
<param-value>10</param-value>
</context-param>
<context-param>
<param-name>startIndex</param-name>
<param-value>0</param-value>
</context-param>
<!--注意:以上的配置信息属于应用级的配置信息,一般一个项目中共享的配置信息会放到以上的标签当中。-->
<!--如果你的配置信息只是想给某一个servlet作为参考,那么你配置到servlet标签当中即可,使用ServletConfig对象来获取。-->
// 获取应用的根路径(非常重要),因为在java源代码当中有一些地方可能会需要应用的根路径,这个方法可以动态获取应用的根路径
// 在java源码当中,不要将应用的根路径写死,因为你永远都不知道这个应用在最终部署的时候,起一个什么名字。
public String getContextPath();
//String contextPath = application.getContextPath();
// 获取文件的绝对路径(真实路径)
public String getRealPath(String path);
// 通过ServletContext对象也是可以记录日志的
public void log(String message);
public void log(String message, Throwable t);
// 这些日志信息记录到哪里了?
// localhost.2021-11-05.log
// Tomcat服务器的logs目录下都有哪些日志文件?
//catalina.2021-11-05.log 服务器端的java程序运行的控制台信息。
//localhost.2021-11-05.log ServletContext对象的log方法记录的日志信息存储到这个文件中。
//localhost_access_log.2021-11-05.txt 访问日志
// ServletContext对象还有另一个名字:应用域(后面还有其他域,例如:请求域、会话域)
// 如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,
// 可以将这些数据放到ServletContext这个应用域中
// 存(怎么向ServletContext应用域中存数据)
public void setAttribute(String name, Object value); // map.put(k, v)
// 取(怎么从ServletContext应用域中取数据)
public Object getAttribute(String name); // Object v = map.get(k)
// 删(怎么删除ServletContext应用域中的数据)
public void removeAttribute(String name); // map.remove(k)
注意:
以后我们编写Servlet类的时候,实际上是不会去直接继承GenericServlet类的,因为我们是B/S结构的系统,这种系统是基于HTTP超文本传输协议的,在Servlet规范当中,提供了一个类叫做HttpServlet,它是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。(HttpServlet是HTTP协议专用的。)使用HttpServlet处理HTTP协议更便捷。但是你需要直到它的继承结构:
jakarta.servlet.Servlet(接口)【爷爷】
jakarta.servlet.GenericServlet implements Servlet(抽象类)【儿子】
jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】
我们以后编写的Servlet要继承HttpServlet类。