深入理解Servlet技术

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对象被销毁。


深入理解Servlet技术_第1张图片
Servlet的执行过程.jpg

细节

  • 由于客户端是通过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的生命周期

深入理解Servlet技术_第2张图片
image.png

常用类

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) ,以重定向方式,跳转指定路径中


    深入理解Servlet技术_第3张图片
    image.png
重定向和请求转发的区别
  • 重定向跳转是使用url重写的方式进行值的传递,值显示在url的地址栏中
  • 请求转发使用HttpServletRequest对象的setAttribute方法进行值传递,值不会显示在地址
    栏中
  • 重定向传值方式传递适合不敏感数据以及简单的字符串、数字等基本类型
  • 请求转发跳转传值方式适合传递敏感数据以及对象、数组、集合等类型的数据
  • 重定向跳转后浏览器的地址栏中显示的是跳转目标的URL(地址栏会发生改变)
  • 请求转发跳转后地址栏不会显示跳转目标的URL(地址栏不会发生改变)
  • 重定向跳转不会引起表单的重复提交
  • 请求转发跳转会引起表单的重复提交
  • 重定向跳转本质上是服务器产生302响应,客户端再次向服务器发出二次请求


    深入理解Servlet技术_第4张图片
    image.png

会话技术

  • Cookie 技术: 会话数据保存在浏览器客户端
  • Session技术: 会话数据保存在服务端

Cookie核心技术

Cookie类:用于存储会话数据
  1. 构造cookie对象
Cookie(java.lang.String name, java.lang.String value)
  1. 设置cookie
void setPath(java.lang.String uri)   :设置cookie的有效访问路径
                   void setMaxAge(int expiry) : 设置cookie的有效时间
                   void setValue(java.lang.String newValue) :设置cookie的值

  1. 发送cookie到浏览器端保存
void response.addCookie(Cookie cookie)  : 发送cookie
  1. 服务器接受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原理
  1. 服务器创建cookie对象,把会话数据存储到cookie对象中
new Cookie("name","value");

2.服务器发送cookie信息到浏览器

    response.addCookie(cookie);
    举例: set-cookie: name=eric  (隐藏发送了一个set-cookie名称的响应头)
  1. 浏览器得到服务器发送的cookie,然后保存在浏览器端。
  2. 浏览器在下次访问服务器时,会带着cookie信息
 举例: cookie: name=eric  (隐藏带着一个叫cookie名称的请求头)
  1. 服务器接收到浏览器带来的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();
  1. 第一次访问创建session对象,给session对象分配一个唯一的ID,叫JSESSIONID
    new HttpSession();
  1. 把JSESSIONID作为Cookie的值发送给浏览器保存
Cookie cookie = new Cookie("JSESSIONID", sessionID);
                    response.addCookie(cookie);
  1. 第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器
  2. 服务器得到JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。
if(找到){
                        return map.get(sessionID);
                    }
                    Map]


                    <"s001", s1>
                    <"s001,"s2>
  1. 如果找到对应编号的session对象,直接返回该对象
  2. 如果找不到对应编号的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....");
    }
}

异步验证

深入理解Servlet技术_第5张图片
image.png

你可能感兴趣的:(深入理解Servlet技术)