Servlet
Servlet的介绍
-
Servlet是运行在java服务器端的程序,用于接收和响应来自客户端基于HTTP协议的请求
-
如果想要实现Servlet的功能,可以通过java.servlet.Servlet接口或者继承它的实现类
-
核心方法:service(),任何客户端请求都会经过该方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivky8108-1642497460852)(/Users/heroma/Library/Application Support/typora-user-images/image-20220116223918388.png)]
Servlet执行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q5MJySZr-1642497460853)(/Users/heroma/Library/Application Support/typora-user-images/image-20220116220633851.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b0NU73ke-1642497460854)(/Users/heroma/Library/Application Support/typora-user-images/image-20220116221238241.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69jdbkb9-1642497460854)(/Users/heroma/Library/Application Support/typora-user-images/image-20220116221312205.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eVlpb5a0-1642497460855)(/Users/heroma/Library/Application Support/typora-user-images/image-20220116224819646.png)]
Servlet关系视图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0nWfZFBH-1642497460856)(/Users/heroma/Library/Application Support/typora-user-images/image-20220116225039234.png)]
Servlet实现方式
- 第一种
- 实现Servlet接口,实现所有的抽闲方法。该方法支持最大程度的自定义
- 第二种
- 继承GenericServlet抽象类,必须重写service方法,其他方法可以选择重写。该方式让我们开发Servlet变得简单
- 第三种
- 继承HttpServlet抽象类,需要重写doGet和doPost方法。该方法表示请求和相应都需要和HTTP协议相关
Servlet生命周期
- 对象的生命周期,就是对象从出生到死亡的过程。即:出生->活着->死亡。即对象创建到销毁的过程
- 出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,将对象存放到内存中
- 活着:服务器提供服务的整个过程中,该对象一直存在,每次都是在执行service方法
- 死亡:当服务器停止时,或者服务器宕机时,对象死亡
Servletlet线程安全问题
- 由于Servlet采用的是单例模式,也就是整个应用中只有一个实例对象。所以我们需要分析这个唯一的实例对象中的类成员是否线程安全
- 模拟用户登录功能来查看Servlet线程是否安全
- 模拟用户登录时,假设一个浏览器代表一个线程,那么多个浏览器代表多个线程。我们期望每个浏览器看到的应该是自己的用户名,但结果却是数据混乱。因此,Servlet是线程不安全的
- 解决:定义类成员要谨慎。如果是共用的,并且只在初始化时赋值的话,那么是没有问题的。如果不是共享的,或者每次使用都有可能对其赋值,就需要考虑线程安全问题,可以将doGet或者doPost方法内使用同步功能即可
Servlet映射方式
-
具体名称的方式。访问的资源路径必须和映射配置完全相同
-
/开头+通配符的方式。只要符合项目结构即可,不用考虑结尾是什么
-
通配符+固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径
//指定名称 <!-- servlet声明--> <servlet> <servlet-name>Demo04</servlet-name> <servlet-class>hero.mps.web_demo02.servlet.Demo04</servlet-class> </servlet> <!-- servlet映射--> <servlet-mapping> <servlet-name>Demo04</servlet-name> <url-pattern>/Demo04</url-pattern> </servlet-mapping> //开头+通配符的方式。只要符合项目结构即可,不用考虑结尾是什么 <!-- servlet声明--> <servlet> <servlet-name>Demo04</servlet-name> <servlet-class>hero.mps.web_demo02.servlet.Demo04</servlet-class> </servlet> <!-- servlet映射--> <servlet-mapping> <servlet-name>Demo04</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping> //通配符+固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径 <!-- servlet声明--> <servlet> <servlet-name>Demo04</servlet-name> <servlet-class>hero.mps.web_demo02.servlet.Demo04</servlet-class> </servlet> <!-- servlet映射--> <servlet-mapping> <servlet-name>Demo04</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
优先级,越具体优先级越高 第一种-> 第二种 -> 第三种
Servlet多路径映射
-
可以给Servlet配置多个访问路径映射,从而根据不同的请求路径实现不同的功能
-
场景分析:
如果访问路径是/vip 商品价格打九折
如果访问路径是/vvip 商品价格打五折
如果访问路径是其他 商品价格不打折
package hero.mps.web_demo02.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Servlet多路径 */ public class Demo06 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //定义一个金额 int money = 100; //获取访问路径 String uri = req.getRequestURI(); String path = uri.substring(uri.lastIndexOf("/")); //进行判断 if ("/vip".equals(path)){ System.out.println("商品原价:" + money + ".优惠后价格:" + money*0.9); }else if ("/vvip".equals(path)){ System.out.println("商品原价:" + money + ".优惠后价格:" + money*0.5); }else { System.out.println("商品原价:" + money + ".优惠后价格:" + money); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } } /*servlet配置*/ <servlet> <servlet-name>Demo06</servlet-name> <servlet-class>hero.mps.web_demo02.servlet.Demo06</servlet-class> </servlet> <!-- servlet映射--> <servlet-mapping> <servlet-name>Demo06</servlet-name> <url-pattern>/hero/*</url-pattern> </servlet-mapping>
Servlet创建时机
- 第一次访问是创建
- 优势:减少服务器内存的浪费。提高服务器启动效率
- 弊端:如果有一些要在应用加载时做得初始化操作,无法完成
- 服务器加载时创建
- 优势:提前创建好对象,提高了首次运行的效率。可以完成一些应用加载时需要的初始化操作
- 弊端:对服务器内存占用较多,影响了服务器启动的效率
- 修改Servlet创建时机。在标签中,添加标签
//正整数代表服务器加载时创建,值越小,优先级越高。负整数或者不写代表第一次访问时创建
<load-on-startup>1</load-on-startup>
默认Servlet
- 默认servlet是由服务器提供的一个Servlet。它配置在Tomcat的conf目录的web.xml中
- 它的映射路径是/,我们在发送请求时,首先会在我们项目中的web.xml中查找映射噢诶之,找到则执行。当找不到时,就去找默认的servlet,由默认的servlet处理。多以,一切都是servlet
ServletConfig介绍
- ServletConfig是Servlet的配置参数对象,在Servlet的规范中,允许为每一个Servlet都提供一些初始化的配置。所以,每一个Servlet都有一个自己的ServletConfig
- 作用:在Servlet初始化时,把一些配置信息传递个诶Servlet
- 声明周期:和Servlet相同
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8U5AGrTy-1642497460857)(/Users/heroma/Library/Application Support/typora-user-images/image-20220118142805385.png)]
ServletConfig配置方式
- 在标签中,通过标签来配置。有两个子标签
- :代表初始化参数的key
- :代表初始化参数的value
<servlet>
<servlet-name>Demo06</servlet-name>
<servlet-class>hero.mps.web_demo02.servlet.Demo06</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</servlet>
<!-- servlet映射-->
<servlet-mapping>
<servlet-name>Demo06</servlet-name>
<url-pattern>/hero/*</url-pattern>
</servlet-mapping>
ServletConfig常用方法
返回值 | 方法名 | 说明 |
---|---|---|
String | getInitParameter(String name) | 根据参数名获取参数值 |
Enumeration | getInitParameterNames() | 获取所有参数名称的枚举 |
String | getServletName() | 获取Servlet名称 |
ServletContext | getServletContext() | 获取ServletContext对象 |
package hero.mps.web_demo02.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
/**
* Servlet多路径
*/
public class Demo06 extends HttpServlet {
// 1.声明servletconfig
private ServletConfig servletConfig;
// 2.通过init方法来对servletConfig赋值
@Override
public void init(ServletConfig config) throws ServletException {
this.servletConfig = config;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String encoding = servletConfig.getInitParameter("encoding");
System.out.println(encoding);
Enumeration<String> names = servletConfig.getInitParameterNames();
while (names.hasMoreElements()){
String el = names.nextElement();
String value = servletConfig.getInitParameter(el);
System.out.println(el+":" + value);
}
String servletName = servletConfig.getServletName();
System.out.println(servletName);
ServletContext servletContext = servletConfig.getServletContext();
System.out.println(servletContext);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<!-- servlet声明-->
<servlet>
<servlet-name>Demo06</servlet-name>
<servlet-class>hero.mps.web_demo02.servlet.Demo06</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</servlet>
<!-- servlet映射-->
<servlet-mapping>
<servlet-name>Demo06</servlet-name>
<url-pattern>/hero/*</url-pattern>
</servlet-mapping>
ServletContext介绍
- ServletContext是应用上下文对象(应用域对象)。每一个应用中只有一个ServletContext对象
- 作用:可以配置和获得相应的全局初始化参数,可以实现Servlet之间的数据共享
- 生命周期:应用一加载则创建,应用停止则销毁
- 域对象指的是对象有作用域。即作用范围。域对象可以实现数据的共享。不同作用范围的域对象,共享数据的能力也不一样
- 在Servlet规范中,一共有四个域对象。ServletContext就是其中一个。他是web应用中最大的作用域,也叫application域。它可以实现整个应用之间的数据共享
ServletContext配置方式
- 在标签中。通过标签来配置。有两个子标签
- :代表全局初始化参数的key
- :代表全局初始化参数的value
<context-param>
<param-name>globalEncoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
ServletContext常用方法
返回值 | 方法名 | 说明 |
---|---|---|
String | getInitParameter(String name) | 根据名称获取全局配置的参数 |
String | getContextPath() | 获取当前应用的访问虚拟目录 |
String | getRealPath(String path) | 根据虚拟目录获取部署的磁盘绝对路径 |
public class ServletContextDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取ServletContext对象
ServletContext context = getServletContext();
//获取全局配置的globalEncoding
String value = context.getInitParameter("globalEncoding");
System.out.println(value);
//获取应用的访问虚拟目录
String contextPath = context.getContextPath();
System.out.println(contextPath);
//根据虚拟目录获取应用部署的磁盘绝对路径
//获取b.txt文件的绝对路径 获取webapp目录下的b.txt
String b = context.getRealPath("/b.txt");
System.out.println(b);
//获取c.txt文件的绝对路径 WEB-INF下的c.txt
String c = context.getRealPath("/WEB-INF/c.txt");
System.out.println(c);
//获取a.txt文件的绝对路径 对应src下文件a.txt
String a = context.getRealPath("/WEB-INF/classes/a.txt");
System.out.println(a);
//向域对象中存储数据
context.setAttribute("username","zhangsan");
//移除域对象中username的数据
//context.removeAttribute("username");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
返回值 | 方法名 | 说明 |
---|---|---|
void | setAttribute(String name, Object value) | 向应用对象中存储数据 |
Object | getAttribute() | 通过名称获取应用作用域对象中的数据 |
void | removeAttribute(String name) | 通过对象名称移除应用域对象中的数据 |
注解开发Servlet
自动注解开发的实现步骤
- 创建一个web项目
- 定义一个类,继承HttpServlet
- 重写doGet和doPost方法
- 在类上使用@WebServlet注解配置servlet
- 部署并启动项目
- 通过浏览器进行测试
package com.example.web_demo04;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
private String message;
public void init() {
message = "Hello World!";
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println(message);
}
public void destroy() {
}
}
自动注解开发详解
//
// 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 {
//指定servlet名称。等同于web.xml文件下<servlet>标签下的<servlet-name>
String name() default "";
//用于映射Servlet,等效于<url-pattern>
String[] value() default {};
String[] urlPatterns() default {};
//Servlet的加载机制,等效于<load-on-startup>
int loadOnStartup() default -1;
//Servlet初始化参数,等效于<init-param>
WebInitParam[] initParams() default {};
//Servlet是否支持异步
boolean asyncSupported() default false;
//Servlet小图标
String smallIcon() default "";
//Servlet大图标
String largeIcon() default "";
//Servlet的描述信息
String description() default "";
//Servlet显示名称
String displayName() default "";
}