JavaWeb之Servlet基本使用
一、Servlet概述
Servlet在javax.servlet包下,是Servlet规范中的核心接口。
Servlet是一个运行在web服务器中的java小程序,负责接收和响应来自客户端的请求,使用的交互使用的协议通常是HTTP协议。
Servlet是一个接口,一个类要想通过浏览器被访问到,那么这个类就必须直接或间接的实现Servlet接口
对比普通的类和servlet
- servlet必须运行在web服务器上,普通的类可以在任何项目中
- servlet做的事情是(接收请求,处理请求,生成响应);普通的类做的事情是由开发人员自定义的
- servlet的对象是web服务器创建的;普通的类的对象目前都是开发人员自己创建的
二、Servlet快速入门
2.1 编写步骤
- 创建一个 WEB 项目
- 创建一个类实现Servlet接口
- 重写 service 方法。
- 在 web.xml 中配置 Servlet
- 部署并启动项目
- 通过浏览器测试
2.2 代码实现
web.xml中HelloServlet的配置
<?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_3_1.xsd"
version="3.1">
<!--
找到类,给类设置一个访问路径
<servlet>
<servlet-name>别名:整个xml中必须是唯一的</servlet-name>
<servlet-class>类的全限定类名,唯一锁定类.底层tomcat需要借助全限定类名反射实例化(new)对象</servlet-class>
</servlet>
<servlet-mapping> servlet的映射配置
<servlet-name>别名:使用已经定义好的别名</servlet-name>
<url-pattern>访问的链接地址 /名称</url-pattern>
</servlet-mapping>
-->
<servlet>
<servlet-name>demo</servlet-name>
<servlet-class>com.itheima.servlet.DemoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo</servlet-name>
<url-pattern>/sayHello</url-pattern>
</servlet-mapping>
</web-app>
servlet代码
public class DemoServlet implements Servlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("sayHello");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
2.3 执行过程
浏览器发送请求→Tomcat服务器→我们的应用→应用下的web.xml→ServletDem1→执行service方法,响应浏览器
2.4 创建Servlet的三种方式
- 第一种:实现 Servlet 接口,实现所有的抽象方法。该方式支持最大程度的自定义
- 第二种:继承 GenericServlet 抽象类,必须重写 service 方法,其他方法可选择重写;
该方式让我们开发 Servlet 变得简单,但是这种方式和 HTTP 协议无关 - 第三种:继承 HttpServlet ,需要重写
doGet
和doPost
方法,该方式表示请求和响应都需要使用HTTP 协议
使用继承GenericServlet方式创建Servlet
xml配置
<servlet>
<servlet-name>ServletDemo</servlet-name>
<servlet-class>com.itcast.servlet.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo</servlet-name>
<url-pattern>/ServletDemo</url-pattern>
</servlet-mapping>
public class ServletDemo extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("继承GenericServlet的方式");
}
}
使用继承HttpServlet方式创建Servlet
xml配置
<servlet>
<servlet-name>ServletDemo2</servlet-name>
<servlet-class>com.itcast.servlet.ServletDemo2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo2</servlet-name>
<url-pattern>/ServletDemo2</url-pattern>
</servlet-mapping>
public class ServletDemo2 extends HttpServlet {
//post提交
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);//不希望书写代码重复 直接调用一下即可
}
//get提交
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("继承HttpServlet的方式");
}
}
关于HttpServlet
- 是
GenericServlet
的子类 - 重写了父类的
service
方法,通过doPost
和doGet
方法调用 - 类中
doPost
和doGet
方法如果不重写的话会报错,规范要求重写 - 请求和响应都使用了HTTP 协议
实际开发中都是使用继承HttpServlet
类的方式。
2.5 Servlet对象的生命周期
2.5.1 生命周期概述
生命周期:对象从创建到销毁的过程。
- 创建:请求第一次到达Servlet时(第一次访问Servlet时)对象创建
- 活着:只要应用一直提供服务,对象就一直存在
- 销毁:应用停止服务或者是服务器停止(宕机)时,对象销毁
与生命周期相关的API:
public void init(ServletConfig servletConfig)
初始化public void service(ServletRequest servletRequest, ServletResponse servletResponse)
服务public void destroy()
正常销毁
public class ServletDemo implements Servlet {
//初始化
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("初始化方法执行了");
}
//服务
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
int count = 1;
System.out.println("sayHello");
count ++;
System.out.println(count);
}
//销毁
@Override
public void destroy() {
System.out.println("销毁方法执行了");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
上述代码效果:
-
默认第一次访问服务器执行
init
初始化方法,创建Servlet
对象 -
每次访问都会执行
service
方法 -
正常关闭服务器或者卸载应用会执行
destroy
销毁方法
Servlet对象可以看成是单例对象,默认是第一次请求来的时候,web服务器会创建Servlet
对象,然后执行对象的init
方法,实现初始化操作,再执行servlet的service方法来处理当次请求;
再有请求来的时候,web服务器会获取一个线程,在当前线程中,会调用Servlet
对象的service
方法来处理请求;
当Servlet
被移除的时候或者web服务器关闭的时候,执行servlet的destory方法来显示销毁操作。
2.5.2 修改Servlet对象创建时机
在<servlet>
标签中,添加<load-on-startup>
标签,然后在这个标签中添加一个整数:
- 正整数代表服务器加载时创建,值越小、优先级越高
- 负整数或不写代表第一次访问时创建
<servlet>
<servlet-name>demo</servlet-name>
<servlet-class>com.itcast.servlet.ServletDemo</servlet-class>
<!--
load-on-startup : load 加载 startup 启动
启动的时候就加载servlet,标签体内部书写数字 一般1-6(启动顺序)
数字越小 优先级越高
-->
<load-on-startup>2</load-on-startup>
</servlet>
不同时机加载的优劣:
- 第一次访问时创建
优势:减少对服务器内存的浪费,提高了服务器启动的效率。
弊端:如果有一些要在应用加载时就做的初始化操作,无法完成 - 服务器加载时创建
优势:提前创建好对象,提高了首次执行的效率;还可以完成一些应用加载时要做的初始化操作
弊端:对服务器内存占用较多,影响了服务器启动的效率
2.6 Servlet线程安全问题
Servlet是单实例运行的,所以存在线程安全问题,所以在类中尽量不要定义成员变量,已经定义的类成员最好不能修改。
如果需要定义可以修改的成员,要定义到方法里面去。
2.7 Servlet路径匹配
url-pattern
:将一个请求网络地址和Servlet类建立一个映射关系(一对一)
资源访问路径:
2.7.1 配置多个访问映射规则
<servlet>
<servlet-name>ServletDemo5</servlet-name>
<servlet-class>com.itcast.servlet.ServletDemo5</servlet-class>
</servlet>
<!--映射ServletDemo5-->
<servlet-mapping>
<servlet-name>ServletDemo5</servlet-name>
<url-pattern>/ServletDemo5</url-pattern>
</servlet-mapping>
<!--一个Servlet可以配置多个访问映射规则-->
<servlet-mapping>
<servlet-name>ServletDemo5</servlet-name>
<url-pattern>/admin/ServletDemo5</url-pattern>
</servlet-mapping>
2.7.2 不同的路径匹配映射方式
- 全匹配映射方式:访问URL中
ServletURI
的部分和web.xml的URI
配置部分完全一致
如访问地址:http://localhost:8585/servlet/ServletDemo1
xml配置:
<servlet-mapping>
<url-pattern>/ServletDemo1</url-pattern>
</servlet-mapping>
- 路径匹配,其余通配(
*
表示通配符)
如访问地址:http://localhost:8585/servlet/ServletDemo1
http://localhost:8585/servlet/ServletDemo2
<servlet-mapping>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
- 后缀匹配,其余通配(
*
表示通配符)
如访问地址:http://localhost:8585/servlet/ServletDemo1.do
http://localhost:8585/servletDemo/ServletDemo2.do
<servlet-mapping>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
- 缺省匹配:
/"
,此路径不要使用,已经有Servlet使用,任何web服务器中都有两个Serlvet(默认的Servlet和jspservlet)
默认的Servlet:用来处理别的servlet都处理不了的请求
jspserlvet:用来处理后缀名为jsp或者jspx的请求
一个路径只能有一个Servlet执行,路径匹配的优先级:
-
全匹配>路径匹配其余通配>后缀名匹配>缺省匹配
-
谁更精确,谁的优先级更高
-
当都用通配符
*
时,有/
的永远比没/
优先级高
2.8 ServletConfig对象
2.8.1 ServletConfig对象概述
ServletConfig 是 Servlet 的配置参数对象,在 Servlet 的规范中,允许为每一个 Servlet 都提供一些初始化的配置,所以每个 Servlet 都有一个自己的 ServletConfig对象。
ServletConfig的作用是在Servlet 初始化时,把一些配置信息传递给 Servlet。
ServletConfig对象是一个单例对象,其生命周期和Servlet相同。
2.8.2 配置
<servlet>
<servlet-name>ServletDemo</servlet-name>
<servlet-class>com.itcast.servlet.ServletDemo</servlet-class>
<!--配置Servlet的初始化参数-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>namespace</param-name>
<param-value>abc</param-value>
</init-param>
</servlet>
<param-name>
:代表初始化参数的 key<param-value
:代表初始化参数的 value
2.8.3 常用方法
返回值 | 方法名 | 说明 |
---|---|---|
String | getInitParameter(String name) | 根据参数名称获取参数的值 |
Enumeration<String> | getInitParameterNames() | 获取所有参数名称的枚举 |
String | getServletName() | 获取Servlet的名称 |
ServletContext | getServletContext() | 获取ServletContext对象 |
2.9 ServletContext对象
2.9.1 ServletContext对象概述
ServletContext
是应用上下文对象(应用域对象),存放着应用的所有Servlet信息,每一个应用中只有一个ServletContext 对象,它可以实现整个应用间的数据共享。
域对象指有作用范围的对象,ServletContext
的作用范围是整个应用,是整个web应用中四大域对象范围最大那个,称为应用域。
2.9.2 ServletContext对象常用API
getServletConfig().getServletContext()
获取ServletContext对象getServletContext()
获取ServletContext对象简写形式void setAttribute(String name,Object value)
存储数据Object getAttribute(String name)
获取数据void removeAttribute(String name)
删除数据getRealPath(virtualPath)
通过一个虚拟路径获取磁盘真实路径(往服务器上写文件时需要使用此方法)getInitParameter(String name)
整个应用的初始化参数,不在Servlet中配置
<!--映射ServletDemo-->
<servlet-mapping>
<servlet-name>ServletDemo</servlet-name>
<url-pattern>/ServletDemo</url-pattern>
</servlet-mapping>
<!--配置ServletContext的初始化参数-->
<context-param>
<param-name>namespace</param-name>
<param-value>abc</param-value>
</context-param>
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
Enumeration<String> names= getInitParameterNames()
获取初始化参数名称的枚举
2.9.3 ServletContext的生命周期
- 创建:应用加载时,对象创建
- 活着:应用提供服务时,对象一直可用(处于活着的状态)
- 销毁:应用卸载(停止服务),或者服务器宕机,对象消亡
2.10 注解配置Servlet
- Servlet 2.5:xml开发
- Servlet 3.0以后:注解开发
使用注解的作用是替代xml的, 简化xml的配置操作。
使用的注解:@WebServlet
属性:
- value:指定访问Servlet的URI,和urlPatterns属性是一样的
- loadOnStartUp:用于配置Servlet的创建时机
- initParam:用于配置Servlet的初始化参数
@WebServlet(value = "/ServletAnnotationConfigDemo",loadOnStartup = 1,
initParams ={
@WebInitParam(name="namespace",value="abc"),
@WebInitParam(name="encoding",value="UTF-8")
} )
public class ServletAnnotationConfigDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletAnnotationConfigDemo执行了");
//获取Servlet的配置对象
ServletConfig config = this.getServletConfig();
//根据名称获取值
String value1 = config.getInitParameter("namespace");
String value2 = config.getInitParameter("encoding");
System.out.println(value1+","+value2);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}