Javaweb的三大组件之-Servlet
文章目录
概述
Servlet是JavaWeb三大组件之一,它是我们学习JavaWeb最为基本的组件,也就是说一定要100%的掌握它
其它两种:Filter(拦截器)、Listener(观察者模式),后续讲解
Servlet,即Server Let的意思,用来处理用户请求,当客户端发出请求后,由Tomcat去找到可以处理这一请求的Servlet来处理
也就是说,用户的请求是由Servlet来处理的!例如用户发出登录请求,那么就应该由处理登录的Servlet来处理;用户发出登录请求,那么就应该有登录Servlet来处理
Servlet实现
servlet 是运行在 Web 服务器中的小型 Java 程序。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。也就是说,Servlet是由我们自己来完成的!但Servlet一定要实现javax.servlet.Servlet接口,并且还要在web.xml文件中部署!不然Tomcat是找不到我们写的Servlet的
Servlet接口
javax.servlet.Servlet接口中方法如下:
void init(ServletConfig servletConfig)
:当Tomcat创建Servlet实例后,马上调用init()方法。这个方法只在创建后调用一次!用来做Servlet初始化工作!一个Servlet实例只被创建一次,所以init()方法也只被调用一次!(本方法编写对Servlet的初始化代码)void service(ServletRequest request, ServletResponse response):
Servlet实例在每次处理请求时都调用service()方法。void destroy()
:当Tomcat要销毁Servlet实例时,会先调用destroy()方法,再销毁它。所谓销毁Servlet,其实就是在Servlet缓存池中把Servlet移除!一般只有Tomcat关闭时,才会销毁Servlet实例!ServletConfig getServletConfig():
这个方法返回ServletConfig对象,但我们不能自己去创建ServletConfig对象,所以一般我们会在init()方法中把init()方法的参数保存起来,然后再在本方法中返回它。ServletConfig对象对应web.xml中当前Servlet实例的配置信息。String getServletInfo():
这个方法只是返回一个字符串,用来说明当前Servlet。基本没用!
实现:
- 第一步:src下建立class、实现Servlet的接口,实现接口中的方法
import javax.servlet.*;
import java.io.IOException;
public class testServlet 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 {
//以字节流的形式写入页面
servletResponse.getOutputStream().write(" i am login..".getBytes());
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
- 第二步:配置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>login</servlet-name>
<servlet-class>testServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
</web-app>
第三步,启动Tomcat,浏览器输入http://localhost:8081/login
成功访问到servlet
JavaWeb请求响应原理如下:
当Tomcat接收到请求http://localhost:8081/login
后,Tomcat会找到项目中的web.xml文件,然后通过login这个请求路径,查找处理这个请求的Servlet类型,这刚好与<url-pattern>/login</url-pattern>
匹配,说明存在一个可以通过这个请求的Servlet,然后再通过<url-pattern>/login </url-pattern>
查找到<servlet-name>login</servlet-name>
,然后再通过<servlet-name>login </servle-name>
查找到<servlet-class>testServlet</servlet-class>
。这时Tomcat已经得到了一个Servlet类名字(一个字符串而已)
omcat通过Servlet类名字去查找内存中是否存在Servlet对象,如果存在,那么就不用再去创建,直接获取这个Servlet实例,调用它的service()方法完成请求
如果这个Servlet不存在,那么Tomcat会通过反射来创建Servlet实例,并把Servlet实例存放到Servlet池中,再去调用Servlet的service方法处理请求
Servlet生命周期
Servlet对象的实例默认情况下是在浏览器第一次调用servlet时候被创建的(可以修改其创建时机后续讲解)
Servlet的实例不由我们创建,Servlet的方法不由我们来调用,这一切都是由Tomcat来完成,这就是说由Tomcat来管理Servlet,而我们只需要去编写Servlet实现类,并将其部署到web.xml文件中去
提醒:只有这三个方法(init()、service()、destroy()
)是生命周期中的方法。也就是说,生命周期方法会被Tomcat在不同的时间点来调用!而其它方法就不会被调用了!!!如果你在自己写的Servlet中添加了其他方法,那么Tomcat也是不会去调用它们的!但你可以让生命周期方法去调用你自己写的方法就OK了
HttpServlet
因为现在我们的请求都是基于HTTP协议的,所以我们应该专门为HTTP请求写一个Servlet做为通用父类
对于专注于HTTP的Servlet,我们需要处理以下几个问题:
service()
方法的参数ServletRequest和ServletResponse
,因为所有的请求都是HTTP请求,所以传递给service()
就去的参数其实是HttpServletRequest和HttpServletResponse对象
。如果子类希望使用的是HttpServletRequest,而不是ServletRequest,那么它需要自己去做强转
Servlet:一个标准
GenericServlet:是Servlet接口子类
HttpServlet:与协议相关的Servlet
实现HttpServlet,只需要重写doGet和doPost就行
根据请求方式的不同,调用doGet方法或doPost方法
代码示例:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getOutputStream().write(" doGet方法被调用".getBytes());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getOutputStream().write(" doPost方法被调用".getBytes());
}
}
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>HttpServlet</servlet-name>
<servlet-class>TestHttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HttpServlet</servlet-name>
<url-pattern>/httpservlet</url-pattern>
</servlet-mapping>
</web-app>
效果:
Tomcat启动时调用Servlet
有些Servlet需要在Tomcat启动时就被创建,而不是第一次访问时被创建,那么可以在web.xml文件中配置< servlet >
元素,添加子元素< load-on-startup>
元素,里面的数值必须大于0
<servlet>
<servlet-name>One</servlet-name>
<servlet-class>com.rl.servlet.OneServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet>
<servlet-name>Two</servlet-name>
<servlet-class> com.rl.servlet.TwoServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
-
所有添加了
< load-on-startup>
子元素的Servlet,都会在Tomcat启动时被创建!当然,只是被创建,但没有处理请求 -
< load-on-startup>
元素的值是一个序号,Tomcat会使用这个序号给多个Servlet排序!然后在Tomcat启动时会按这个顺序来创建Servlet实例对象
ServletConfig
ServletConfig对象对应web.xml文件中的< servlet>元素,例如,你想获取当前Servlet在web.xml文件中的配置名,那么可以使用servletConfig.getServletName()
方法获取
例如
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println(config.getServletName());
}
你不能自己去创建ServletConfig对象,Servlet的init()方法的参数就是ServletConfig类型的。Tomcat在调用init()方法时,会传递ServletConfig对象。你可以在init()方法中使用它
添加初始化参数:
<servlet>
<servlet-name>One</servlet-name>
<servlet-class>
OneServlet
</servlet-class>
<init-param>
<param-name>paramName1</param-name>
<param-value>paramValue1</param-value>
</init-param>
<init-param>
<param-name>paramName2</param-name>
<param-value>paramValue2</param-value>
</init-param>
</servlet>
配置中加了两个初始化参数,第一个参数的名称为paramName1,第一个参数的值为paramValue1;第二个参数的名称为paramName2,第二个参数值为paramValue2
获取实例
System.out.println(config.getInitParameter("paramName1"));
Servlet路径映射
这是关于Url-Pattern的配置
<servlet-mapping>
<servlet-name>HttpServlet</servlet-name>
<url-pattern>/httpservlet</url-pattern>
</servlet-mapping>
-
完全路径匹配 以/开头 例如 /aaa /aaa/bbb
-
目录匹配(通配符匹配) 以/开头 例如 /aaa/* /*
例如,我们把路径映射设置如下
<servlet-mapping>
<servlet-name>HttpServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
启动tomcat,访问http://localhost:8081/
或者访问http://localhost:8081/a/b(a/b不存在)
都可以被调用
- 扩展名匹配 不能以/开头 例如 *.do *.action …
优先级: 完全路径匹配 > 目录匹配 > 扩展名匹配
经典错误: /*.do
ServletContext(容器)
WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象
一般应用:
获取WEB应用的全局初始化参数,通过ServletContext域对象实现数据共享
来源:
调用:
拿到域对象之后可以做的事情:
…
核心方法:
getRealPath获得路径问题
获取根目录:
是从当前servlet 在tomcat 中的存放文件夹开始计算起的(一般称根目录)
@Override
public void init(ServletConfig config) throws ServletException {
ServletContext context = config.getServletContext();
//"/"代表根目录
String path = context.getRealPath("/");
System.out.println(path);
}
- /代表根目录
- 此方法不会判断文件是否真实存在,故路径要写对(文件所在的位置按照根目录(/)后的目录层级写入)
读取文件信息:
@Override
public void init(ServletConfig config) throws ServletException {
ServletContext context = config.getServletContext();
//"/"代表根目录
System.out.println(context.getRealPath("/"));
/*先从本地根目录文件夹下找到test.properties文件,确定好根目录后的路径,再确定参数*/
InputStream in = context.getResourceAsStream("/WEB-INF/classes/test.properties") ;
Properties pro = new Properties() ;
try {
pro.load(in);
System.out.println(pro.get("key"));
} catch (IOException e) {
e.printStackTrace();
}
}
单例的Servlet
因为Servlet实例是由Tomcat来创建的,但Tomcat只会创建一个Servlet实例,所以Servlet就是单例的!这与我们自己写的单例模式不太一样。因为这种单例是通过容器来管理而实现的!
一个实例需要在同一个时间点上处理多个请求!
同步就是安全,但效率太低!
Servlet是线程不安全的!
所以注意:
单例的servlet的属性的公共的,在不加锁的情况下,线程不安全,故
-
不写属性;
-
不写可以存储数据的属性!