1. 什么是servlet
servlet Servlet是javax.servlet.Servlet包中定义的一个接口.它声明了servlet生命周期中不可少的三个方法init,service和destory(),每个servlet必须实现这三个方法,而且服务器在特定的时刻调用.
2.生命周期
容器加载 -> 初始化 init (仅一次) -> 进入服务 service (Get/Post 请求)-> 销毁 destroy -> 容器卸载
执行过程
- 客户端发出请求http://localhost:8080/hello
- 根据web.xml文件的配置,找到<url-pattern>子元素的值“/hello”的<servlet-mapping>元素
读取<servlet-mapping>元素的<servlet-name>子元素的值,由此确定Servlet的名字为”HelloServlet”
找到<servlet-name>值为HelloServlet的<servlet>元素
读取<servlet>元素的<servlet-class>子元素的值,由此确定Servlet的类名为com.kaishengit.web.HelloServlet。
到Tomcat安装目录/webapps/Demo1/WEB-INF/classes/cn/itcast目录下查找到HelloServlet.class文件
客户端发出请求,容器产生request和response对象,容器根据url找到合适的servlet并分配线程进行访问,service根据请求头调用doXX方法,servlet使用相应对象通过容器对客户端做出响应,service方法执行结束,然后调用destory()方法,访问线程和request、response对象被销毁。
细节
- 由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
- <servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。
一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name>和<url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。例如:
<?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_2_5.xsd" id="WebApp_ID" version="2.5">
<!--
配置Servlet
servlet-name 名字自定义
servlet-class Servlet类的完全限定名
-->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.kaishengit.web.HelloServlet</servlet-class>
</servlet>
<!--
servlet-name 名字自定义,但是必须和<servlet>节点中的<servlet-name>值相同
url-pattern 客户端请求的路径名称,必须以/开头
-->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.kaishengit.web.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<!--
重要提示!!!修改web.xml文件必须要重启容器,才能生效
欢迎页面配置,优先级是从上倒下越来越低,如果都找不到,则显示404错误页面
常见的HTTP状态码:
200 正常响应
404 访问的资源不存在
500 服务器异常
-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>main.jsp</welcome-file>
<welcome-file>home.jsp</welcome-file>
</welcome-file-list>
</web-app>
Servlet重要的四个生命周期方法
- 构造方法: 创建servlet对象的时候调用。默认情况下,第一次访问servlet的时候创建servlet对象 只调用1次。证明servlet对象在tomcat是单实例的。
- init方法: 创建完servlet对象的时候调用。只调用1次。
- service方法: 每次发出请求时调用。调用n次。
- destroy方法: 销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象。
只调用1次。
伪代码演示servlet的生命周期
Tomtcat内部代码运行:
```
1)通过映射找到到servlet-class的内容,字符串: gz.itcast.a_servlet.FirstServlet
2)通过反射构造FirstServlet对象
2.1 得到字节码对象
Class clazz = class.forName("gz.itcast.a_servlet.FirstServlet");
2.2 调用无参数的构造方法来构造对象
Object obj = clazz.newInstance(); ---1.servlet的构造方法被调用
3)创建ServletConfig对象,通过反射调用init方法
3.1 得到方法对象
Method m = clazz.getDeclareMethod("init",ServletConfig.class);
3.2 调用方法
m.invoke(obj,config); --2.servlet的init方法被调用
4)创建request,response对象,通过反射调用service方法
4.1 得到方法对象
Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);
4.2 调用方法
m.invoke(obj,request,response); --3.servlet的service方法被调用
5)当tomcat服务器停止或web应用重新部署,通过反射调用destroy方法
5.1 得到方法对象
Method m = clazz.getDeclareMethod("destroy",null);
5.2 调用方法
m.invoke(obj,null); --4.servlet的destroy方法被调用
```
(重点图)用时序图来演示servlet的生命周期
常用类
HttpServletRequest
- 是JSP的内置对象之一,jsp中叫做request
- 用于接受客户端请求,可以获取客户端一些数据
- getParameter(String name) 获取URL或者form表单中的值
- setAttribute(String name,Object value)向跳转目标对象传值
- getAttribute(String name) 获取传值
- getRequestDispatcher(String path) 获取RequestDispatcher对象,使用RequestDispatcher进
行请求转发跳转
HttpservletResponse
- 给客户端做出响应
-
sendRedirect(String urlName) ,以重定向方式,跳转指定路径中
重定向和请求转发的区别
- 重定向跳转是使用url重写的方式进行值的传递,值显示在url的地址栏中
- 请求转发使用HttpServletRequest对象的setAttribute方法进行值传递,值不会显示在地址
栏中 - 重定向传值方式传递适合不敏感数据以及简单的字符串、数字等基本类型
- 请求转发跳转传值方式适合传递敏感数据以及对象、数组、集合等类型的数据
- 重定向跳转后浏览器的地址栏中显示的是跳转目标的URL(地址栏会发生改变)
- 请求转发跳转后地址栏不会显示跳转目标的URL(地址栏不会发生改变)
- 重定向跳转不会引起表单的重复提交
- 请求转发跳转会引起表单的重复提交
-
重定向跳转本质上是服务器产生302响应,客户端再次向服务器发出二次请求
会话技术
- Cookie 技术: 会话数据保存在浏览器客户端
- Session技术: 会话数据保存在服务端
Cookie核心技术
Cookie类:用于存储会话数据
- 构造cookie对象
Cookie(java.lang.String name, java.lang.String value)
- 设置cookie
void setPath(java.lang.String uri) :设置cookie的有效访问路径
void setMaxAge(int expiry) : 设置cookie的有效时间
void setValue(java.lang.String newValue) :设置cookie的值
- 发送cookie到浏览器端保存
void response.addCookie(Cookie cookie) : 发送cookie
- 服务器接受cookie
Cookie[] request.getCookies() : 接收cookie
设置
package com.kaishengit.web;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SetCookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("playId","1002");
cookie.setDomain("localhost");
cookie.setPath("/");
cookie.setMaxAge(60 * 60 * 24 * 7);
cookie.setHttpOnly(true); //
resp.addCookie(cookie);
Cookie cookie2 = new Cookie("productId","2908");
cookie2.setDomain("localhost");
cookie2.setPath("/");
cookie2.setMaxAge(60 * 60 * 24 * 7);
resp.addCookie(cookie2);
System.out.println("set cookie success!");
}
}
获取Cookie
package com.kaishengit.web;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class GetCookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
if(cookies != null) {
for(Cookie cookie : cookies) {
System.out.println(cookie.getName() + " -> " + cookie.getValue());
}
}
System.out.println("get cookie success!");
}
}
JQuery Cookie插件:
Cookie插件:
Cookie原理
- 服务器创建cookie对象,把会话数据存储到cookie对象中
new Cookie("name","value");
2.服务器发送cookie信息到浏览器
response.addCookie(cookie);
举例: set-cookie: name=eric (隐藏发送了一个set-cookie名称的响应头)
- 浏览器得到服务器发送的cookie,然后保存在浏览器端。
- 浏览器在下次访问服务器时,会带着cookie信息
举例: cookie: name=eric (隐藏带着一个叫cookie名称的请求头)
- 服务器接收到浏览器带来的cookie信息
request.getCookies();
记住账号
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<c:if test="${not empty param.callback}">
<div class="alert alert-danger">请登录再继续</div>
</c:if>
<c:if test="${not empty message }">
<div class="alert alert-danger">${message }</div>
</c:if>
<form method="post" id="loginForm">
<div class="form-group">
<label>账号</label>
<input type="text" name="username" id="username" class="form-control" value="${username }"/>
</div>
<div class="form-group">
<label>账号</label>
<input type="password" name="password" class="form-control"/>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="remeberme" value="remeberme" id="remeberme" />记住账号
</label>
</div>
<div>
<button type="button" id="loginBtn" class="btn btn-success">登录</button>
</div>
</form>
</div>
<script src="/static/js/jquery-1.11.3.min.js"></script>
<script src="/static/js/jquery.cookie.js"></script>
<script>
$(function(){
//$("#username").val($.cookie("username"));
$("#loginBtn").click(function(){
/* if($("#remeberme")[0].checked) {
$.cookie("username",$("#username").val(),{ expires: 7, path: '/' });
} */
$("#loginForm").submit();
});
});
</script>
</body>
</html>
服务端记住账号
package com.kaishengit.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import com.kaishengit.entity.Admin;
import com.kaishengit.exception.ServiceException;
import com.kaishengit.service.AdminService;
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = "";
Cookie[] cookies = req.getCookies();
if(cookies != null) {
for(Cookie cookie : cookies) {
if(cookie.getName().equals("username")) {
username = cookie.getValue();
break;
}
}
}
req.setAttribute("username", username);
req.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
String callback = req.getParameter("callback");
String remeberme = req.getParameter("remeberme");
AdminService adminService = new AdminService();
try {
Admin admin = adminService.login(username, password);
//判断是否选中了[记住账号]框
if(StringUtils.isNotEmpty(remeberme)) {
Cookie cookie = new Cookie("username",username);
cookie.setDomain("localhost");
cookie.setPath("/");
cookie.setMaxAge(60 * 60 * 24 * 365 * 100);
cookie.setHttpOnly(true);
resp.addCookie(cookie);
}
//获取HttpSession
HttpSession session = req.getSession();
session.setAttribute("admin", admin);
if(StringUtils.isEmpty(callback)) {
resp.sendRedirect("/list");
} else {
resp.sendRedirect(callback);
}
} catch (ServiceException e) {
req.setAttribute("message", e.getMessage());
req.setAttribute("username", username);
req.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(req, resp);
}
}
}
Session原理
问题: 服务器能够识别不同的浏览者!!!
前提: 在哪个session域对象保存数据,就必须从哪个域对象取出!!!!
浏览器1:(给s1分配一个唯一的标记:s001,把s001发送给浏览器)
1)创建session对象,保存会话数据
HttpSession session = request.getSession(); --保存会话数据 s1
浏览器1 的新窗口(带着s001的标记到服务器查询,s001->s1,返回s1)
1)得到session对象的会话数据
HttpSession session = request.getSession(); --可以取出 s1
新的浏览器1:(没有带s001,不能返回s1)
1)得到session对象的会话数据
HttpSession session = request.getSession(); --不可以取出 s2
浏览器2:(没有带s001,不能返回s1)
1)得到session对象的会话数据
HttpSession session = request.getSession(); --不可以取出 s3
代码解读:HttpSession session = request.getSession();
- 第一次访问创建session对象,给session对象分配一个唯一的ID,叫JSESSIONID
new HttpSession();
- 把JSESSIONID作为Cookie的值发送给浏览器保存
Cookie cookie = new Cookie("JSESSIONID", sessionID);
response.addCookie(cookie);
- 第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器
- 服务器得到JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。
if(找到){
return map.get(sessionID);
}
Map<String,HttpSession>]
<"s001", s1>
<"s001,"s2>
- 如果找到对应编号的session对象,直接返回该对象
- 如果找不到对应编号的session对象,创建新的session对象,继续走1的流程
结论:通过JSESSION的cookie值在服务器找session对象!!!!!
总结:
1)会话管理: 浏览器和服务器会话过程中的产生的会话数据的管理。
2)Cookie技术:
new Cookie("name","value")
response.addCookie(coookie)
request.getCookies()
3)Session技术
request.getSession();
setAttrbute("name","会话数据");
getAttribute("会话数据")
监听器
web.ml
<?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_3_1.xsd"
version="3.1">
<!-- 登录过滤器 -->
<filter>
<filter-name>ValidateFilter</filter-name>
<filter-class>com.kaishengit.web.filter.ValidateServlet</filter-class>
</filter>
<filter-mapping>
<filter-name>ValidateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
适配器模式:AbstractFilter
package com.kaishengit.web.filter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* @author Wgs
* @version 1.0
* @create:2018/05/26
*/
public class FilterServlet implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("FilterServlet.init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterServlet.doFilter");
}
@Override
public void destroy() {
}
}
登陆拦截器
package com.kaishengit.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.kaishengit.entity.Admin;
public class ValidateFilter extends AbstractFilter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1. 获取用户访问的资源地址
String uri = request.getRequestURI();
System.out.println(uri);
if("/".equals(uri) || "/index.jsp".equals(uri) || "/login".equals(uri)
|| uri.startsWith("/static/")) {
filterChain.doFilter(request, response);
} else {
HttpSession session = request.getSession();
Admin admin = (Admin) session.getAttribute("admin");
if(admin != null) {
filterChain.doFilter(request, response);
} else {
response.sendRedirect("/login?callback="+uri);
}
}
}
}
监听器
web.xml
<!--监听器的配置-->
<listener>
<listener-class>com.kaishengit.web.listener.MyServletContextListener</listener-class>
</listener>
<listener>
<listener-class>com.kaishengit.web.listener.MyHttSessionListener</listener-class>
</listener>
<context-param>
<param-name>userName</param-name>
<param-value>jack</param-value>
</context-param>
HttpSessionListener
package com.kaishengit.web.listener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class MyHttSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("session create...");
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("session destory...");
}
}
ServletContextListener
package com.kaishengit.web.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContextListener init....");
//通过ServletContextEvent对象来获取ServletContext对象
ServletContext servletContext = servletContextEvent.getServletContext();
String userName = servletContext.getInitParameter("userName");
System.out.println(userName);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContextListener destroy....");
}
}