JavaWeb02:Servlet+Cookie+Session

1. Servlet简介

1)它是sun公司开发动态web的一门技术。
2)sun在API中提供的一个servlet接口,因此想开发servlet程序,需要完成两个步骤:编写一个类,实现servlet接口;把开发好的Java类部署到web服务器中。
3)把实现了servlet接口的Java程序叫做Servlet,即本质上还是Java程序。

2. HelloServlet

1)构建一个普通Maven项目,删除里面的src目录,以后在这个项目里建module。这个空的工程叫主工程。主工程导入dependency。
2)如有必要(有时候子项目parent标签会被覆盖掉,导致dependency无法关联),手动在子项目中的pom.xml添加parent标签信息。关于Maven父子工程的理解:

//父项目中有子项目(模块)
    <modules>
        <module>servlet-01</module>
    </modules>
//子项目
	<parent>
    	<artifactId>javaweb-02-servlet</artifactId>
    	<groupId>com.yyz</groupId>
    	<version>1.0-SNAPSHOT</version>
 	</parent>

子项目中:
在这里插入图片描述
父项目中的jar包子项目可以直接用

3) 复制最新的配置信息到web.xml

<?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"
         metadata-complete="true">
</web-app>

4)在主项目pom.xml添加依赖,子项目也可以关联到

	<dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

5)在main文件夹下添加java和resources文件夹
6)编写servlet程序,在子项目java文件夹下新建package(注意规范):
7)然后实现一个接口:
值得注意的是sun公司有两个默认的实现类,HttpServlet 和 GenericServlet
这里直接继承HttpServlet
在这里插入图片描述
注:command+下方向键查看源码,可得
在这里插入图片描述

在这里插入图片描述
实际代码:

import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
    
    
    //由于get和post只是请求实现的不同方式,可以相互调用,业务逻辑都一样,写一个就行。
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("进入了doGet方法");
        //ServletOutputStream outputStream = resp.getOutputStream();
        PrintWriter writer = resp.getWriter();//响应流
        writer.println("hello servlet");
        //super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //super.doPost(req, resp);
    }
}

7)编写servlet的映射:
why:写的是java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以需要在web服务中注册我们写的servlet,还需要给他一个浏览器能够访问的路径。
方法:打开web.xml,添加如下代码

  <!--注册servlet-->
  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.yyz.servlet.HelloServlet</servlet-class>
  </servlet>
  <!--servlet请求(映射)路径-->
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>

在这里插入图片描述

8)配置Tomcat
注意配置项目发布的路径:在这里插入图片描述
9)启动
在这里插入图片描述
注:输入 http://localhost:8080/s1/hello可访问之前映射的路径。

3. Servlet原理

servlet是由web服务器调用,web服务器在收到浏览器请求之后,会:
在这里插入图片描述

关于映射路径:

一个请求可以指定一个映射路径

<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

一个请求可以指定多个映射路径:类似上面的增加,或/*

  <!--servlet默认请求,进servlet而不是jsp文件,尽量不要这么写-->
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
  <!--可以自定义后缀实现请求-->
  <!--url:localhost:8080/s1/xxxxx.yyz
  注意:*前面不能加映射的路径-->
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>*.yyz</url-pattern>
  </servlet-mapping>

优先级问题:指定了固有的映射路径优先级最高,如果找不到,就会走默认的处理请求。

  <!--404-->
  <!--注册servlet-->
  <servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>com.yyz.servlet.ErrorServlet</servlet-class>
  </servlet>
  <!--servlet请求(映射)路径-->
  <servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

4. ServletContext

web容器在启动的时候,会为每个web程序都创建一个对应的servletcontext对象,它代表了当前的web应用。如:

–>应用1:共享数据(后面用session,request做):我在一个servlet中保存的数据能在另一个servlet中拿到。它凌驾于众多servlet之上。
在这里插入图片描述
1)先新建一个包含context对象的类,用来放置数据。并且在web.xml中加入映射。

public class HelloServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //System.out.println("hello servlet");

        //servlet上下文
        ServletContext context =this.getServletContext();
        String username = "公孙离";
        //将一个数据保存在ServletContext中,以键值对的形式
        context.setAttribute("username",username);//key-value
    }
}
	<servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.yyz.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

2)然后再建立一个读取context的类。同样在web.xml中加入映射。

public class GetServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //数据
        //和HelloServlet里的共用context
        ServletContext context = this.getServletContext();
        //object强转到string
        String username = (String) context.getAttribute("username");

        //响应
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().print("名字:"+username);
    }
}
	<servlet>
        <servlet-name>getc</servlet-name>
        <servlet-class>com.yyz.servlet.GetServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>getc</servlet-name>
        <url-pattern>/getc</url-pattern>
    </servlet-mapping>

3)测试访问结果,先访问hello(存储context),再访问getc即可看到读取数据。

–>应用2:获取初始化参数(一般不用):

 web.xml文件中
 <!--配置一些web应用的初始化参数-->
    <!--JDBC的mybatis框架-->
    <context-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
    </context-param>
使用
@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        ServletContext context = this.getServletContext();

        //url是jdbc的key
        String url = context.getInitParameter("url");//
        resp.getWriter().println(url);
    }

–>应用3:请求转发(后面用request做),可以转发url
当A不能直接获取C的信息,可以从B中转在这里插入图片描述

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        ServletContext context = this.getServletContext();
        //请求转发,转发地址
        //RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");//转发的请求路径
        //requestDispatcher.forward(req,resp);//转发(request进, response出)
        context.getRequestDispatcher("/gp").forward(req,resp);
    }

与请求转发对应的是重定向:
在这里插入图片描述

–>应用4:读取资源文件(后面用类加载,反射)
Properties: 在java目录下,resources(用这个)目录下新建properties,发现在target文件夹中都被打包到了同一个路径classes下,称为类路径。
思路:需要一个文件流(db.properties):

username=root
password=123456
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //读取properties文件信息,在target可以找到路径
        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
        Properties properties = new Properties();
        properties.load(is);
        //存在db.properties中的信息
        String username = (String) properties.get("username");
        String password = (String) properties.get("password");

        resp.getWriter().print(username+":"+password);
    }

5. HttpServletResponse

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,和代表响应的HttpServletResponse对象。

如果要获取客户端请求过来的参数,找HttpServletRequest
如果要给客户端响应信息,找HttpServletResponse

1)简单分类

负责向浏览器发送数据的方法:

//平常的流
public ServletOutputStream getOutputStream() throws IOException;
//一般中文用getwriter
public PrintWriter getWriter() throws IOException;

负责向浏览器发送响应头的方法

public void setCharacterEncoding(String charset);

public void setContentLength(int len);
    
public void setContentLengthLong(long len);

public void setContentType(String type);

public void setDateHeader(String name, long date);

public void addDateHeader(String name, long date);

public void setHeader(String name, String value);
 
public void addHeader(String name, String value);

public void setIntHeader(String name, int value);

public void addIntHeader(String name, int value);

响应的状态码:200,3xx,4xx,5xx

2)常见应用

  1. 向浏览器输出消息(前面已经提过)
  2. 下载文件:
    1)要获取下载的文件路径
    2)下载的文件名是什么
    3)设置:让浏览器能够支持下载需要的文件
    4)获取下载文件的输入流
    5)创建缓冲区(三部曲)
    6)获取outputstream对象(三部曲)
    7)将fileoutputstream流写到缓冲区(三部曲)
    8)将缓冲区中的输出输出到客户端
public class FileServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //1)要获取下载的文件路径
        //getRealPath的路径是get/Users/mac/Environment/tomcat-9.0.40/webapps/r/,所以用绝对路径
        //String realPath = this.getServletContext().getRealPath("/2.png");
        String realPath = "/Users/mac/IdeaProjects/Servlet/javaweb-03-servlet/response/target/classes/啦啦啦.png";
        System.out.println("下载路径:"+realPath);

        //2)下载的文件名是什么
        //注意:在mac中就是/,但是在windows中,文件路径是\,\是转译符,要再加上一个\,\\才是\:
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);

        //3)设置:让浏览器能够支持(Content-Disposition)下载需要的文件
        //https://blog.csdn.net/weixin_33747129/article/details/93745005
        //设置响应的消息头
        resp.setContentType("text/html;charset=UTF-8");
        //设置响应类型中包含文件附件。中文文件名URLEncoder.encode编码,否则可能乱码。
        resp.setHeader("Content-Disposition", "attachment; " + "filename="+ URLEncoder.encode(fileName,"utf-8"));

        //4)获取下载文件的输入流
        FileInputStream in = new FileInputStream(realPath);

        //5)创建缓冲区(三部曲)
        int len = 0;
        byte[] buffer = new byte[1024];

        //6)获取OutputStream对象(三部曲)
        ServletOutputStream out = resp.getOutputStream();

        //7)将FileOutputStream流写到缓冲区(三部曲)
        //8)将缓冲区中的输出输出到客户端
        while((len=in.read(buffer))>0){
    
    
            out.write(buffer,0,len);
        }

        //9)关闭流,保证安全
        in.close();
        out.close();
    }

3)验证码功能

验证可有前端或后端实现。
后端实现需要用到java的图片类,生成一个图片。
在这里插入图片描述
在这里插入图片描述

4)重定向(重点)

一个web资源收到客户端A的请求,B会通知A客户端去访问另外一个web资源C。
常见场景:用户登录。
重定向和转发的区别:
相同点:都可以实现页面跳转
不同点:请求转发的时候url不会产生变化(307),但是重定向会发生变化(302)。

应用:

//后端
@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("进入doGet");

        //处理请求
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println(username+":"+password);

        //重定向一定要注意路径问题,否则会出现404
        resp.sendRedirect("/r/success.jsp");
    }
<%--这里提交的路径,需要寻找到项目的类路径(/login,但前端不明白/),用${}  --%>
<form action="${pageContext.request.contextPath}/login" method="get">
    username:<input type="text" name="username"><br>
    password:<input type="password" name="password"><br>
    <input type ="submit">
</form>

在这里插入图片描述

6. HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过http协议访问服务器,,http请求中的所有信息会被封装到HttpServletRequest,我们通过HttpServletRequest的方法获得客户端所有信息。

应用场景:获取前端传进的参数,请求转发

public String getParameter(String name);
public String[] getParameterValues(String name);
<div style="text-align: center">
    <%--pageContext在导入的jsp包里--%>
    <%--以post方式提交到login请求--%>
    <form action = "${pageContext.request.contextPath}/login" method="post">
    用户名:<input type="text" name="username"> <br>
    密码:<input type="password" name="password"> <br>
    <input type="checkbox" name="hobby" value="a">a
    <input type="checkbox" name="hobby" value="b">b
    <input type="checkbox" name="hobby" value="c">c
    <input type="checkbox" name="hobby" value="d">d
        <br>
    <input type="submit">
        </form>
</div>
 @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobby = req.getParameterValues("hobby");
        System.out.println("=======================");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobby));
        System.out.println("=======================");
		//通过请求转发
        //因为/在request里会自动被识别成当前web应用目录,所以不用加	req.getContextPath()
        req.getRequestDispatcher("/success.jsp").forward(req,resp);
        System.out.println(req.getContextPath());

7. Cookie和Session
7.1 会话

会话:用户打开一个浏览器没惦记了很多超链接,访问多个web资源,关闭浏览器这个过程,称为会话。
有状态会话:
服务端给客户端一个信件,客户端访问服务端带上信件即可:cookie
服务器登记你来过了,下次你来的时候,我来匹配:session

7.2 保存会话的两种技术

cookie:客户端技术(响应,请求)
session:服务器技术(可以保存用户的会话信息),我们可以把信息或者数据放在session中。
常见场景:网站登录之后,下次不用再登陆了。

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //服务器告诉你你来的时间,把这个时间封装成一个信件,你下次来,服务器就知道你来了。

        //解决中文乱码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        PrintWriter out = resp.getWriter();

        //Cookie,服务器从客户端获取。
        Cookie[] cookies = req.getCookies();//这里返回数组,说明cookie可能存在多个

        //判断cookie是否存在
        if(cookies!=null){
    
    
            out.write("上一次访问的时间是:");

            for(int i = 0; i<cookies.length; i++){
    
    
                Cookie cookie = cookies[i];
                //获取cookie的名字
                if(cookie.getName().equals("lastLoginTime")){
    
    
                    //获取cookie中的值
                    long lastLoginTime = Long.parseLong(cookie.getValue());
                    Date date = new Date(lastLoginTime);
                    out.write(date.toLocaleString());
                }
            }
        }else{
    
    
            out.write("这是您第一次访问本站");
        }

        //服务器给客户端响应一个cookie:
        Cookie cookie = new Cookie("lastLoginTime",String.valueOf(System.currentTimeMillis()));

        cookie.setMaxAge(24*60*60);//cookie有效期为一天

        resp.addCookie(cookie);
    }

7.3 Cookie

从请求中拿到cookie信息
服务器响应给客户端cookie

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //服务器告诉你你来的时间,把这个时间封装成一个信件,你下次来,服务器就知道你来了。

        //解决中文乱码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        PrintWriter out = resp.getWriter();

        //第一步:服务器从客户端获取Cookie
        Cookie[] cookies = req.getCookies();//这里返回数组,说明cookie可能存在多个

        //判断cookie是否存在
        if(cookies!=null){
    
    
            out.write("上一次访问的时间是:");

            for(int i = 0; i<cookies.length; i++){
    
    
                Cookie cookie = cookies[i];
                //第二步:获取cookie的名字(key)
                if(cookie.getName().equals("lastLoginTime")){
    
    
                    //第三步:获取cookie中的值
                    long lastLoginTime = Long.parseLong(cookie.getValue());
                    Date date = new Date(lastLoginTime);
                    out.write(date.toLocaleString());
                }
            }
        }else{
    
    
            out.write("这是您第一次访问本站");
        }

        //第四步:服务器给客户端响应一个cookie:
        Cookie cookie = new Cookie("lastLoginTime",String.valueOf(System.currentTimeMillis()));

		//第五步:设置cookie的有效期
        cookie.setMaxAge(24*60*60);//cookie有效期为一天
		//第六步:响应给客户端一个cookie
        resp.addCookie(cookie);
    }

cookie一般会保存在用户目录下appdata的文件下。
一个cookie只能保存一个信息
一个web站点可以给浏览器发送多个cookie,每个站点最多存20个。
cookie大小限制为4kb
300个cookie浏览器上限

删除cookie:
1)不设置有效期,关闭浏览器,自动失效。
2)设置有效期时间为0。(新建另一个demo文件)

//解决乱码:编码
Cookie cookie = new Cookie("name",URLEncoder.encode("公孙离""utf-8"));
//对应的在取cookie时候,解码
URLDecoder.decode(cookie.getValue(),"utf-8");

7.4 Session(重点)

服务器会给每一个用户(浏览器)创建一个Session对象。
一个session独占一个浏览器,只要浏览器没有关闭,这个session就存在。
用户登录之后,整个网站它都可以访问。 它保存用户的信息,保存购物车的信息,等等。

session和cookie的区别:
cookie是把用户的数据写给用户的浏览器,浏览器保存(可有多个)。
session是把用户的数据写到用户独占的session中,服务器保存(保存重要信息,减少服务器资源的浪费)。
session对象由服务器创建。

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //解决乱码问题
        resp.setCharacterEncoding("utf-8");
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");//响应回去是html页面

        //得到session
        HttpSession session = req.getSession();

        //给session中存东西,session存(string, object),而cookie(string, string)
        session.setAttribute("name",new Person("公孙离",15));

        //获取session的id
        String sessionID = session.getId();

        //判断session是不是新创建的
        if (session.isNew()){
    
    
            resp.getWriter().write("session创建成功,ID:"+ sessionID);
        } else{
    
    
            resp.getWriter().write("session已经在服务器中存在了,ID:"+ sessionID);
        }

        //session创建的时候做了什么事
        //Cookie cookie = new Cookie("JSESSIONID", sessionID);
        //resp.addCookie(cookie);

    }

在这里插入图片描述

//跨servlet获取信息
@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //解决乱码问题
        resp.setCharacterEncoding("utf-8");
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");//响应回去是html页面

        //得到session
        HttpSession session = req.getSession();

        //给session中存东西,session存(string, object),而cookie(string, string)
        Person person = (Person) session.getAttribute("name");

        System.out.println(person);
    }

删除session方法1:手动注销

//删除session
@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //得到session
        HttpSession session = req.getSession();
        session.removeAttribute("name");//取消
        session.invalidate();//注销,sessionId就没了,但会立刻生成一个新的
}

删除session方法2:在web.xml中配置(二者可同时使用)

<!--设置session失效时间-->
    <servlet>
        <servlet-name>SessionDemo03</servlet-name>
        <servlet-class>com.yyz.servlet.SessionDemo03</servlet-class>
    </servlet>
    <servlet-mapping>
        <!--15分钟后session自动失效-->
        <servlet-name>SessionDemo03</servlet-name>
        <url-pattern>/s3</url-pattern>
    </servlet-mapping>

session使用场景:
1)保存一个用户的登陆信息
2)购物车信息
3)在整个网站中,经常会使用的数据,我们将它保存在session里。

cookie:
在这里插入图片描述
session:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43378019/article/details/110793876