Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
Servlet是JavaWeb的三(Servlet、Filter、Listener)大组件之一,它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
① 接收请求数据;
② 处理请求;
③ 完成响应。
例如客户端发出登录请求,或者输出注册请求,这些请求都应该由Servlet来完成处理!Servlet需要我们自己来编写,每个Servlet必须实现javax.servlet.Servlet接口。
图示:
① 实现javax.servlet.Servlet接口;
② 继承javax.servlet.GenericServlet类;
③ 继承javax.servlet.http.HttpServlet类;
通常我们会去继承HttpServlet类来完成我们的Servlet,但学习Servlet还要从javax.servlet.Servlet接口开始学习。
问题:有人就要问了,为什么开发Servlet有三种方法?
回答:应为Servlet这个技术不是一开始出现就那么成熟,他经历一个发展的阶段。
Servlet --> GenericServlet --> HttpServlet
① C/S图示:
总结:
B/S的优势:
a.开发成本低
b.管理维护简单
c.产品升级便利
d.对用户培训费用低
e.用户使用方便,出现错误的概率低。
B/S的不足:
a.安全性不足
b.客户端不能随心变化,受浏览器的限制
我们开始第一个Servlet应用吧!首先在webapps目录下创建helloservlet目录,它就是我们的应用目录了,然后在helloservlet目录中创建准备JavaWeb应用所需内容:
① 创建/helloservlet/WEB-INF目录;
② 创建/helloservlet/WEB-INF/classes目录;
③ 创建/helloservlet/WEB-INF/lib目录;
④ 创建/helloservlet/WEB-INF/web.xml文件;
Eclipse创建项目:(实战)
避免出现乱码:
1、整个web项目编码utf-8;
2、创建数据库utf-8;
3、Servlet设置字符编码;
4、Jsp页面设置utf-8;
5、Tomcat配置utf-8;(URIEncoding="UTF-8")
接下来我们开始准备完成Servlet,完成Servlet需要分为两步:
⑤ 编写Servlet类;
⑥ 在web.xml文件中配置Servlet;
案列:01_Servlet1(实现Servlet接口)
用接口Servlet来编写一个Servlet(介绍Servlet的生命周期,该Servlet完成一个简单的功能,想浏览器相应一条信息“hello world”)
eclipse创建动态项目
创建Hello类实现implements Servlet
Servlet类文件:
package com.hqyj.servlet.test;
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 Servlet1 implements Servlet {
// 销毁Servlet实例,释放内存。
// 什么时候调用三种情况
// 1、reload 该servlet(webApps)
// 2、关闭tomcat
// 3、关闭电脑
@Override
public void destroy() {
System.out.println("----destroy------");
}
// 得到Servlet配置文件
@Override
public ServletConfig getServletConfig() {
System.out.println("----getServletConfig------");
return null;
}
// 得到Servlet信息
@Override
public String getServletInfo() {
System.out.println("----getServletInfo------");
return null;
}
// 该函数用于初始化Servlet,类似于类的构造函数。
// 该 只会被调用一次,当用户第一次访问该Servlet时被调用。
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("----init-----");
}
// 用于处于业务逻辑
// 程序员应该把业务逻辑代码写在这里
// 当用于每访问该类的时候,都会被调用
// request 用于获得客户端的信息(浏览器)
// response 用于向客户端返回(服务器)
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("----service------");
}
}
配置Web.xml文件
Servlet1
com.hqyj.servlet.test.Servlet1
Servlet1
/servlet1
总结:
Servlet中的方法大多数不是由我没来调用,而是由Tomcat来调用,并且Servlet对象也不是由我们来创建的,而是由Tomcat来创建,并由服务器来调用相应的方法。
单列,一个类只有一个对象;一个应用中当然可以存在多个Servlet类。
线程不安全,但它的效率是最高的。
Servlet部署在容器里(应用服务器Tomcat,其他应用服务器,比如jboss,weblogic…),它的生命周期由容器来管理。
Servlet的生命周期分为以下几个阶段:(重点)
1.装载Servlet,由相应的容器来完成
2.创建一个Servlet实例
3.调用Servlet的init()方法,该方法只会在第一次访问Servlet时被调用一次
4.服务:调用Servlet的service()方法,一般业务逻辑在这里处理,该方法在访问该Servlet时,会被调用
5.销毁:调用Servlet的destroy()方法,销毁该Servlet实例,该方法在以下情况被调用:
a.tomcat重新启动
b.reload该webapps
c.重新启动电脑
图1:
图2:
① ServletConfig
ServletConfig对象对应web.xml文件中的元素。例如你想获取当前Servlet在web.xml文件中的配置名,那么可以使用servletConfig.getServletName()方法获取!
图示:
ServletConfig对象是由服务器创建的,然后传递给Servlet的init()方法,你可以在init()方法中使用它!
String getServletName():获取Servlet在web.xml文件中的配置名称,即指定的名称;
ServletContext getServletContext():用来获取ServletContext对象,ServletContext会在后面讲解;
String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;
Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称;
案列:02_Servlet2
用接口Servlet来编写一个Servlet(介绍Servlet的生命周期,该Servlet完成一个简单的功能,想浏览器相应一条信息“hello world”)
eclipse创建动态项目
创建Hello类实现implements Servlet
Servlet类文件:
// 该函数用于初始化Servlet,类似于类的构造函数。
// 该 只会被调用一次,当用户第一次访问该Servlet时被调用。
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("----init-----");
// 获取初始化参数
System.out.println(config.getInitParameter("m1"));
System.out.println(config.getInitParameter("m2"));
// 获取所有初始化参数名称
Enumeration e = config.getInitParameterNames();
while (e.hasMoreElements()) {
System.out.println(e.nextElement());
}
}
配置Web.xml文件
Servlet1
com.hqyj.servlet.test.Servlet2
m1
v1
m2
v2
Servlet1
/servlet1
案列:03_Servlet3通过继承GenericServlet开发servlet)
通过GenericServlet去开发servlet,只需要重写service方法,相对来说要简单一些。
Servlet类文件:
package com.hqyj.servlet.test;
import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class Servlet3 extends GenericServlet {
@Override
public void init() throws ServletException {
System.out.println("------初始化------");
}
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("------service-------");
}
@Override
public void destroy() {
System.out.println("------destroy------");
}
}
配置Web.xml文件
Servlet1
com.hqyj.servlet.test.Servlet3
Servlet1
/servlet3
案列:04_Servlet4(通过继承HttpServlet)
通过HttpServlet去开发servlet,需要重写doGet、doPost方法,这是目前用的最多的一种方法。
表单提交数据get请求和post请求的区别:
1.从安全性看getpost。get请求服务器立即处理请求,而post请求可能形成一个队列请求
eclipse创建动态项目
创建Hello类实现implements Servlet
Servlet类文件:
package com.hqyj.servlet.test;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("-------doGet--------");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("-------doPost--------");
}
}
配置Web.xml文件
Servlet4
com.hqyj.servlet.test.Servlet4
Servlet4
/servlet4
优化:
package com.hqyj.servlet.test;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------doGet--------");
// 业务逻辑
PrintWriter printWriter = resp.getWriter();
printWriter.println("hello world! HelloHttpServer");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------doPost--------");
this.doGet(req, resp);
}
}
案列:05_UserSystem(通过HttpServlet去开发Servlet)
现在我们已经初步了解了Servlet的基本原理和机制做一个:用户登录网站
功能:
1.进行简单的用户验证
2.如何使用Servlet开发动态网页
3.如何从一个页面跳转到另一个页面上
login.jsp文件:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
Insert title here
首页
Servlet类文件:
package com.hqyj.servlet.test;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet4 extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("---init()---");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1、接收数据
String userName = request.getParameter("userName");
String password = request.getParameter("password");
System.out.println("---------" + userName);
System.out.println("=========" + password);
if (userName.equals("admin")) {
// 正确
// 3、跳转页面
response.sendRedirect("view/mian.jsp");
} else {
// 错误
// 3、跳转页面
response.sendRedirect("login.jsp");
}
// 2、调用业务
// ?
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("--------doPost-----------");
this.doGet(request, response);
}
}
Web.xml文件:
04_j1809_servlet
servlet4
com.hqyj.servlet.test.Servlet4
0
servlet4
/servlet4
/ssss4
login.jsp
因为一个类型的Servlet只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求,那么Servlet是否为线程安全的呢?
答案是:“不是线程安全的”。这说明Servlet的工作效率很高,但也存在线程安全问题!
所以我们不应该在Servlet中创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。
public class Servlet4 extends HttpServlet {
// 成员变量
private String name = "zhangsan";
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 成员变量
private String name = "zhangsan";
// 1、接收数据
String userName = request.getParameter("userName");
String password = request.getParameter("password");
默认情况下,服务器会在某个Servlet第一次收到请求时创建它。也可以在web.xml中对Servlet进行配置,使服务器启动时就创建Servlet。
图:
servlet4
com.hqyj.servlet.test.Servlet4
0
servlet4
/servlet4
在元素中配置元素可以让服务器在启动时就创建该Servlet,其中元素的值必须是大于等于的整数,它的使用是服务器启动时创建Servlet的顺序。上例中,根据的值可以得知服务器创建Servlet的顺序为Hello1Servlet、Hello2Servlet、Hello3Servlet。
Servlet类文件:
package com.hqyj.servlet.test;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet4 extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("---init()---");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1、接收数据
String userName = request.getParameter("userName");
String password = request.getParameter("password");
System.out.println("---------" + userName);
System.out.println("=========" + password);
if (userName.equals("admin")) {
// 正确
// 3、跳转页面
response.sendRedirect("view/mian.jsp");
} else {
// 错误
// 3、跳转页面
response.sendRedirect("login.jsp");
}
// 2、调用业务
// ?
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("--------doPost-----------");
this.doGet(request, response);
}
}
结果:控制台打印
十月 29, 2018 10:33:05 下午 org.apache.catalina.loader.WebappClassLoader validateJarFile
信息: validateJarFile(D:\Java_XG\Environment\TOMCAT8.0\wtpwebapps\04_j1809_servlet\WEB-INF\lib\servlet-api.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class
十月 29, 2018 10:33:06 下午 org.apache.catalina.util.SessionIdGenerator createSecureRandom
信息: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [410] milliseconds.
---init()---
十月 29, 2018 10:33:06 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory D:\Java_XG\Environment\TOMCAT8.0\webapps\docs
十月 29, 2018 10:33:06 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory D:\Java_XG\Environment\TOMCAT8.0\webapps\manager
十月 29, 2018 10:33:06 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory D:\Java_XG\Environment\TOMCAT8.0\webapps\ROOT
十月 29, 2018 10:33:07 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8080"]
十月 29, 2018 10:33:07 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["ajp-nio-8009"]
十月 29, 2018 10:33:07 下午 org.apache.catalina.startup.Catalina start
信息: Server startup in 2025 ms
是的子元素,用来指定Servlet的访问路径,即URL。它必须是以“/”开头!
Servlet4
com.hqyj.servlet.test.Servlet4
Servlet4
/servlet4
/ssss
那么这说明一个Servlet绑定了两个URL,无论访问/servlet4还是/ssss,访问的都是servlet4。
2)还可以在中使用通配符,所谓通配符就是星号“”,星号可以匹配任何URL前缀或后缀,使用通配符可以命名一个Servlet绑定一组URL,例如:
/servlet/:/servlet/a、/servlet/b,都匹配/servlet/;(路径匹配)
.do:/abc/def/ghi.do、/a.do,都匹配*.do;(后缀名匹配)
/*:匹配所有URL;(什么都匹配)
请注意,通配符要么为前缀,要么为后缀,不能出现在URL中间位置,也不能只有通配符。例如:/.do就是错误的,因为星号出现在URL的中间位置上了。.*也是不对的,因为一个URL中最多只能出现一个通配符。
注意,通配符是一种模糊匹配URL的方式,如果存在更具体的,那么访问路径会去匹配具体的。例如:
Servlet4
com.hqyj.servlet.test.Servlet4
Servlet4
/servlet4/mm
Servlet5
com.hqyj.servlet.test.Servlet5
Servlet5
/servlet4/*
login.jsp
当访问路径为http://localhost:8080/hello/servlet/hello1时,因为访问路径即匹配hello1的,又匹配hello2的,但因为hello1的中没有通配符,所以优先匹配,即设置hello1。
服务器会为每个应用创建一个ServletContext对象:
ServletContext对象的创建是在服务器启动时完成的;
ServletContext对象的销毁是在服务器关闭时完成的。
ServletContext对象的作用是在整个Web应用的动态资源之间共享数据!例如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值,这就是共享数据了。
在Servlet中获取ServletContext对象:
在void init(ServletConfig config)中:ServletContext context = config.getServletContext();
ServletConfig类的getServletContext()方法可以用来获取ServletContext对象;
在GenericeServlet或HttpServlet中获取ServletContext对象:
GenericServlet类有getServletContext()方法,所以可以直接使用this.getServletContext()来获取;
public class MyServlet implements Servlet {
public void init(ServletConfig config) {
ServletContext context = config.getServletContext();
}
…
}
…
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletContext context = this.getServletContext();
}
}
ServletContext是JavaWeb四大域对象之一:
PageContext;
ServletRequest;
HttpSession;
ServletContext;
(注:作用域从小到大为:PageContext(jsp页面),ServletRequest(一次请求),HttpSession(一次会话),ServletContext(整个web应用))
所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据,下面是ServletContext对象用来操作数据的方法:
1)void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“xxx”, “xxx”),在ServletContext中保存了一个域属性,域属性名称为xxx,域属性的值为XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;
2)Object getAttribute(String name):用来获取ServletContext中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)servletContext.getAttribute(“xxx”);,获取名为xxx的域属性;
3)void removeAttribute(String name):用来移除ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
4)Enumeration getAttributeNames():获取所有域属性的名称;
案例:
AServlet 类文件:
package com.hqyj.servlet.test;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取ServletContext对象
// 2、调用其setAttribute()方法完成保存数据
ServletContext application = this.getServletContext();
application.setAttribute("name", "翠花");
}
}
BServlet 类文件:
package com.hqyj.servlet.test;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取ServletContext对象
// 2、调用其getAttribute()方法完成获取数据
ServletContext application = this.getServletContext();
String name = (String) application.getAttribute("name");
System.out.println("------------" + name);
}
}
Web.xml文件
aServlet
com.hqyj.servlet.test.AServlet
aServlet
/aServlet
bServlet
com.hqyj.servlet.test.BServlet
bServlet
/bServlet
相信各位一定见过很多访问量统计的网站,即“本页面被访问过XXX次”。因为无论是哪个用户访问指定页面,都会累计访问量,所以这个访问量统计应该是整个项目共享的!很明显,这需要使用ServletContext来保存访问量。
AServlet 类文件:
package com.hqyj.servlet.test;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取ServletContext对象
// 2、从ServletContext对象中获取名为count的属性
// 3、如果存在,访问量+1,然后再保存回去
// 4、如果不存在,表明第一次访问,向ServletContext中保存名为count的属性,值为1
ServletContext application = this.getServletContext();
Integer count = (Integer) application.getAttribute("count");
if (count == null) {
application.setAttribute("count", 1);
} else {
application.setAttribute("count", count + 1);
}
// 向浏览器输出
PrintWriter printWriter = resp.getWriter();
printWriter.print(count);
}
}