java-Servlet详解
java-Servlet 详解
简介:
Java Servlet 是运⾏在 Web 服务器或应⽤服务器上的程序,它是作为来⾃ Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应⽤程序之间的中间层。使⽤ Servlet,可以收集来⾃⽹页表单的⽤户输⼊,呈现来⾃数据库或者其他源的记录,还可以动态创建⽹页。
Servlet ⼊门实例
第⼀步:创建⼀个JavaWeb项⽬,并创建⼀个servlet类-----HelloServlet,实现接⼝ Servlet
package com.ys.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class HelloServlet implements Servlet{
//只被调⽤⼀次,第⼀次请求Servlet时,创建Servlet的实例,调⽤构造器
public HelloServlet() {
System.out.println("构造器 HelloServelt()...");
}
//该⽅法⽤于初始化Servlet,就是把该Servlet装载⼊内存
//只被调⽤⼀次,在创建好实例后⽴即被调⽤
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("初始化⽅法 init()...");
}
//被多次调⽤,每次请求都会调⽤service⽅法。实际⽤于响应请求的
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("执⾏⽅法主体 service()...");
}
//只被调⽤⼀次,在当前Servlet所在的WEB应⽤被卸载前调⽤,⽤于释放当前Servlet所占⽤的资源
@Override
public void destroy() {
System.out.println("servlet 销毁时调⽤⽅法 destroy()...");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
第⼆步:在 l ⽂件中配置上⾯创建的 HelloServlet 映射关系
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="/2001/XMLSchema-instance"
xmlns="java.sun/xml/ns/javaee"
xsi:schemaLocation="java.sun/xml/ns/javaee
java.sun/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<!--在tomcat 服务器中运⾏时,如果不指名访问⽂件名,默认的根据项⽬名访问⽂件顺序如下配置 --> <welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!--给创建的 Servlet 配置映射关系 -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.ys.servlet.HelloServlet</servlet-class>
<!--servlet的完整名称-->
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<!-- 与上⾯配置的 servlet-name 名字要对应,⼀个servlet可以有多个 servlet-mapping -->
<url-pattern>/hello</url-pattern>
<!--访问路径-->
</servlet-mapping>
</web-app>
⽅式⼆、不使⽤l,使⽤注解⽅式配置路由
/**
* 注解WebServlet⽤来描述⼀个Servlet
* 属性name描述Servlet的名字,可选
* 属性urlPatterns定义访问的URL,或者使⽤属性value定义访问的URL.(定义访问的URL是必选属性)
*/
@WebServlet(name="ServletDemo",urlPatterns="/ServletDemo")
public class Myservlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
第三步:将项⽬部署在 tomcat 服务器,然后启动服务器
Servlet 的⽣命周期
我们通过上⾯的实例,可以看到也就是只有第⼀次才会执⾏构造器和 init() ⽅法,后⾯每次点击都只调⽤ service() ⽅法。那这是为什么呢?
上⾯这幅图可以这样理解:
1、客户端向 Web 服务器发送请求,服务器查询 l ⽂件配置。根据请求信息到对应的 Servlet。
2、Servlet 引擎检查是否已经装载并创建了该 Servlet 的实例对象,如果有,则直接执⾏第4步,否则执⾏第
3步,
3、Web 服务器加载 Servlet,并调⽤ Servlet 构造器(只会调⽤⼀次),创建 Servlet 的实例对象。并调⽤
init() ⽅法,完成 Servlet 实例对象的初始化(只会调⽤⼀次)。
4、Web 服务器把接收到的 http 请求封装成 ServletRequest 对象,并创建⼀个响应消息的
ServletResponse 对象,作为 service() ⽅法的参数传⼊。(每⼀次访问都会调⽤⼀次该⽅法)
5、执⾏ service()⽅法,并将处理信息封装到 ServletResponse 对象中返回
6、浏览器拆除 ServletResponse 对象,形成 http 响应格式,返回给客户端。
7、Web 应⽤程序停⽌或者重新启动之前,Servlet 引擎将卸载 Servlet实例,并在卸载之前调⽤ destory() ⽅
法
创建 Servlet 的三种⽅法
第⼀种:就是我们上⾯写的实现 Servlet 接⼝
第⼆种:由于实现接⼝我们需要实现⾥⾯所有的⽅法,⾥⾯有⼀些⽅法我们可能并不想实现,那么我们就继
承 GenericServlet 类
public class HelloServlet extends GenericServlet{
//只被调⽤⼀次,第⼀次请求Servlet时,创建Servlet的实例,调⽤构造器
public HelloServlet() {
System.out.println("构造器 HelloServelt()...");
}
//该⽅法⽤于初始化Servlet,就是把该Servlet装载⼊内存
//只被调⽤⼀次,在创建好实例后⽴即被调⽤
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("初始化⽅法 init()...");
}
//被多次调⽤,每次请求都会调⽤service⽅法。实际⽤于响应请求的
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("执⾏⽅法主体 service()...");
}
//只被调⽤⼀次,在当前Servlet所在的WEB应⽤被卸载前调⽤,⽤于释放当前Servlet所占⽤的资源
@Override
public void destroy() {
System.out.println("servlet 销毁时调⽤⽅法 destroy()...");
}
}
第三种:通常我们浏览器发出的请求都是 http 请求,那么请求⽅式可能有多种,⽐如 get,post,⽽我们在处理请求的时候都是在 service() ⽅法中,这种⽅式显然不够明确。那么我们通常是继承 HttpServlet 类
public class HelloServlet extends HttpServlet{
//只被调⽤⼀次,第⼀次请求Servlet时,创建Servlet的实例,调⽤构造器
public HelloServlet() {
System.out.println("构造器 HelloServelt()...");
}
//该⽅法⽤于初始化Servlet,就是把该Servlet装载⼊内存
//只被调⽤⼀次,在创建好实例后⽴即被调⽤
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("初始化⽅法 init()...");
}
//处理 post 请求
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
//处理get请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
servlet和tomcat的关系//只被调⽤⼀次,在当前Servlet所在的WEB应⽤被卸载前调⽤,⽤于释放当前Servlet所占⽤的资源
@Override
public void destroy() {
System.out.println("servlet 销毁时调⽤⽅法 destroy()...");
}
}
Servlet 的多线程问题
我们通过 Servlet 的⽣命周期可以知道,Servlet 类的构造器只会在第⼀次访问的时候调⽤,后⾯的请求都不会再重新创建 Servlet 实例。即 Servlet 是单例,那么既然是单例的,那就要注意多线程访问所造成的安全问题。如下:public class HelloServlet extends GenericServlet{
//多线程共享资源
private int i = 0;
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
i++;
/
/为了使多线程访问安全问题更加突出,我们增加⼀个延时程序
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
结果分析:显然,我们⽤两个浏览器访问,便相当于两个线程,第⼀个访问,已经执⾏了 i++,但是还没来得及打印 i 的值,就马上就睡眠了;接着第⼆个浏览也来访问,执⾏ i++,那么i的值相当于增加加了两次1,然后这两个浏览器输出最终结果。这便造成了多线程访问共享资源造成冲突。那么如何解决
多线程冲突呢?
第⼀种⽅法:使⽤同步代码块
public class HelloServlet extends GenericServlet{
//多线程共享资源
private int i = 0;
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
synchronized (this) {
i++;
//为了使多线程访问安全问题更加突出,我们增加⼀个延时程序
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
结果:
分析:这种办法虽然能解决多线程同步问题,但是如果延时程序特别长,那么会造成访问假死的现象。即第⼀个线程访问结果没有出来,第⼆个线程就会⼀直卡死,出不来结果
第⼆种办法:实现接⼝ SingleThreadModel
public class HelloServlet extends GenericServlet implements SingleThreadModel{
//多线程共享资源
private int i = 0;
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
i++;
//为了使多线程访问安全问题更加突出,我们增加⼀个延时程序
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
分析:SingleThreadModel 接⼝指定了系统如何处理对同⼀个Servlet的调⽤。如果⼀个Servlet被这个接⼝指定,那么在这个Servlet中的service⽅法将不会有两个线程被同时执⾏,当然也就不存在线程安全的问题。但是,如果⼀个Servlet实现了SingleThreadModel接⼝,Servlet引擎将为每个新的请求创建⼀个单独的Servlet实例,这将引起⼤量的系统开销,在现在的Servlet开发中基本看不到SingleThreadModel的使⽤,这种⽅式了解即可,尽量避免使⽤。
第三种办法:避免使⽤实例变量
线程安全问题很⼤⼀部分是由于实例变量造成的,那么我们只要在 Servlet ⾥⾯不定义任何的实例变量,那么就不会有线程安全的问题。因为在 Java 内存模型中,⽅法中的临时变量是在栈上分配空间,⽽且每个线程都有⾃⼰的私有栈空间,不会造成线程安全问题。
public class HelloServlet extends GenericServlet{
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
int i = 0;
i++;
//为了使多线程访问安全问题更加突出,我们增加⼀个延时程序
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论