(关于如何创建动态web工程,参考文章:juejin.cn/post/714320… 的第3部分)
Servlet简介
Servlet概述
动态网页开发技术(Servlet 和 JSP)中的其中一项;是运行在web服务器上的java程序,可以用于处理web客户端发送的请求,并且可以对请求做出响应。
案例入门
目录结构:
package com.servlet.demo;
import javax.servlet.*;
import java.io.IOException;
public class HelloServlet implements Servlet {
// 在 Servlet 对象创建之后马上执行,并只执行一次。
// ServletConfig是Tomcat传回来的
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
// 获取 Servlet 的位置信息
@Override
public ServletConfig getServletConfig() {
return null;
}
// 可以被调用多次,且每次处理请求都是在调用这个方法
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.getWriter().println("Hello Servlet...");
}
// 获取 Servlet 的信息
@Override
public String getServletInfo() {
return null;
}
// 在 Servlet 被销毁之前调用,且只会调用一次
@Override
public void destroy() {
}
}
<?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">
<display-name>web_test2</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!--配置Servlet-->
<servlet>
<!--配置Servlet名称-->
<servlet-name>HelloServlet</servlet-name>
<!--配置Servlet全路径-->
<servlet-class>com.servlet.demo.HelloServlet</servlet-class>
</servlet>
<!--配置Servlet的一个映射-->
<servlet-mapping>
<!--配置Servlet名称-->
<servlet-name>HelloServlet</servlet-name>
<!--配置访问的路径-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
验证:
Servlet原理
Servlet执行流程
Servlet接口实现关系
- servlet接口
- GenericServlet实现类(抽象类,通用的Servlet)
- HttpServlet实现类(HTTP专用的Servlet)
HttpServlet是与协议相关的,专门处理HTTP协议的请求的。一般都会继承这个类,然后重写service方法
service()内部根据请求方式的不同去执行不同的方法,get请求执行doGet()方法,post请求执行doPost()方法
所以一般不重写service()方法,一般重写doGet()方法和doPost()方法即可
public class AServlet extends HttpServlet {
// @Override
// protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.service(req, resp);
// }
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello Servlet,Hello doGet...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello Servlet,Hello doPost...");
}
}
doPost可以用表单进行测试
Servlet生命周期
- init()
- Servlet在第一次被访问时被实例化,调用此方法
- service()
- 每一次从客户端发来的请求,就会执行此方法
- destory()
- Servlet从服务器移除或者服务器关闭的时候,Servlet对象会被销毁,执行此方法,然后垃圾回收
Servlet的启动时加载
servlet默认是在第一次访问的时候创建对象,现在通过配置将servlet的实例化过程放在服务器启动的时候(不过一般不这么用)
<servlet>
<servlet-name>ServletDemo2</servlet-name>
<servlet-class>com.servlet.demo.ServletDemo2</servlet-class>
<!--配置启动时加载,数值越小,启动优先级越高,一般是2或以后的数字-->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo2</servlet-name>
<url-pattern>/demo2</url-pattern>
</servlet-mapping>
效果:
Servlet的访问路径配置
<url-pattern>配置方式:
- 完全路径匹配
- 以/开始,比如/demo2 /aaa/demo2
- 目录匹配
- 以/开始,以/*结束,比如/* /aaa/*
- 扩展名匹配
- 不能以/开始;以*开始,比如*.action *.do *.jsp
- 访问优先级: 完全路径匹配 > 目录匹配 > 扩展名匹配
<servlet>
<servlet-name>ServletDemo3</servlet-name>
<servlet-class>com.servlet.demo1.ServletDemo3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo3</servlet-name>
<!--完全路径匹配-->
<!-- <url-pattern>/demo3</url-pattern> -->
<!--目录匹配-->
<!-- <url-pattern>/*</url-pattern> -->
<!--扩展名匹配-->
<url-pattern>*.abc</url-pattern>
</servlet-mapping>
Servlet常用对象
ServletConfig(接口)
ServletConfig是用来获得Servlet的相关配置的对象(一个ServletConfig对象对应一段web.xml中Servlet的配置信息)
获得ServletConfig对象:getServletConfig()
- String getServletName():获取的是<servlet-name>中的内容;
- ServletContext getServletContext():获取Servlet上下文对象;
- String getInitParameter(String name):通过名称获取指定初始化参数的值;
- Enumeration getInitParameterNames():获取所有初始化参数的名称;
<!--配置Servlet-->
<servlet>
<!--配置Servlet名称-->
<servlet-name>BServlet</servlet-name>
<!--配置Servlet全路径-->
<servlet-class>com.servlet.demo.BServlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>root1234</param-value>
</init-param>
</servlet>
<!--配置Servlet的一个映射-->
<servlet-mapping>
<!--配置Servlet名称-->
<servlet-name>BServlet</servlet-name>
<!--配置访问的路径-->
<url-pattern>/btest</url-pattern>
</servlet-mapping>
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().append("Served at:").append(req.getContextPath()); //Served at:/web_test1_war_exploded
// 获得servletConfig对象
ServletConfig config = this.getServletConfig();
String username = config.getInitParameter("username");
String password = config.getInitParameter("password");
System.out.println("getInitParameter:" + username + "--" + password); // getInitParameter:root--root1234
// 获得所有初始化参数的名称
Enumeration<String> names = config.getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = config.getInitParameter(name);
System.out.println("getInitParameterNames:" + name + "--" + value);
// 输出:
// getInitParameterNames:password--root1234
// getInitParameterNames:username--root
}
// 获得servlet的名称
String servletName = config.getServletName();
System.out.println("getServletName:" + servletName); // getServletName:BServlet
}
}
ServletContext
Servlet的上下文对象。此对象一个web项目只有一个(在服务器启动的时候创建,当web项目从服务器移除或者关闭服务器时,对象被销毁)
public class CServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获得ServletContext
ServletContext context = this.getServletContext();
// 获取文件的MIME类型
String mimeType = context.getMimeType("aa.txt");
System.out.println("mimeType:" + mimeType); // mimeType:text/plain
// 获得请求路径的工程名
String path = context.getContextPath();
System.out.println("ContextPath:" + path); // ContextPath:/web_test1_war_exploded
// 获得全局初始化参数
String username = context.getInitParameter("username");
String password = context.getInitParameter("password");
System.out.println("InitParameter: username:" + username + " password:" + password);
// 获得所有初始化参数的名称
Enumeration<String> names = context.getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
System.out.println("getInitParameterNames:" + name);
}
}
}
还可以读取项目下的文件:
// 读取web项目下的文件
InputStream inputStream = context.getResourceAsStream("/WEB-INF/classes/db.properties");
ServletContext作为域对象存取数据:(域对象内部有一个Map)
- 存入数据:setAttribute(String name, object value)
- 获取数据:getAttribute(String name)
- 移除数据:removeAttribute(String name)
- 获取所有域属性的名称:Enumeration gettributeNames()
当ServletContext对象被销毁时,数据才会失效。否则数据会一直存在。数据引用范围是整个web应用
public class ServletDemo7 extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void init() throws ServletException {
this.getServletContext().setAttribute("name", "张三");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获得ServletContext
ServletContext context = getServletContext();
String name = (String)context.getAttribute("name");
System.out.println("姓名:" + name);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
public class ServletDemo8 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获得ServletContext
ServletContext context = getServletContext();
String name = (String)context.getAttribute("name");
System.out.println("姓名:" + name);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Response对象(▲)
HttpServletResponse常用API
关于响应行的方法:
- void setStatus(int sc):设置响应的状态码
关于响应头的方法:
- void setHeader(String name, String value):针对一个key对应一个value的情况
- void addHeader(String name, String value):针对一个key对应多个value的情况
关于响应体的方法:
- ServletOutputStream getOutputStream():使用字节流输出
- PrintWriter getWriter():使用字符流输出
其他方法:
- void sendRedirect(String location):重定向
- void setContentType(String type):设置浏览器显示页面时,所采用的字符集
- void setharacterEncoding(String charset):设置响应的缓冲区的字符集
- void addCookie(Cookie cookie):服务器向浏览器回写cookie的方法
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置响应状态码:重定向
resp.setStatus(302);
// 设置字符集
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("页面将于5秒后跳转");
// 添加定时跳转
resp.setHeader("Refresh","5;url=/web_test1_war_exploded/test2");
// 完成重定向(302状态码和Location响应头结合使用的效果)
// response.setHeader("Location", "/web_test/ResponseDemo2");
// 或者直接设置重定向-sendRedirect
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 防止中文乱码
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("I'm Servlet2,欢迎光临");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
Request对象(▲)
HttpServletRequest常用API
获得客户端的信息:
- String getMethod():获得请求方式
- String getQueryString():获得请求路径后的参数内容
- String getRequestURI():获得请求的路径
- StringBuffer getRequestURL():获得请求的路径
- 父类中:String getRemoteAddr():获得客户端的IP地址
获得请求头的方法:
- String getHeader(String name):针对一个key对应一个value的情况
- Enumeration getHeaders(String name):针对一个key对应多个value的情况
获得请求参数的方法:(父类里)
- String getParameter(String name):获得提交的参数,一个名称对应一个值的情况
- Map getParameterMap():获得提交的参数的名称和对应的值存在Map集合中
- String[] get ParameterValues (String name) :获得提交的参数,一个名称对应多个值的情况
request作为域对象存取数据的方法:
- void setAttribute(String name, Object o):向request域中存数据
- Object get Attribute(String name):从 request域中取数据
- void removeAttribute(String name):向request域中存数据
public class Servlet3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 请求方法:GET
System.out.println("请求方法:" + req.getMethod());
// 客户端的IP地址:0:0:0:0:0:0:0:1
System.out.println("客户端的IP地址:" + req.getRemoteAddr());
// 请求参数的字符串:name=aaa&pwd=bbb
System.out.println("请求参数的字符串:" + req.getQueryString());
// 请求路径的URL:http://localhost:8080/web_test1_war_exploded/test3
System.out.println("请求路径的URL:" + req.getRequestURL());
// 请求路径的URI:/web_test1_war_exploded/test3
System.out.println("请求路径的URI:" + req.getRequestURI());
// 获得请求头的信息
System.out.println("请求头的信息:" + req.getHeader("User-Agent"));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
接收表单请求参数:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 接收用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username + "-" + password); // hahah-1111222
// 接收性别(单选)和籍贯(下拉列表)
String sex = request.getParameter("sex");
String city = request.getParameter("city");
System.out.println("性别:" + sex); // 性别:woman
System.out.println("籍贯:" + city); // 籍贯:shenzhen
// 接收爱好(多选)
String[] hobby = request.getParameterValues("hobby");
System.out.println("爱好:" + Arrays.toString(hobby)); // 爱好:[basketball, football]
// 接收自我介绍
String info = request.getParameter("info");
System.out.println("自我介绍:" + info); // 自我介绍:sfafasf
// 获取所有参数
Map<String, String[]> map = request.getParameterMap();
for(String key : map.keySet()) {
String[] value = map.get(key);
System.out.println(key + ":" + Arrays.toString(value));
}
}
最佳实践
网站访问量统计
public class VisitServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
Integer count = (Integer) context.getAttribute("count");
if (count == null) {
context.setAttribute("count", 1);
} else {
context.setAttribute("count", count++);
}
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("本页面一共被访问" + count + "次");
context.setAttribute("count", count);
}
}