Servlet:
Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一。使用JavaEE的API。目前在Oracle官网中的最新版本是JavaEE8,
- Servlet是一个运行在web服务端的java小程序
- 它可以用于接收和响应客户端的请求
- 要想实现Servlet功能,可以实现Servlet接口,继承GenericServlet或者HttpServlet
- 每次请求都会执行service方法
- Servlet还支持配置
Servlet执行过程:
- 客户端浏览器发起请求
- Tomcat服务器解析URL
- 通过URL找到对应的应用
- 通过应用找到web.xml
- 解析请求资源地址URL
- 找到应用的资源
- 执行service方法,响应给客户端浏览器
Servlet关系视图:
Servlet实现方式:
1、 实现Servlet接口,接口中的方法必须全部实现。
表示接口中的所有方法在需求方面都有重写的必要。这种方式支持最大程度的自定义。
2、继承GenericServlet,service方法必须重写,其他方可根据需求,选择性重写。
表示只在接收和响应客户端请求这方面有重写的需求,而其他方法可根据实际需求选择性重写,使开发Servlet变得简单。但是,此种方式是和HTTP协议无关的。
3、继承HttpServlet,它是javax.servlet.http包下的一个抽象类,是GenericServlet的子类。如果选择继承HttpServlet时,只需要重写doGet和doPost方法,不要覆盖service方法。
表示我们的请求和响应需要和HTTP协议相关。也就是说通过HTTP协议来访问的。那么每次请求和响应都符合HTTP协议的规范。请求的方式就是HTTP协议所支持的方式
实现Servlet:
public class ServletDemo03 implements Servlet {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {System.out.println("实现Servlet方式");}@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {}
}
继承GenericServlet:
public class ServletDemo01 extends GenericServlet {@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {System.out.println("service方法执行了...");}
}
继承HttpServlet::
public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("Servlet执行了");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}
配置文件:
<?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><servlet-name>servletDemo</servlet-name><servlet-class>com.example.demo.ServletDemo</servlet-class></servlet><!--映射配置--><servlet-mapping><servlet-name>servletDemo</servlet-name><url-pattern>/servletDemo</url-pattern></servlet-mapping><!--生命周期配置--><servlet><servlet-name>servletDemo02</servlet-name><servlet-class>com.example.demo.ServletDemo02</servlet-class></servlet><servlet-mapping><servlet-name>servletDemo02</servlet-name><url-pattern>/servletDemo02</url-pattern></servlet-mapping></web-app>
Servlet生命周期:
- 对象的生命周期,就是对象从生到死的过程,
- 出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。
- 活着:服务器提供服务的整个过程中,该对象一直存在,每次都是执行service方法。
- 死亡:当服务停止时,或者服务器宕机时,对象消亡。
- Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,用了单例模式。
演示:
public class ServletDemo02 extends HttpServlet {// 对象出生@Overridepublic void init() throws ServletException {System.out.println("出生了");}// 对象服务@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("收到客户端请求");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}// 对象死亡@Overridepublic void destroy() {System.out.println("挂了");}
}
线程安全:
在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据。其实每一个浏览器端发送请求,就代表是一个线程,那么多个浏览器就是多个线程,多个线程会共享Servlet类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,所以Servlet它不是线程安全的。分析产生这个问题的根本原因,其实就是因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。
解决:
在Servlet中定义类成员要慎重。如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线程安全问题了,把它定义到doGet或者doPost方法里面去就可以了。或者是直接加synchronized
演示:
public class ServletDemo03 extends HttpServlet {private String username;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {synchronized (this) {// 在这里声明的话,就是把成员变量变成局部变量,谷歌进来是一个username,火狐进来又是另一个就不会出现出数据不安全了// String username = null;// String getParameter(String name):根据参数名称获取参数值username = req.getParameter("username");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// resp.getWriter 传数据给前端PrintWriter writer = resp.getWriter();writer.println("username:" + username);writer.close();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req,resp);}
}
Servlet映射三种方式:
- 指名道姓,给具体的名称,访问路径必须和配置的一样
- /开头+通配符的方式,只要符合目录结构,无视结尾
- 通配符+固定格式结尾方式,只要符合固定结尾,无视前面路径
说明:映射优先级:越具体优先级越高,1>2>3
演示:
<?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"><!--<!–ServletHttp配置–><servlet><servlet-name>servletDemo</servlet-name><servlet-class>com.example.demo.ServletDemo</servlet-class></servlet><servlet-mapping><servlet-name>servletDemo</servlet-name><url-pattern>/servletDemo</url-pattern></servlet-mapping>--><!-- <!–/开头+通配符的方式–><servlet><servlet-name>servletDemo</servlet-name><servlet-class>com.example.demo.ServletDemo</servlet-class></servlet><servlet-mapping><servlet-name>servletDemo</servlet-name><url-pattern>/servlet/*</url-pattern></servlet-mapping>--><!--通配符+固定格式结尾 结尾写什么就写什么,开心就好--><servlet><servlet-name>servletDemo</servlet-name><servlet-class>com.example.demo.ServletDemo</servlet-class></servlet><servlet-mapping><servlet-name>servletDemo</servlet-name><url-pattern>*.itzhuzhu</url-pattern></servlet-mapping>
</web-app>
实现类:
public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 定义一个商品金额int money = 1000;// 获取访问路径String uri = req.getRequestURI();// public int lastIndexOf(int ch): 返回指定字符在此字符串中最后一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。uri = uri.substring(uri.lastIndexOf("/"));// 条件判断if ("/vip".equals(uri)) {System.out.println("原价:" + money + "尊敬的Vip您的折后价是:" + money * 0.8);} else if ("/svip".equals(uri)) {System.out.println("原价:" + money + "尊敬的Sip您的折后价是:" + money * 0.1);} else {System.out.println("会员都不开还想打折?打骨折");}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}
Servlet创建时机:
第一种:服务器加载时创建
优势:
在服务器启动时,就把需要的对象都创建完成了,从而在使用的时候减少了创建对象的时间,提高了首次执行的效率。
弊端:
因为在应用加载时就创建了Servlet对象,因此,导致内存中充斥着大量用不上的Servlet对象,造成了内存的浪费。
第二种:第一次访问时创建
优势:
就是减少了对服务器内存的浪费,因为那些一直没有被访问过的Servlet对象都没有创建,因此也提高了服务器的启动时间。
弊端:
如果有一些要在应用加载时就做的初始化操作,它都没法完成,从而要考虑其他技术实现。
修改创建时机:在标签中添加标签
标签里的数字越小优先级越高,正整数
代表服务器加载时创建,负数
或不写
代表第一次访问的时候创建
<servlet><servlet-name>servletDemo</servlet-name><servlet-class>com.example.demo.ServletDemo</servlet-class><!--数字越小越高,负数或者不写就是第一次访问的时候创建--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>servletDemo</servlet-name><url-pattern>/servletDemo</url-pattern></servlet-mapping>
默认Servlet:
默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下web.xml中。它的映射路径是
<url-pattern>/<url-pattern>
,在发送请求时,首先会在应用中的web.xml中查找映射配置,找到就执行。找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。
ServletConfig:
ServletConfig是Servlet的配置参数对象,在Servlet规范中,允许为每个Servlet都提供一些初始化配置。所以,每个Servlet都一个自己的ServletConfig。它的作用是在Servlet初始化期间,把一些配置信息传递给Servlet。比如servlet是个孩子,ServletConfig就是保姆
生命周期:
由于它是在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同。需要注意的是,如果Servlet配置了1,那么ServletConfig也会在应用加载时创建。
配置ServletConfig:
在
<servlet>
标签中,通过<init-param>
标签配置,有两个子标签
<param-name>:
代表初始化参数的key
<param-value>:
代表初始化参数的value
ServletConfig方法:
返回值 | 方法名 | 说明 |
---|---|---|
String | getInitParameter(String name) | 根据参数名称获取参数的值 |
Enumeration | getInitParameterNames() | 获取所有参数名称的枚举 |
String | getServletName | 获取Servlet的名称 |
SerlvetContext | getServletContext | 获取ServletContext对象 |
演示:
配置信息:
<servlet><servlet-name>servletDemo05</servlet-name><servlet-class>com.example.demo.ServletDemo05</servlet-class><!--配置ServletConfig,是以键值对的形式存在的--><init-param><param-name>itz</param-name><param-value>itzhuzhu</param-value></init-param></servlet><servlet-mapping><servlet-name>servletDemo05</servlet-name><url-pattern>/servletDemo05</url-pattern></servlet-mapping>
实现类:
public class servletDemo04 extends HttpServlet {// 声明ServletConfigprivate ServletConfig config;// 因为ServletConfig是在初始化期间传递信息,所以要在init方法里去获取@Overridepublic void init(ServletConfig config) throws ServletException {// 通过init方法,对ServletConfig对象赋值this.config = config;}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// getInitParameter(String name):根据key获取valueString encodingValue = config.getInitParameter("encoding");System.out.println(encodingValue);// getInitParameterNames():获取所有keyEnumeration<String> keys = config.getInitParameterNames();while (keys.hasMoreElements()) {// 获取每一个keyString key = keys.nextElement();// 根据key获取valueString value = config.getInitParameter(key);System.out.println(key + " : " + value);}// getServletName 获取Servlet的名称String servletName = config.getServletName();System.out.println(servletName);// getServletContext 获取ServletContext对象ServletContext servletContext = config.getServletContext();System.out.println(servletContext);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}
ServletContext:
ServletContext对象,它是应用上下文对象。每一个应用有且只有一个ServletContext对象。
作用:
它可以配置和获取应用的全局初始化参数,实现所有Servlet之间的数据共享。
生命周期:
- 出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都-是单例的)
- 活着:只要应用一直提供服务,对象就一直存在。
- 死亡:应用被卸载(或者服务器挂了),对象死亡。
域对象概念:
- 域对象:指的是对象有作用域(作用范围)
- 域对象的作用:域对象可以实现数据共享。
- 不同作用范围的域对象,共享数据的能力不一样。在Servlet规范中,一共有4个域对象。ServletContext就是其中一个。也是web应用中最大的作用域,叫application域。每个应用只有一个application域(应用域),它可以实现整个应用间的数据共享功能。
ServletContext配置:
ServletContext被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配置。它的配置被称为应用的初始化参数配置。
配置的方式,需要在标签中使用来配置初始化参数
<param-name>:
全局初始化参数的key
<param-value>:
全局初始化参数的value
常用方法:
返回值 | 方法名 | 说明 |
---|---|---|
String | getInitParameter(String name) | 根据名称获取全局配置的参数 |
String | getContextPath() | 获取当前应用访问的虚拟目录 |
String | getRealPath | 根据虚拟目录获取应用部署的磁盘绝对路径 |
应用域常用方法:
返回值 | 方法名 | 说明 |
---|---|---|
void | setAttribute(String name,Object value) | 向应用域对象中存储数据 |
Object | getAttribute(String name) | 通过名称获取应用域对象中的数组 |
void | removeAttribute(String name) | 通过名称移除应用域对象中的数据 |
配置文件:
<!--因为servletContext它不属于某一个servlet,它是配置全局的,所以不能写在servlet里--><context-param><param-name>desc</param-name><param-value>This is Ser vletContextDemo</param-value></context-param><servlet><servlet-name>servletContextDemo</servlet-name><servlet-class>com.example.demo.ServletContextDemo</servlet-class></servlet><servlet-mapping><servlet-name>servletContextDemo</servlet-name><url-pattern>/servletContextDemo</url-pattern></servlet-mapping>
实现类:
public class ServletContextDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取ServletContext对象 , GenericServlet底层已经实现了getInitParameter所以可以直接调用 ServletConfig sc = this.getServletConfig();ServletContext context = getServletContext();//获取全局配置参数的descString value = context.getInitParameter("desc");System.out.println(value);//获取应用的访问虚拟目录String contextPath = context.getContextPath();System.out.println(contextPath);//根据虚拟目录获取应用部署的磁盘绝对路径String realPath = context.getRealPath("/");System.out.println(realPath);// 在src、wabapp、webinf创建三个文件获取路径//获取a.txt文件的绝对路径String a = context.getRealPath("/WEB-INF/classes/a.txt");System.out.println(a);//获取b.txt文件的绝对路径String b = context.getRealPath("/b.txt");System.out.println(b);String c = context.getRealPath("/WEB-INF/c.txt");System.out.println(c);//向域对象中存储数据context.setAttribute("username", "itzhuzhu");//移除域对象中username的数据,删除了再访问就变成null了context.removeAttribute("username");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}
获取共享数据:
public class ServletDemo05 extends HttpServlet {// 声明ServletConfigprivate ServletConfig config;// 因为ServletConfig是在初始化期间传递信息,所以要在init方法里去获取@Overridepublic void init(ServletConfig config) throws ServletException {// 通过init方法,对ServletConfig对象赋值this.config = config;}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// getServletContext 获取ServletContext对象ServletContext servletContext = config.getServletContext();System.out.println(servletContext);// getAttribute(String name) 通过名称获取应用域对象中的数组Object username = servletContext.getAttribute("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}