Servlet学习

Servlet

一、软件架构

在软件发展过程中,有两种基本的软甲架构。CS(客户端/服务器)和BS(浏览器/服务器)。

1.1 CS架构

Client/Server,客户端/服务器程序

优点:

  • 图像化的效果较好(3D等)

缺点:

  • 需要下载客户端
  • 升级不仅需要升级服务器,而且还需要客户升级客户端

1.2 BD架构

Brower/Server,浏览器/服务器程序

优点:

  • 无需下载客户端,任意系统只要有浏览器就可使用
  • 更新比较方便,只需要升级服务器即可

缺点:

  • 图像化效果不好

二、服务器

静态资源:HTML、CSS、JS

动态资源:Servlet、JSP

在Java中,动态资源的开发技术统称为Java Web。

Web服务器:用来运行和发布Web应用的容器

web项目必须放到Web容器(Web服务器)中才能运行,才能使网络中的用户使用浏览器访问

常用的Web服务器:

  • 开源免费
    • Tomcat
    • Jetty
    • Resin
  • 收费
    • WebLogic(BEA,后来被Oracle收购)
    • WebSphere(IBM)
    • 功能强大,耗资源

三、Tomcat服务器

Tomcat是Apache的一个核心项目。目录结构如下:

bin:可执行文件

conf:配置文件

lib:第三方库,jar包

logs:日志文件

temp:临时文件

webapps:开发的代码打包后放的位置

  • ROOT:默认项目

work:JSP转译成servlet时,源代码和字节码的位置

启动(两种方式):

  • 解压后打开bin目录,双击startup.bat文件(会启动一个新的cmd界面来启动服务器)
  • 在bin目录中,在路径框输入cmd,进入cmd后输入catalina.bat run后启动服务器(会在当前cmd启动服务器)

访问:

  • 启动后,打开浏览器,输入:http://localhost:8080,就可以访问首页(ROOT项目),也可以输入http://ip地址:8080访问他人的服务器

停止:

  • 强制关闭cmd
  • 使用bin目录下的shutdown.bat停止

修改端口号:

  • 打开conf文件夹,打开server.xml,修改以下内容

        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
    

    port修改成想要的端口号,只要端口不冲突就可,修改后需要重启服务才有效

404:报错信息,找不到页面(可能是服务器启动错误)

在这里插入图片描述

四、Servlet概述

是指运行在服务器端的一段程序

  • 动态网页技术
  • 是JavaWeb的基础
  • 用来处理客户端的请求,并完成响应

作用:

  • 处理客户端的请求
  • 在服务端动态生成网页,并返回给客户端
  • 直接跳转已经存在的网页

五、开发步骤

  1. 导入包servlet-api.jar。(创建Java Enterprise工程时,会自动导入tomcat的包)
  2. 编写Servlet
  3. 在web.xml中配置Servlet的访问路径
  4. 将程序部署到tomcat中,并运行

客户端如何发送请求:

  • 表单提交(action属性设置服务器的路径)
  • 链接
  • 使用js的localtion.href

服务端响应:

  • 服务端处理:使用Java代码进行相应处理(业务逻辑,数据库操作)
  • 服务器返回信息:
    • 动态生成网页,并显示
    • 跳转到已经存在的网页

5.1 配置环境

配置IDEA中Tomcat

需要先将该项目变成web项目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

配置IDEA中Servlet

在这里插入图片描述

5.2 基本使用

现在我们编写时,不提交给数据库,分为三步

  • 编写前端网页文件(.html文件)
  • 编写servlet的实现类,因为tomcat会调用servlet接口的实现类(.class文件)
  • 编写servlet接口的实现类和提交地址的映射,让其前端能够提交给正确的地址。(web.xml中编写)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--这里提交时,地址不加/-->
    <form action="a.lmz" method="post">
        <input type="text" name="username" placeholder="用户名"><br>
        <input type="password" name="pwd" placeholder="密码"><br>
        <input type="submit" value="提交" />
    </form>
</body>
</html>

LoginDemo1.java,需要实现Servlet接口

//需要实现Servlet接口才能在Tomcat启动时被调用
public class LoginDemo1 implements Servlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {}

    @Override
    public ServletConfig getServletConfig() {return null;}

    //这个方法是核心方法(只需要重写这一个方法)
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        //request是前端请求,response表示后端响应
        //得到请求中name=username的值
        String username = request.getParameter("username");
        //得到请求中name=pwd的值
        String pwd = request.getParameter("pwd");

        //此处可以先不写,把其他的写了运行一遍看,会出现后端响应给前端的文字乱码,所以这里是将响应的字符编码改为utf-8
        response.setContentType("text/html;charset=utf-8");
        //获得响应的输出流(可以将响应输出给前端)
        PrintWriter out = response.getWriter();
        if (username.equals("zhangsan")&&pwd.equals("123456")){
            //这个是打印到控制台
            System.out.println("登录成功success");
            out.println("登录成功success");
        }else{
            System.out.println("登录失败fail");
            out.println("登录失败fail");
        }
    }

    @Override
    public String getServletInfo() {return null;}

    @Override
    public void destroy() {}
}

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">

<!--    这里需指向编写的Servlet,设置一个url-pattern作为地址给前端提交-->
    <servlet>
<!--   这里名称可以随意,与下面mapping中的name一致即可,这两个相关联,即将这个类对应/a.lmz地址-->
        <servlet-name>lmz</servlet-name>
<!--        指向哪个Servlet-->
        <servlet-class>com.lmz.day1.LoginDemo1</servlet-class>
    </servlet>
<!--    类映射地址-->
    <servlet-mapping>
<!--        这里name与上面一致表示映射-->
        <servlet-name>lmz</servlet-name>
<!--        注意:这里的地址前面必须加/,否则会报错,这里地址给前端请求时使用-->
        <url-pattern>/a.lmz</url-pattern>
    </servlet-mapping>
</web-app>

六、HTTP协议

超文本传输协议。运行于TCP的协议之上

  • 基于请求响应模式
  • 无状态协议
  • HTTP1.1

请求报文。(发送请求时的格式)

  • 请求头
    • 方法:GET或者POST
    • 请求url(包含GET提交的表单信息)
    • 协议的版本HTTP1.1
    • IP地址和端口号
  • 请求正文
    • POST提交的表单信息

响应报文

  • 响应头
    • 协议版本
    • 状态行,例如:200(状态码) OK(描述)
    • 返回类型。(text/html;charset=utf-8)
    • 内容长度
  • 响应正文
    • 内容

常见的状态码:

  • 200 ,OK,成功
  • 400,参数类型格式转换错误(在springMVC框架中比较常见)
  • 403,Forbidden,接收到请求,但是不提供服务
  • 404,Not Found,页面未找到
  • 500,服务器代码出错

七、Servlet核心接口和类

Servlet接口

  • init:初始化数据
  • getServletConfig:获取servlet配置信息
  • getServletinfo:获取servlet描述信息
  • service:核心方法,作用是用来处理请求和返回响应
  • destroy销毁前执行(这里的销毁是关闭服务器)

GeneicServlet抽象类

  • 实现了Servlet接口
  • 将除了service方法以外的其他方法进行近乎空的实现,作用是可以更容易编写Servlet(因为只有service为抽象方法,所以继承该抽象类只需要重写service方法即可)

HTTPServlet抽象类

  • 继承了GenericServlet类,并实现了service方法,该方法将HTTP协议加入了
  • 并在service方法中,将请求的不同种类进行了分支,get请求执行doGet方法,post请求执行doPost方法等。(可查看底层源码)
  • 提供doGet、doPost等方法,并在这些方法中写了发送错误的代码,在子类中如果不发送响应的请求,可以不重写该方法,但是如果发送了请求而没有重写该方法,则会报错。

综上所述,一般情况下,编写Servlet应该继承HTTPServlet,并重写doGet、doPost等方法

经典面试题:

如果在继承了HttpServlet的类中,重写了doGet和doPost以及service方法,那么发送一个get请求,会调用哪个方法?

  • service方法。(因为请求过来会调用service方法,没有重写service方法的情况下,父类的service方法将请求不同种类进行了分支;当你重写了service方法后,父类的service方法就不会被执行,就会执行重写后的方法)(底层源码可见)

八、Servlet的配置

  1. 使用web.xml进行配置
    • 在配置文件中配置,可以兼容旧版本
    • 可以在生产环境下修改
    • 可以对第三方写的Servlet进行配置
<!-- 给servlet起个名字-->
    <servlet>
        <servlet-name>lmz</servlet-name>
        <servlet-class>com.lmz.day1.LoginDemo1</servlet-class>
    </servlet>
<!-- 给对应名字的servlet一个访问路径,路径必须加/-->
    <servlet-mapping>
        <servlet-name>lmz</servlet-name>
        <url-pattern>/a.lmz</url-pattern>
    </servlet-mapping>
  1. 使用注解配置
    • 配置简单
    • 只有Servlet3.0以上的版本才可以使用

注意:

  • 当使用第一种方法进行配置后,就不需要使用第二种方法配置,当两种方法都配置且路径一样时,就会起不起来服务。
  • 当使用第二种方法配置后,括号中必须写路径,否则日志中也会报错,就算该Servlet没有被使用
//继承HttpServilet类,不需要重写service方法,因为底层帮我们重写该方法了且区分了请求类型,只需要重写请求类型的方法即可
//此处使用注解来配置Servlet
@WebServlet("/login")
public class LoginDemo1 extends HttpServlet {
    //即使你没有使用get请求,避免被使用,可以将该请求转给dopost
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }

    //因为表单使用的post请求,所以需要重写post方法
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String pwd = request.getParameter("pwd");

        //响应的字符编码改为utf-8
        response.setContentType("text/html;charset=utf-8");

        PrintWriter out = response.getWriter();
        if (username.equals("zhangsan")&&pwd.equals("123456")){
            System.out.println("登录成功success");
            out.println("登录成功success");
        }else{
            System.out.println("登录失败fail");
            out.println("登录失败fail");
        }
    }
}

注意:1.Servlet配置路径时,在配置的地方一定要加/。否则会出现Invalid <url-pattern> ls.do in servlet mapping错误,导致项目无法启动

  1. 两个Servlet配置的路径不能相同。否则会出现 The servlet named [com.lmz.day1.LoginDemo1] and [com.lmz.day1.LoginDemo2] are both mapped to the url-pattern [/login] which is not permitted,导致项目无法启动
  2. 如果在控制台中没有报错信息,应该点击旁边的两个Tomcat相关的Log窗口查看错误信息
  3. 在页面发送该路径请求时不要加/,否则会出现无法访问或404.

Servlet默认是在第一次访问时由容器创建对象。仅创建一个对象,但是一致持续提供服务。

load-on-startup默认为-1,表示第一次访问时才创建对象,可以设置为0或者正整数,以使容器启动时就可以创建对象。数字越小,优先级越高(先创建)。

<!-- 给servlet起个名字-->
    <servlet>
        <servlet-name>lmz</servlet-name>
        <servlet-class>com.lmz.day1.LoginDemo1</servlet-class>
    	<load-on-startup>3</load-on-startup>
	</servlet>
<!-- 给对应名字的servlet一个访问路径,路径必须加/-->
    <servlet-mapping>
        <servlet-name>lmz</servlet-name>
        <url-pattern>/a.lmz</url-pattern>
    </servlet-mapping>
@WebServlet(value = "/login",loadOnStartup = 0)
public class LoginDemo1 extends HttpServlet {}

九、GET和POST的区别

GET:

  • 将表单数据放到url上,使用?后面拼接参数,以键值对的方式蛮多个参数使用&连接
  • 数据不安全
  • url有长度限制,所以get不能提交大量的数据信息
  • 不能用来上传文件
  • 效率相对高
  • 链接分享时,作为邮件发送时,一般用get方式

POST

  • 将表单数据放到请求正文中
  • 数据相对安全
  • post数据长度理论来说没有限制
  • 可以用来上传文件
  • 效率相对低

注意:为什么javaweb中没有main方法?

main方法在Tomcat中,当启动Tomcat,相当于启动了main方法

十、request对象和response对象

10.1 request对象

封装用户的请求。

常用方法:

  • getParameter("key"):获取请求参数
  • getParameterValues("key"):获取一组相同name 的请求参数,返回一个数组(例如:复选框,可以选择多个值 ,就需要使用该方法)
  • setCharacterEncoding("utf-8"):设置POST请求字符集

10.2 response

用来封装响应对象

  • setHeader(""):设置响应头信息,一般情况下不用设置,主要是下载时需要设置
  • setContentType(""):设置响应的格式
  • setCharacterEncoding(""):设置响应编码。在响应格式中可以设置编码,不需要通过此代码设置
  • getWriter():获取页面输出流

响应输出乱码问题解决:response.setContentType("text/html;charset=utf-8");

10.3 案例

  • 在数据库中建立一个Student表,表中字段有id,name,password,sex,age,hobby,phone字段,且id可以设置为自动增长,用户注册时不用输入id。
  • 前端网页让用户输入用户信息,提交后保存到Student表中,提交后显示提交成功(可以将用户输入的信息响应给用户看)

在这里插入图片描述

在这里插入图片描述

10.3.1 前端

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2>用户注册</h2>
    <form action="register" method="post">
        用户名:<input type="text" name="username" placeholder="用户名"><br>&nbsp;&nbsp;码: <input type="password" name="pwd" placeholder="密码"><br>&nbsp;&nbsp;龄: <input type="text" name="age" value="18" /><br>&nbsp;&nbsp;别: <input type="radio" name="sex" value="" checked>&nbsp;&nbsp;<input type="radio" name="sex" value=""><br>&nbsp;&nbsp;好: <input type="checkbox" name="hobby" value="吃饭">吃饭
        <input type="checkbox" name="hobby" value="睡觉">睡觉
        <input type="checkbox" name="hobby" value="打豆豆">打豆豆<br>&nbsp;&nbsp;话: <input type="text" name="phone" placeholder="电话"><br>

        <input type="submit" value="提交" /> <input type="reset" value="重置" />
    </form>
</body>
</html>
10.3.2 后端
10.3.2.1 Servlet
//此处使用注解配置Servlet
@WebServlet("/register")
public class LoginServlet extends HttpServlet {
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收请求参数时需要设置字符集,否则会乱码
        request.setCharacterEncoding("utf-8");
        //响应字符集也需要设置(如果响应有文字
        response.setContentType("text/html;charset=utf-8");

        //当请求只有一个参数时,使用getParameter
        String username = request.getParameter("username");
        String pwd = request.getParameter("pwd");
        String age = request.getParameter("age");
        String sex = request.getParameter("sex");
        String phone = request.getParameter("phone");
        //当请求可能有多个参数时,使用getParameterValues
        String[] hobby = request.getParameterValues("hobby");

        //得到请求数据后,需要将这些数据交给Service层,然后Service层调用DAO层存到数据库
        //将数据封装成对象
        Student student = new Student();
        student.setName(username);
        student.setPwd(pwd);
        student.setAge(age);
        student.setSex(sex);
        student.setPhone(phone);
        //因为hobby是字符串数组,所以需要转成字符串
        student.setHobby(String.join(",",hobby));

        StudentServiceImpl studentService = new StudentServiceImpl();
        boolean save = studentService.save(student);

        if (save){
            response.getWriter().println("注册成功");
        }else{
            response.getWriter().println("注册失败");
        }
    }
}
10.3.2.2 Utils

常量接口数据库连接

public interface Constants {
    String STUDENTSAVE = "insert into student(name,`password`,age,sex,hobby,phone) VALUES (?,?,?,?,?,?)";
}
public class DBConnection {
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal();
    private static final Properties PROPERTIES = new Properties();
    private static DataSource dataSource;

    static {
        InputStream inputStream = DBConnection.class.getResourceAsStream("/druid.properties");
        try {
            PROPERTIES.load(inputStream);
            dataSource = DruidDataSourceFactory.createDataSource(PROPERTIES);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static DataSource getDataSource() {
        return dataSource;
    }


    public static Connection getConnection() throws SQLException {
        Connection connection = threadLocal.get();
        if (connection == null || connection.isClosed()) {
            connection = dataSource.getConnection();
            threadLocal.set(connection);
        }
        return connection;
    }

    public static void close() throws SQLException {
        Connection connection = threadLocal.get();
        if (connection != null && !connection.isClosed()) {
            connection.close();
            threadLocal.remove();
        }
    }

}
10.3.2.3 entity
//实例类
public class Student {
    private int id;
    private String name;
    private String pwd;
    private String age;
    private String sex;
    private String hobby;
    private String phone;

    //此处省略有参构造,无参构造,toString,getter和setter方法
}
10.3.2.4 Service

接口和接口的实现类

public interface StudentService {
    boolean save(Student student);
}
public class StudentServiceImpl implements StudentService {
    StudentDAO studentDAO = new StudentDAOImpl();
    @Override
    public boolean save(Student student) {
        try {
            return studentDAO.add(student);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return false;
    }
}
10.3.2.5 DAO

DAO接口和DAO接口的实现类

public interface StudentDAO {
    boolean add(Student student) throws SQLException;
}
public class StudentDAOImpl implements StudentDAO {
    //我们没有使用事务,所以直接将连接传给queryRunner来管理,所以不需要关闭
    private QueryRunner queryRunner = new QueryRunner(DBConnection.getDataSource());
    @Override
    public boolean add(Student student) throws SQLException {
        //name,`password`,age,sex,hobby,phone
        return queryRunner.update(Constants.STUDENTSAVE,
                student.getName(),student.getPwd(),student.getAge(),student.getSex(),student.getHobby(),
                student.getPhone()) > 0;
    }
}

十一、转发和重定向

跳转页面

经典面试题:转发和重定向的区别

11.1 请求转发

  • 使用请求对象
  • 会将请求对象传到下一个路径,不会丢失
  • 请求路径不会发生改变
  • 只能站内转发
  • 当需要将数据放入到请求中,且在下一个路径将数据取出,可以使用
    • request.setAttribute("key",value):设置数据,键值对方式,key只能是String,值可以是任意类型
    • request.getAttribute("key"):获取数据,通过key获取。返回object类型,需要强转
    • request.removeAttribute("key"):删除request中的数据。

语法:

request.getRequestDispatcher("路径").forward(request,response);
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String username = req.getParameter("username");
    System.out.println("a.do=====" + username);
    //可以在转发时设置数据让转发后的路径取值
    req.setAttribute("password","123");
    //通过getRequestDispatcher将参数传递给b.do
    req.getRequestDispatcher("b.do").forward(req,resp);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //获取请求的参数
    String username = req.getParameter("username");
    //获取设置的数据
    String password = (String)req.getAttribute("password");
    System.out.println("b.do=====" + username);
    System.out.println("b.do=====" + password);
    //删除请求中设置的数据
    req.removeAttribute("passsword");
}

在这里插入图片描述

11.2 重定向

  • 使用响应对象

  • 相对于a链接

  • 丢失请求的所有信息

  • 会显示新的路径

  • 可以重定向到其他网站(例如:百度等)

  • 如果要实现数据传递,可以使用get方式,将此参数放到url中(相当于在url中用?拼接数据),但是只能是基本数据类型和string,然后新的路径通过request.getParameter()获取。不推荐使用此方式

语法:

response.sendRedirect("路径");
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //重定向到b.do并传递数据给b.do,因为重定向会丢失所有信息,所以我们可以使用get方式拼接信息
    resp.sendRedirect("b.do?id=1");
}
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String id = req.getParameter("id");
    System.out.println("b.do====" + id);
}

在这里插入图片描述

十二、Servlet生命周期

是指Servlet从创建到销毁过程中的状态

  • 创建:对象的创建过程,只会执行一次,默认为第一次访问时执行
  • 初始化:init方法,只会执行一次
  • 服务:service方法,用来处理用户的请求和返回响应。核心方法,会执行多次
  • 销毁:destroy,只会执行一次,在对象被销毁前执行

十三、Servlet特性

经典面试题:简述Servlet的单实例多线程

  • servlet对象由容器创建,只会创建一个,默认第一次访问时创建,可以通过设置load-on-startup的值来让servlet在容器启动时就创建
  • 只有一个对象,却提供多个请求同时访问的效果,说明它是多线程访问的
  • 如果在servlet中设置属性,则该属性是多线程共享,可能会出现线程安全问题
  • 应该尽量避免上述情况发生,如果有,可以通过线程同步的方式来解决该问题,但是可能因此而降低性能

十四、状态管理

HTTP协议是无状态的,服务器不记录客户端的信息。

状态管理是指服务器能够记录客户端的信息。将一个客户端的多次操作关联起来

有两种方式来解决此问题:

  • 客户端技术:在客户端记录用户信息,每次给服务器发送请求时,将客户端记录的信息一起发送。cookie技术,在客户端以文本明文的方式记录信息,不安全,大小有限制(4kb),类型只能是字符串。
  • 服务器技术:session技术。在服务器记录用户信息,但是将用户身份编码发送到客户端,客户端将该id记录在cookie中,每次给服务器发送请求时,将客户端记录的id一起发送,服务器接收id后,查找该id对应的信息,相对安全,大小理论没有限制,类型没有限制。

14.1 cookie的使用

在这里插入图片描述

@WebServlet("/login.do")
public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String pwd = request.getParameter("pwd");

        //判断是否登录成功
        if ("admin".equals(username) && "123456".equals(pwd)) {
            //如果成功,就将信息保存到cookie中
            //创建cookie
            Cookie cookie = new Cookie("username", username);

            //如果是中文,就需要进行编码(因为此处我们用的固定的用户名和密码,所以这里使用中文来测试)
            username = URLEncoder.encode("张三", "utf-8");
            System.out.println(username);
            //将username变成cookie对象,键值对形式,且都必须是字符串
            cookie = new Cookie("username",username);

            //设置路径PATH
            cookie.setPath("/");
            //设置生命周期
            //单位为秒,表示该cookie保存的时长
            //如果设置0,表示立即失效,即删除
            //如果为-1,表示浏览器关闭后就会失效
            cookie.setMaxAge(2 * 60);//两分钟
            //响应到客户端(用response将cookie添加)
            response.addCookie(cookie);
            //使用转发和重定向都可
            response.sendRedirect("home.do");
        } else {
            request.setAttribute("fail", "账号或密码错误");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}
@WebServlet("/home.do")
public class HomeServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = null;
        //获取所有cookie
        Cookie[] cookies = req.getCookies();
        if (cookies != null){
            //在cookie中获取key为username对应的value
            for (Cookie cookie : cookies) {
                if ("username".equals(cookie.getName())){
                    //默认取值为英文
                    username = cookie.getValue();
                    //如果是中文,需要反编译(解码)
                    username = URLDecoder.decode(username,"utf-8");
                }
            }
        }

        //当没有cookie中没有对应的value时,就跳转到login.html登录界面(当cookie中没有username信息,且直接网址进入home.do时,就为true)
        if (username == null){
            resp.sendRedirect("login.jsp");
        }else {
            //否则就直接使用cookie保存的用户名登录主页(当直接网址进入home.do时,且cookie中有信息,就可以直接进入home.jsp)
            req.setAttribute("username",username);
            //因为这里使用的转发,所以网址不会跳转到home.jsp
            req.getRequestDispatcher("home.jsp").forward(req,resp);
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>欢迎进入,${username}</h1>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <div style="color: red">${fail}</div>
    <form action="login.do" method="post"><br>
        用户名:<input type="text" name="username" placeholder="用户名"><br>
        密码:<input type="password" name="pwd" ><br>
        <input type="submit" value="登录"><br>
    </form>
</body>
</html>

优点:

  • 可配置到期时间
  • 简单,使用键值对操作
  • 数据持久,即使关机后,再开机还保存

缺点:

  • 大小受限制,根据浏览器不同,有4k和8k的限制
  • 可能会被用户禁用
  • 存在风险,可能会被篡改

14.2 sesson使用

session称为会话,一次会话表示使用同一个浏览器发送多次请求,浏览器关闭,则结束会话。

创建session对象:

//创建Session对象,获取Session
HttpSession session = request.getSession();
//存数据,这里key和value,key是字符串,value可以是任意对象
session.setAttribute("key",value);
//获取数据,根据key获取value
session.getAttribute("key");
//删除数据
session.removeAttribute("key");

作用域:

  • request作用域:作用在同一个请求之内,(可以是使用请求转发的多个路径)
  • session作用域:在同一个浏览器多次发送请求时,所有路径均有效,除非浏览器关闭,或者主动销毁session
    • 失效方式:
        1. 关闭浏览器
        1. 超时:session默认有效时间为30分钟,可以通过session.setMaxInactiveInterval(second)来设置。当一直没有向服务器发送请求,时间超过了超时时间,会失效。
        1. 注销:request.getSession().invalidate();

经典面试题:removeAttribute与invalidate的区别?

  • removeAttribute是清空当前session中指定的属性,下个request中的sessionID是不变的,session还是原来的session;

  • invalidate就是销毁此session对象,session对象中绑定的那些对象值也都不存在了,session改变;

在这里插入图片描述

@WebServlet("/login.do")
public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String pwd = request.getParameter("pwd");

        //判断是否登录成功
        if ("admin".equals(username) && "123456".equals(pwd)) {
            //创建Session对象,获取Session
            HttpSession session = request.getSession();
            //存数据,这里key和value,value可以是任意对象
            session.setAttribute("username","张三");
            //可以使用重定向或者转发,因为session是任何地方都可以访问
            response.sendRedirect("home.jsp");
        } else {
            request.setAttribute("fail", "账号或密码错误");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}
@WebServlet("/logout.do")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //注销session(注销后cookie中session编号就会改变,就需要重新登录)
        request.getSession().invalidate();
        //注销结束跳转到登录界面
        response.sendRedirect("login.jsp");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>欢迎进入,${username}</h1>
    <a href="logout.do">注销</a>
</body>
</html>

注意:session中保存的数据会较为长久的驻留在服务器内存中,不应该将大量数据保存在session中,所以如果有大量数据需要传递,应该使用request对象,session一般用来保存用户身份信息、权限信息等。

14.3 ServletContext对象

ServletContext接口的对象,一般会被命名为application对象。该对象也可以存储数据。

//类似于request存数据,只不过是一直有效,除非服务器重启或者删除
ServletContext application = request.getServletContext();
//存数据,这里key和value,key是字符串,value可以是任意对象
application.setAttribute("key",value);
//获取数据,根据key获取value
application.getAttribute("key");
//删除数据
application.removeAttribute("key");

在整个服务器上有效,意味着常驻内存。而且对所用用户有效。除非重启服务器或者通过removeAttribute删除,否则一直存在。所以一般用来保存服务器的设置信息等。

//访问count.html会执行
@WebServlet("/count.html")
public class CountServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取
        ServletContext application = req.getServletContext();
        //获取count的值
        Integer count = (Integer) application.getAttribute("count");

        //如果第一次进入,则count就为null,所以赋值为0
        if (count == null) count = 0;
        //次数+1
        count++;
        //设置到application中
        application.setAttribute("count", count);
        //设置打印的格式
        resp.setContentType("text/html;charset=utf-8");
        //获取打印流,直接打印,不传递到网页中
        PrintWriter out = resp.getWriter();
        //在浏览器中打印次数
        out.println("<h1>此页面被刷新了" + count + "次</h1>");
    }

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

在这里插入图片描述

十五、Filter(过滤)

在JavaWeb中,用来拦截用户请求,并根据用户的请求信息判定是否合法,如果不合法,可以停止该请求过程,将其跳转到其他路径。

步骤:

  1. 实现Filter接口,并重写doFilter方法
  2. 对其拦截路径进行配置。(类似Servlet)

常用场景:

  1. 转码(当需要将一些请求内容进行转码时,可以将其设置到Filter中过滤,此时所有请求都会被过滤)
  2. 权限拦截

两种配置方式:

  1. web.xml
    <filter>
        <filter-name>EncodingFilter</filter-name>
        <filter-class>com.lmz.demo1.filter.EncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>EncodingFilter</filter-name>
<!--        表示对所有请求都进行过滤-->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  1. 使用注解
@WebFilter("/*")
public class EncodingFilter implements Filter{}

过滤器链:

多个过滤器在项目中形成一个过滤器链,应该依次拦截请求进行过滤,通过以下代码执行。

//让过滤器链上其他过滤器执行,(放行)注意,此代码必须写在最后一行
filterChain.doFilter(servletRequest,servletResponse);

编码过滤器:

//使用注解配置
@WebFilter("/*")
public class EncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //该方法过滤用户的请求,并进行相应的处理
        //转码过滤
        servletRequest.setCharacterEncoding("utf-8");
       	//让过滤器链中的其他过滤器继续执行
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

权限过滤器

在这里插入图片描述

//使用注解配置
@WebFilter("/*")
public class AuthFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //权限过滤,当用户没有登录时,让其无法访问home.jsp或者其他界面,只有登录后才让其访问
        //将请求和响应转为HttpServletRequest,才能获得session存放的用户信息,因为用户信息存放在session中
        // 网页中都可以访问session中的信息,且关闭浏览器后就需要重新登录
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //获取session中用户信息,因为用户登录成功后会将信息存入session,那么每次发送请求就去获取
        //若没有登录就访问其他的网页那么这里的username就为null
        Object username = request.getSession().getAttribute("username");

        //获取用户访问的url地址(网页地址)
        String requestURI = request.getRequestURI();
        //获取当前工程名称
        String path = request.getContextPath();

        //当访问的地址是登录界面或首页,或直接访问的网站,或各种资源(例如css、js、Ajax请求等等)就不进行过滤操作
        //这里可以将不需要过滤的资源全部列出,那么这些资源就不会被过滤掉
        if (requestURI.endsWith("/login.do") || requestURI.endsWith("/login.jsp")
                || requestURI.endsWith("/index.jsp")
                || requestURI.endsWith(path) || requestURI.endsWith(path + "/")
                || requestURI.endsWith(".css") || requestURI.endsWith(".js")
                || requestURI.endsWith(".jpg") || requestURI.endsWith(".png")) {

        } else {
            //过滤,判断用户是否直接登录的其他网页,是则直接让其跳转到登录页面,提示用户需要先登录才能访问此网页
            if (username == null) {
                request.setAttribute("msg", "请先登录");
                request.getRequestDispatcher("login.jsp").forward(request, response);
                //一定要return退出,否则他还会继续往后面执行,那么就没有达到过滤的效果
                return;
            }
        }
        //进行链式过滤,如果后面没有过滤条件了就会执行对应的Servlet
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
@WebServlet("/login.do")
public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String pwd = request.getParameter("pwd");
        System.out.println(111111);
        //判断是否登录成功
        if ("admin".equals(username) && "123456".equals(pwd)) {
            //创建Session对象,获取Session
            HttpSession session = request.getSession();
            //存数据,这里key和value,value可以是任意对象
            session.setAttribute("username",username);
            //可以使用重定向或者转发,因为session是任何地方都可以访问
            response.sendRedirect("home.jsp");
        } else {
            request.setAttribute("msg", "账号或密码错误");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}
@WebServlet("/logout.do")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //注销session(注销后cookie中session编号就会改变,就需要重新登录)
        request.getSession().invalidate();
        //注销结束跳转到登录界面
        response.sendRedirect("login.jsp");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
}
<%--index.jsp--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
      <a href="login.jsp">登录</a>
  </body>
</html>
<%--login.jsp--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <div style="color: red">${msg}</div>
    <form action="login.do" method="post"><br>
        用户名:<input type="text" name="username" placeholder="用户名"><br>
        密码:<input type="password" name="pwd" ><br>
        <input type="submit" value="登录"><br>
    </form>
</body>
</html>
<%--home.jsp--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>欢迎进入,${username}</h1>
    <a href="logout.do">注销</a>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值