1. 什么是servlet
servlet Servlet是javax.servlet.Servlet包中定义的一个接口.它声明了servlet生命周期中不可少的三个方法init,service和destory(),每个servlet必须实现这三个方法,而且服务器在特定的时刻调用.
2.生命周期
容器加载 -> 初始化 init (仅一次) -> 进入服务 service (Get/Post 请求)-> 销毁 destroy -> 容器卸载
执行过程
- 客户端发出请求http://localhost:8080/hello
- 根据web.xml文件的配置,找到
子元素的值“/hello”的 元素
读取元素的 子元素的值,由此确定Servlet的名字为”HelloServlet”
找到值为HelloServlet的 元素
读取元素的 子元素的值,由此确定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的注册名称和Servlet的完整类名。
一个元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素: 和 ,分别用于指定Servlet的注册名称和Servlet的对外访问路径。例如:
HelloServlet
com.kaishengit.web.HelloServlet
HelloServlet
/hello
LoginServlet
com.kaishengit.web.LoginServlet
LoginServlet
/login
index.jsp
main.jsp
home.jsp
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" %>
Document
请登录再继续
${message }
服务端记住账号
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]
<"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
ValidateFilter
com.kaishengit.web.filter.ValidateServlet
ValidateFilter
/*
适配器模式: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
com.kaishengit.web.listener.MyServletContextListener
com.kaishengit.web.listener.MyHttSessionListener
userName
jack
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....");
}
}