简介
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
servlet是Server Applet的简称,翻译过来就是服务程序.好吧,这么说你可能还是不太懂,简单的讲,这个servlet是运行在服务器上的一个小程序,用来处理服务器请求的.进一步讲,我们知道,一般的网页程序,是由我们通过浏览器访问来实现的,在这个过程中,我们的浏览器发送访问请求,服务器接收请求,并对浏览器的请求作出相应的处理.这就是我们熟悉的B/S模型(浏览器-服务器模型).而servlet就是对请求作出处理的组件,运行于支持Java的应用服务器中.
作用
在servlet刚刚出现的那个年代,servlet的作用十分复杂,既承担着处理数据的作用,又承担着展示页面的作用,美工人员想要参与开发,基本上是不太现实的,毕竟美工不可能再去花时间将页面做好.随着时间的推移,出现了MVC思想,也就是模型-界面-控制器思想,极大的简便了开发,也明确了servlet的作用.
根据上面这张图,我们就能知道,servlet在其中承担的作用是controller,控制器,起到对数据进行操作的作用.
顺便补充说明一下,最经典的MVC模型就是JSP+JavaBean+Servlet开发的模式
执行原理:
1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径。
2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
3. 如果有,则在找到对应的<servlet-class>全类名(2,3步骤也可以通过注解实现-常用)
4. tomcat会将字节码文件加载进内存,并且创建其对象
5. 调用其方法
开发流程
狭义上讲,servlet是java语言实现的一个类,所以我们就要根据这个类进行相应的扩展开发.
开发流程如下:
- 编写一个java类,继承HttpServlet类
- 重写HttpServlet类的doGet方法和doPost方法
- 配置web.xml文件,或者使用注解对servlet进行配置
开发流程就是这个样子,我们先来看一下最后一个步骤.
对servlet进行配置
你一定在想,如果我写了好几个servlet,但是前端发送请求的时候,究竟会把请求发送给哪个servlet呢?我在输入某个地址的时候,究竟是由哪个servlet进行响应的呢?
这时候servlet的配置就显得尤为重要.对servlet的配置指定了对前端请求处理究竟是通过哪个servlet.
配置servlet一共有两种方式,一种是使用web.xml文件配置,另外一种就是使用注解配置,下面我们来详解介绍这两种配置方式:
1.使用web.xml文件配置,注意,servlet的配置内容要写在webapp内部
当你访问/first的时候,服务器自然就会把请求交给MyServlet进行处理了.
首先在maven配置文件pom.xml中导入Servlet - API的依赖
<webapp>
<!-- 配置一个servlet -->
<!-- servlet的配置 -->
<servlet>
<!-- servlet的内部名称,自定义。尽量有意义 -->
<servlet-name>MyServlet</servlet-name>
<!-- servlet的类全名: 包名+简单类名 -->
<servlet-class>cn.roobtyan.servlet.FirstServlet</servlet-class>
</servlet>
<!-- servlet的映射配置 -->
<servlet-mapping>
<!-- servlet的内部名称,一定要和上面的内部名称保持一致!! -->
<servlet-name>MyServlet</servlet-name>
<!-- servlet的映射路径(访问servlet的名称) -->
<url-pattern>/first</url-pattern>
</servlet-mapping>
</webapp>
2.使用@注解配置
新版本的servlet支持使用注解进行配置,这样极大的简便了开发.
注解配置如下:
@WebServlet(name = "LoginServlet",urlPatterns = {"/login"})
public class LoginServlet extends HttpServlet {
}
然后,你在访问/login的时候,服务器同样就会将处理交由LoginServlet进行处理了.
这样是不是非常爽?(-)
实际上,注解的作用和web.xml的作用是相同的,一般都是推荐使用注解的方式进行开发,这样十分简便,可读性也变的更加强大.
路径模糊匹配
url可不可以不这么精确的配置,用一种模糊匹配的方式,就是我访问某种规则的路径的时候,统一调用一个servlet,这当然是可以的了.这就涉及到映射路径的问题了
<url-pattern>/first<url-pattern>
@WebServlet(name = "LoginServlet",urlPatterns = {"/login"})
精确匹配就是我们上面用的那种方式,使用固定的url来访问这个servlet,这种没什么需要说明的。
模糊匹配,我们可以让好多路径映射到同一个servlet,模糊匹配一般有如下格式。
这里面有两点是需要注意的,一是url要么以/开头,要么以*开头,其他的都是非法的
/* 任意路径都映射到这个servlet
/roobtyan/* /roobtyan下的任意路径映射到该servlet
*.(*.do *.action *.html) 是这样的:/任意路径.do/action/html
生命周期
一般来讲,servlet只会初始化一次,也就是整个过程中只存在一个servlet对象,即便是有多次访问,依然只有一个对象,这个对象是可以复用的.我想你一定会好奇这个servlet究竟是在什么时候创建的,所以就来讲一下servlet的生命周期,所谓的生命周期我们在java基础知识中一定也了解过,就是servlet类究竟在什么时候创建,调用了何种方法,最后在什么时候被销毁.我们之前学过的对象都是自己手动创建,最后由JVM来销毁的,而servlet的整个生命周期,都是由tomacat,也就是服务器控制的.
得出结论,Servlet的生命周期是:
实例化 --》初始化 --》多次调用service服务 --》 tomcat容器关闭时销毁
servlet共有三个关键的方法,分别是init(),service(),destroy().
- init方法只会调用一次,只是在创建servlet实例的时候才会创建
- service方法,是进行数据处理的,只要接受了一次请求,就会被调用一次
- destroy方法,销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象,同样也是调用一次
Servlet获取参数的方法
这里主要解析Servlet获取get和post参数的方式,主要有三种:
- 使用getParameter()获取参数方法,单个获取。
- 使用getParameterMap()获取Map键值对参数的方法。注意该方法获取到的请求参数是以Map键值对的形式存在的,并且在构造实体时,我们还需要将获取到的Map类型的参数通过BeanUtils工具类转换为需要的实体类类型进行构造。所以需要先导入BeanUtils的依赖。
- get特有的查询字符串的方法以及post以流的形式查询字符串的方法。这两种获取字符串的方式比较特别,get获取的是浏览器中URL地址栏访问路径?后面的参数,而post是从请求体中获取参数,这两者获取到的字符串参数都是URL编过码的,所以在获取参数时候都需要解码。
get请求特有的查询字符串方法
post请求以流的形式获取请求体中的字符串参数
在使用该方法前,我们需要先简单了解下tomcat的工作原理:
如图所示,整个过程都是以流的形式传输的,所以在post请求中要获取请求体中的字符串参数,就需要先获取到这条输入流。
注意:获取流查询字符串参数的方式要放在最前面执行,因为流是只能消耗一次,放在其它操作过流的方法后面该方法就获取不到数据了,因为流被消耗了。
servlet响应页面和数据
响应页面,也就是收到前端的请求响应需要跳转的页面。Servlet响应页面有转发和重定向两种方式。
1、转发 — 也就是携带客户端发送的请求转发跳转到下一个页面,使用的是request请求对象
//转发可以将请求中的数据带到新的页面。
//getRequestDispatcher():表示获取请求转发器的方法
//forward():表示将请求及响应对象一并转发到下一个新页面
request.getRequestDispatcher("跳转页面路径").forward(request,response);
2、重定向 — 服务器接收到前端请求,通过响应将要跳转的页面地址响应给客户端,客户端再重新发送请求跳转到目标页面。
//重定向是将要跳转的页面路径交给前端重新发送请求跳转页面。
//相当于客户端发送了两次请求,所以原来请求中的数据就没有了。
response.sendRedirect("响应的页面路径")
Servlet响应数据
响应数据也是通过流的方式响应给前端,这里主要用到的是一条打印流(也就是之前JSP中的内置对象out).响应数据一般都是响应的json格式的数据,前端获取到json格式的数据就相当于拿到了一个对象,通过对象的方式去获取数据。
1、获取到打印流响应普通数据(注意:有乱码要设置编码格式)
2、响应json字符串格式的数据
- 首先我们需要先在maven中导入json的依赖,如图:
- 响应json格式数据
一个登录控制的例子来简单的看一下servlet开发的步骤.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>roobtyan登录控制系统</title>
</head>
<body>
<h1 align="center" style="color: red;">欢迎您登录系统后台</h1><hr/>
<%--the form start--%>
<div align="center">
<form method="post" action="/login">
Username:<input type="text" name="username"/><br/><br/>
Password:<input type="password" name="password"/><br/><br/>
<input type="submit" value="登录"/>
</form>
</div>
</body>
</html>
- 创建一个登录成功页面,同样使用jsp页面
welcome.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎页面</title>
</head>
<body>
<h1 align="center" style="color: red">Welcome:</h1>
<%
out.println(session.getAttribute("user"));
%>
<hr/>
<span style="align:center; color:yellow">
Time:<%
out.println(new Date());
%>
</span>
</body>
</html>
- 创建LoginServlet.java
public class LoginServlet extends HttpServlet {
public void service(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
//设置字符编码
request.setCharacterEncoding("utf8");
//从request对象中获取username,password
String username = request.getParameter("username");
String password = request.getParameter("password");
//判断是否为管理员
if("administrator".equals(username)&&"123456".equals(password)){
//登录成功,设置session
HttpSession session = request.getSession(true);
session.setAttribute("user", "管理员,欢迎你!");
}else {
session.setAttribute("user","登录信息错误,请检查用户名或密码");
}
//将页面转发到欢迎页面
requestDispatcher = request.getRequestDispatcher("/welcome.jsp");
requestDispatcher.forward(request,response);
}
}
还有一个地方你可能存在疑惑,为什么使用request.getParameter方法可以获取到提交的表单中的内容呢?这个很好解释,因为前端使用post或者get方法将表单信息提交到servlet的时候,将表单信息封装成了request对象,这样就可以获取到了.值得注意的是,表单中的name字段,就是我们获取值的根据.
配置servlet
这里对于servlet的配置,我们采取web.xml的方式,主要是因为这种方法相对麻烦,为了让你有着更好的理解,就这样做了
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.roobtyan.cn.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
这样,我们的第一个servlet程序就做完了
Servlet自动加载
前面我们说了,servlet只有在第一次被访问的时候才会加载,这肯定会造成第一个访问的人访问时间较长,因为他需要等待servlet完成加载.那么,有没有什么方法能够使得servlet自动加载呢,就是在启动服务器的时候就将servlet加载起来呢?同样可以在web.xml中进行配置
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>cn.roobtyan.LoginServlet</servlet-class>
<!-- 让servlet对象自动加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
就是使用的<login-on-startup></login-on-startup>
配置的,注意: 其中的整数值越大,创建优先级越低!
Servlet多线程问题
前面我们讲了,一个servlet在服务器中只会存在一个实例,不论是有多少访问,都掉用的同一个实例,也就是单实例多线程的.这就存在着一定的线程安全问题,比如说,我在servlet中定义了一个全局变量,那么这个变量的值很有可能不是我期待的值,所以,在servlet中要尽量避免使用全局变量.
Servlet中重要的对象
在servlet中共有四个重要的对象:
HttpServletRequest 请求对象:获取请求信息
HttpServletResponse 响应对象: 设置响应对象
ServletConfig对象 servlet配置对象
ServletContext对象 servlet的上下文对象