Servlet 详解
1、什么是 Servlet?
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet,可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
2、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;
} }
第二步:在 web.xml 文件中配置上面创建的 HelloServlet 映射关系
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/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>
第三步:将项目部署在 tomcat 服务器,如何部署请看这篇文章:http://www.cnblogs.com/ysocean/p/6893446.html,然后启动服务器
这里我们项目的结构为:
①、我们直接通过项目名来访问,由于我们在 web.xml 文件中配置了 <welcome-file-list>,那么会依次找下面配置的文件,我们只创建了一个 index.jsp,那么就会访问这个JSP 文件
②、通过在 web.xml 文件中配置的<url-pattern>/hello</url-pattern> 来访问
我们可以看控制台打印内容如下:
如果我们不断的刷新 http://localhost:8080/ServletImprove/hello 这个访问链接,那么控制台如下:
3、Servlet 的生命周期
我们通过上面的实例,可以看到也就是只有第一次才会执行 构造器和 init() 方法,后面每次点击都只调用 service() 方法。那这是为什么呢?
上面这幅图可以这样理解:
1、客户端向 Web 服务器发送请求,服务器查询 web.xml 文件配置。根据请求信息找到对应的 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() 方法
4、创建 Servlet 的三种方法
第一种:就是我们上面写的 实现接口 Servlet
第二种:由于实现接口我们需要实现里面所有的方法,里面有一些方法我们可能并不想实现,那么我们就继承 GenericServlet 类
package com.ys.servlet; import java.io.IOException; import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; 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 类
package com.ys.servlet; import java.io.IOException; import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源
@Override
public void destroy() {
System.out.println("servlet 销毁时调用方法 destroy()...");
} }
其实上面三种方法,后面两种都是对 Servlet 类的封装,我们可以看 API,其实 HttpServlet 是继承 GenericServlet的。
而 GenericServlet 又是实现 Servlet 接口的
5、Servlet 的多线程问题
我们通过 Servlet 的生命周期可以知道,Servlet 类的构造器只会在第一次访问的时候调用,后面的请求都不会再重新创建 Servlet 实例。即 Servlet 是单例,那么既然是单例的,那就要注意多线程访问所造成的安全问题。如下:
package com.ys.servlet; import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; 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);
} }
我们用两个浏览器,输入 http://localhost:8080/ServletImprove/hello,然后一起访问,不断刷新,结果如下:
结果分析:显然,我们用两个浏览器访问,便相当于两个线程,第一个访问,已经执行了 i++,但是还没来得及打印 i 的值,就马上就睡眠了;接着第二个浏览也来访问,执行 i++,那么i的值相当于增加加了两次1,然后这两个浏览器输出最终结果。这便造成了多线程访问共享资源造成冲突。那么如何解决多线程冲突呢?
可以参考这篇文章:如何解决多线程同步问题 http://www.cnblogs.com/ysocean/p/6883729.html
那么在 Servlet 中如何处理呢?
第一种方法:使用同步代码块
package com.ys.servlet; import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; 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
package com.ys.servlet; import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.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 内存模型中,方法中的临时变量是在栈上分配空间,而且每个线程都有自己的私有栈空间,不会造成线程安全问题。
package com.ys.servlet; import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; public class HelloServlet extends GenericServlet{ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
int i = 0;
i++;
//为了使多线程访问安全问题更加突出,我们增加一个延时程序
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
} }
结果:
6、Servlet 和 JSP 的区别
①、JSP 的本质就是 Servlet,JSP 经过编译后就会变为一个类似 Servlet 的Java文件
②、Servlet 基本是JAVA程序代码构成,擅长于流程控制和事务处理,当然也可以用来生成html代码,但是通过Servlet来生成动态网页很不直观.
③、JSP由HTML代码和JSP标签构成,可以方便地编写动态网页,当然里面也可以编写 Java代码,但是整体看上去不够优雅。而且比较麻烦
所以:JSP侧重于视图,Servlet主要用于控制逻辑。
我们可以看一个 JSP 文件,index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
index.jsp
</body>
</html>
经过编译后:很显然下面的代码结构和 Servlet 是差不多的
package org.apache.jsp; import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
} public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
} public void _jspDestroy() {
} public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html; charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out; out.write("\r\n");
out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\tindex.jsp\r\n");
out.write("</body>\r\n");
out.write("</html>");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
JSP 页面的九个隐含对象:
①、request:HttpServletRequest的一个对象,封装请求信息
②、pageContext:页面的上下文,是PageContext的一个对象,可以从该对象中获取其它8个隐含对象。
③、session:代表浏览器和服务器的一次会话,是HttpSession 的一个对象
④、application:代表当前WEB应用,是ServletContext对象
⑤、config:当前JSP对应Servlet的ServletConfig对象
⑥、out:JspWriter对象,调用out.prinln()可以直接把字符串打印到浏览器上
⑦、page:指向当前JSP对应的Servlet对象的应用,但为Object类型,只能调用 Object 类的方法
⑧、exception:在声明了page指令的isErrorPage="true"时,才可以使用
7、Servlet 的转发和重定向
重定向:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.sendRedirect("index.jsp");//重定向
}
转发:
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//response.sendRedirect("index.jsp");
request.getRequestDispatcher("/index.jsp").forward(request, response);//转发
我们再看看浏览器访问:同时输入 http://localhost:8080/ServletImprove/hello
重定向变为:
转发为:
本质区别:转发只发出了一次请求,而重定向发出了两次请求
①.转发:地址栏是初次发出请求的地址
重定向:地址栏不再是初次发出的请求地址,地址栏为最后响应的那个地址
②.转发:在最终的Servlet中,request对象和中转的那个request是同一个对象
重定向:在最终的Servlet中,request对象和中转的那个request不是同一个对象
③.转发:只能转发给当前WEB应用的资源
重定向:可以重定向到任何资源
response.sendRedirect("http://www.baidu.com");是可以的
转发就不行
④.转发:/ 代表的是当前WEB应用的根目录(http://localhost:8080/项目名称/)
重定向: / 代表的是当前WEB站点的根目录(http://localhost:8080/)
注意:这两条跳转语句不能同时出现在一个页面中,否则会报IllegalStateException - if the response was already committed
8、Servlet 的过滤器
①、什么是 过滤器?
JavaWEB 的一个重要组件,可以对发送到 Servlet 的请求进行拦截,并对响应也进行拦截
②、如何实现一个过滤器?
第一步:创建一个过滤器类,实现 Filter 接口
package com.ys.filter; import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; public class HelloFilter implements Filter{
public HelloFilter() {
System.out.println("构造器 HelloFilter()...");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init()...");
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("doFilter()...");
} @Override
public void destroy() {
System.out.println("destroy()...");
} }
第二步:在 web.xml 文件中配置过滤器
<!--给创建的过滤器配置关系 -->
<filter>
<filter-name>helloFilter</filter-name>
<filter-class>com.ys.filter.HelloFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>helloFilter</filter-name>
<url-pattern>/*</url-pattern><!-- 这表示可以拦截任何请求 -->
</filter-mapping>
启动服务器:我们发现还没发送请求,过滤器的 构造方法和 init() 方法就已经开始运行了
服务器启动成功之后,我们输入任意连接,比如
每刷新一次,控制台都会打印 doFilter()...
总结:生命周期和 Servlet 的类似。只不过其构造方法和初始化方法是在容器启动时就调用了,而其 doFilter() 方法则是在每次请求的时候调用。故过滤器可以对请求进行拦截过滤。可以用来进行权限设置,对传输数据进行加密等等操作。
Servlet 详解的更多相关文章
- Servlet详解
原文出处:http://blog.csdn.net/q547550831/article/details/50458456 Servlet详解 基本概述 Session在计算机中,尤其是在网络应用中, ...
- Java Servlet详解(体系结构+注解配置+生命周期)
Java Servlet详解(注解配置+生命周期) 什么是Servlet : (Server applet)? 顾名思义:服务端的小程序 Servlet只是一个接口,定义了Java被浏览器访问到(To ...
- Java Web(一) Servlet详解!!
这篇文章到上一篇,距离的有点遥远呀,隔了大概有两个月把,中间在家过了个年,哈哈~ 现在重新开始拾起,最近在看一本个人觉得很棒的书,<Java Web 整合开发王者归来>,现在写的这一系列基 ...
- (转)Java Web(一) Servlet详解!!
https://www.cnblogs.com/whgk/p/6399262.html 这篇文章到上一篇,距离的有点遥远呀,隔了大概有两个月把,中间在家过了个年,哈哈~ 现在重新开始拾起,最近在看一本 ...
- web开发(一)-Servlet详解
在网上看见一篇不错的文章,写的详细. 以下内容引用那篇博文.转载于<http://www.cnblogs.com/whgk/p/6399262.html>,在此仅供学习参考之用. 一.什么 ...
- JavaWeb笔记一、Servlet 详解
一.创建一个 Servlet 简单实现类 1.创建一个 HelloServlet 类(测试 Servlet 接口方法) 1 //创建一个 HelloServlet 类并实现 Servlet 接口 2 ...
- jsp/servlet学习二之servlet详解
Servlet API概览 Servlet API有一下四个java包: 1,javax.servlet,其中包含定义servlet和servlet容器之间契约的类和接口. 2,javax.servl ...
- Java Web(二) Servlet详解
什么是Servlet? Servlet是运行在Web服务器中的Java程序.Servlet通常通过HTTP(超文本传输协议)接收和响应来自Web客户端的请求.Java Web应用程序中所有的请求-响应 ...
- 04_web基础(四)之servlet详解
16.17.18.servlet生命周期 javax.servlet.Servlet接口方法:public String getServletInfo():获取Servlet相关信息(作者,版权,版本 ...
随机推荐
- qt5的.ui文件在VS2010中无法编译问题
自己手动添加的.ui文件在VS中是无法右键编译的,也即是说,在用QT designer编辑过的.ui文件无法实时更新相应的ui_XX.h文件,造成调试结果无法显示编辑过的新界面. 解决办法: 右键.u ...
- window server2012 许可证过期
研发的服务器装得windows server 2012 Standard ,许可证只有半年使用时间,过期了老是自动关机,于是在网上找了下,最终找了个可以用的方法,记录下,留用 步骤: 1.cmd命令打 ...
- XMLHttpRequest API 使用指南
一.实例化 XMLHttpRequest 对象 使用 Ajax API 的第一件事情就是实例化 XMLHttpRequest 对象. var xhr = new XMLHttpRequest(); 二 ...
- STM32固件库文件分析
STM32固件库文件分析 1.汇编编写的启动文件 startup/stm32f10x.hd.s:设置堆栈指针,设置pc指针,初始化中断向量,配置系统时钟,对用c库函数_main最后去c语言世界里. 2 ...
- css 实现旋转八卦图
虽然这不算什么亮点,不过也可以供路上的小伙伴学习下 直接上干货: <!doctype html> <html lang="en"> <head> ...
- Redis技术分享
环境介绍: 开发环境: spring3+tomcat7+maven3+redis-3.0.7 运行环境: Linux 前言: 项目中引入redis背景: 项目中最初将科目.打印.利润表.资产负债表.现 ...
- 【stm32】时钟树解析
有时候会突然忘了这个重要的时钟树,这里转载一个比较好的,以防忘记. STM32时钟系统 在STM32中,有五个时钟源,为HSI.HSE.LSI.LSE.PLL. ①HSI是高速内部时钟,RC振荡器,频 ...
- elasticsearch基础概念
接近实时(NRT) Elasticsearch是一个接近实时的搜索平台.这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒). 集群(clu ...
- Excel基本操作1
Excel的基本操作之二,录入及快速填充.不足之处,欢迎补充
- python结合shell脚本实现简单的日常集中巡检
一.环境配置 1.说明 下面的安装过程适合开发.调试Python脚本,如果是直接使用的话没有这么复杂.为了防止由于版本问题导致安装问题,请到http://pan.baidu.com/s/1nt1NKS ...