JavaWeb

1 基本概念

1.1 前言

web开发:

  • web,网页的意思,www.baidu.com
  • 静态web
    html, css
    提供给所有人看的数据始终不会发生变化
  • 动态web
    淘宝,几乎是所有的网站
    提供给所有人看的数据始终会发生变化,每个人在不同时间,不同地点看到的信息各不相同
    技术栈:Servlet/JSP, ASP, PHP
    在java中,动态web资源开发的技术统称为JavaWeb

1.2 Web应用程序

web应用程序:可以提供浏览器访问的程序;

  • a.html, b.html…多个web资源,这些web资源可以被外界访问,对外界提供服务;
  • 能访问到的任何一个页面或资源,都存在于计算机上。
  • URL
  • 统一的web资源会被放在同一个文件夹下,web应用程序–>Tomcat:服务器
  • 一个web应用由多部分组成(静态web,动态web)
    html,css,js
    jsp, servlet
    java程序
    jar包
    配置文件(properties)
    web应用程序编写完毕后,若想提供给外界访问,需要一个服务器来统一管理;

1.3 静态web

  • *.html, *.htm,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以直接进行读取。

    请求:浏览器地址栏输入URL,点击回车的操作叫做请求(客户端→服务器)
    响应:点击回车后出现的页面叫做响应(服务器→客户端)
  • 静态web存在的缺点
    web页面无法动态更新,所有用户看到的都是同一个页面
      - 轮播图,点击特效:伪动态
      - JavaScript[实际开发中应用最多]
      - VBScript
    无法和数据库交互——数据无法持久化,用户无法交互

1.4 动态web

页面会动态展示:“web页面的展示的效果因人而异”
在这里插入图片描述
WebServer 插件可以筛选安全请求,然后根据请求资源(静态动态)不同,分为两条线去寻找,最终还是将资源通过WebServer返回。
缺点:

  • 加入服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序,重新发布
    • 停机维护

优点:

  • web页面可以动态更新,所有用户看到的都不是同一个页面
  • 可以数据库交互——数据持久化,用户交互
    在这里插入图片描述

2 web服务器

2.1 技术讲解

ASP

  • 微软:国内最早流行的,
  • 在HTML中嵌入了VB的脚本,ASP + COM
  • 在ASP开发中,基本一个页面都有几千行业务代码,页面极其混乱
  • 维护成本高
  • C#
  • IIS
    PHP
  • PHP开发速度很快,功能很强大,跨平台,代码简单——70%网站都是中小型网站,开源WP
  • 无法承载大访问量的情况——局限性
    JSP(本质Servlet)
  • sun公司主推的B/S架构
  • 基于Java语言的(所有的大公司,或者一些开源的组件,都是用Java写的)
  • 可以承载三高问题带来的影响(高并发,高可用,高性能)

等等

2.2 web服务器

服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息;
IIS
微软的;ASP;windows中自带的
Tomcat
Tomcat是Apache的一个核心项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个JavaWeb初学者来说,它是最佳选择
Tomcat 实际上运行JSP 页面和Servlet。目前Tomcat最新版本为9.0
工作3-5年之后,可以尝试手写Tomcat服务器
下载Tomcat:
1.安装or解压
2.了解配置文件以及目录结构
3.了解其作用

3 Tomcat

3.1 安装Tomcat

Tomcat官网:http://tomcat.apache.org/
下载
在这里插入图片描述

3.2 Tomcat的启动和配置

文件夹信息
在这里插入图片描述
启动Tomcat
startup.bat
可能出现的问题:
1.Java环境变量没有配置
2.闪退问题
3.乱码问题:配置文件中设置

3.3 配置

服务器核心配置文件
在这里插入图片描述
可以配置启动的端口号

  • tomcat的默认端口号是8080
  • mysql3306
  • http80
  • https443
<Connector port="8070" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF-8"/>

配置主机的名称

  • 默认的主机名为localhost ->127.0.0.1
  • 默认网站应用存放位置:webapps
<Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

面试题
请你谈一谈网站是如何进行访问的?

  • 输入一个域名;点击回车
  • 检查本机的C:\Windows\System32\drivers\etc\hosts.tmp配置文件下有没有这个域名的映射
      - 有:直接返回对应的ip地址,地址中有我们需要访问的web程序,可以直接访问
      - 无:去DNS服务器上找,找到即返回,找不到即无此域名

在这里插入图片描述

3.4 发布一个web网站

  • 将自己写的网站放到服务器(Tomcat)中指定的web应用的文件夹下(webapps)下
  • 网站结构
    - webapps:Tomcat服务器的web目录
     - root
     - myWeb:网站的目录名
      - WEB-INFO
       - classes:java程序
       - lib:web应用所依赖的jar包
       - web.xml:网站的配置文件
      - index.html:默认的首页
      - static
       - css
        - style
       - js
       - img

4 Http

4.1 什么是Http

http(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上。

  • 文本:html,字符串,。。。
  • 超文本:图片音乐视频,定位,地图
  • 端口80
    Https:安全的
  • 端口443

4.2 两个时代

  • Http 1.0
     HTTP/1.0:客户端与web服务器连接后,只能获得一个web资源,断开连接
  • Http 2.0
     HTTP/1.1:客户端与web服务器连接后,可以获得多个web资源

4.3 Http请求

  • 客户端——发请求——服务器

百度:

Request URL: https://www.baidu.com/  请求地址
Request Method: GET  get方法/post方法
Status Code: 200 OK  状态码:200
Remote Address: 61.135.169.121:443 
Accept: text/html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9 语言中文
Cache-Control: max-age=0
Connection: keep-alive

1.请求行

  • 请求行中的请求方式:GET
  • 请求方式 Get/Post,Head,Delete,Put,Tract…
     get:请求能携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
     post:请求能携带的参数没有限制,大小无限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效(跟get刚好相反)

2.消息头

  1. Accept: 告诉浏览器,它所支持的数据类型
  2. Accept-Encoding: 支持哪种编码格式 GBK utf-8
  3. Accept-Language: 语言环境
  4. Cache-Control: 缓存控制
  5. Connection: 连接状态
  6. HOST:主机

4.4 Http响应

  • 服务器——响应——客户端

百度:

Cache-Control: private 缓存控制
Connection: keep-alive 连接
Content-Encoding: gzip 编码类型
Content-Type: text/html;charset=utf-8

1.响应体

  1. Accept: 告诉浏览器,它所支持的数据类型
  2. Accept-Encoding: 支持哪种编码格式 GBK utf-8
  3. Accept-Language: 语言环境
  4. Cache-Control: 缓存控制
  5. Connection: 连接状态
  6. HOST:主机
  7. Refresh:告诉客户端,多久刷新一次
  8. Location:让网页重新定位
    2.响应状态码
    200:请求响应成功
    3xx:请求重定向
     你重新到我给的新位置去
    4xx:找不到资源 404
     资源不存在
    5xx:服务器代码错误 500
     502:网关错误
    面试题:
    当你的浏览器中地址栏输入地址并回车的一瞬间到页面能展示出来,经历了什么?

5 Maven

技术需求:

  • 在JavaWeb开发中,需要使用大量jar包,手动导入
  • 如何能够让一个东西自动帮我们导入和配置jar包
  • 由此,Maven诞生

5.1 Maven项目架构管理工具

我们目前用来就是方便导入jar包的!
Maven的核心思想:约定大于配置

  • 有约束,不要去违反
    Maven会规定好你该如何去编写我们的java代码,目录结构规范

5.2 下载安装Maven

在这里插入图片描述
下载完成后,解压即可;

5.3 配置环境变量

配置环境变量:

  1. M2_HOME maven目录下的bin目录
  2. MAVEN_HOME maven目录
  3. 在系统的path中配置我们的 %MAVEN_HOME%\bin

5.4 添加阿里云镜像

作用:加速下载

<mirror>
		<id>nexus-aliyun</id>
		<mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf>
		<name>Nexus aliyun</name>
		<url>http://maven.aliyun.com/nexus/content/groups/public</url>
  </mirror>

5.5 本地仓库

建立一个仓库:localRepository

<localRepository>E:/Development_Environment/apache-maven-3.6.3/maven-repo</localRepository>

5.6 在IDEA中使用Maven

  1. 创建一个MavenWeb项目
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    包下载完成 Build Success
  2. 观察Maven仓库中多了什么东西
  3. IDEA中的Maven设置
    注意:
    在这里插入图片描述
    在这里插入图片描述

5.7 创建一个普通的Maven项目

在这里插入图片描述
在这里插入图片描述
web应用下的Maven项目
在这里插入图片描述

5.8 在IDEA中标记文件夹功能

在这里插入图片描述
标记完成
在这里插入图片描述
在这里插入图片描述
标记的另一种方式
在这里插入图片描述

5.9 在IDEA中配置Tomcat

在这里插入图片描述
在这里插入图片描述
解决警告问题:
我们访问一个网站,需要指定一个文件夹的名字;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里我们访问到的Hello World!即index.jsp中的内容
在这里插入图片描述

在这里插入图片描述

5.10 pom.xml文件

pom.xml是Maven的核心配置文件
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>

<!--Maven的版本和头文件-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <!--这里就是我们刚才配置的GAV-->
  <groupId>pers.dongyang</groupId>
  <artifactId>javaweb-01-maven01</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!--Package:项目的打包方式
    jar:java应用
    war:javaweb应用
  -->
  <packaging>war</packaging>

  <!--可删除的模块-->
  <name>javaweb-01-maven01 Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <!--配置-->
  <properties>
    <!--项目的默认构建编码-->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!--编译版本-->
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <!--项目依赖-->
  <dependencies>
    <!--具体依赖的jar包配置文件-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </dependency>
  </dependencies>

  <!--项目构建使用的东西-->
  <build>
    <finalName>javaweb-01-maven01</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <!--插件-->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

jar包及其依赖的导入
在这里插入图片描述
Maven由于约定大于配置,我们之后可能会遇到我们写的配置文件,无法被导出或生效的问题——解决方案:

    <!--在build中配置resources,来防止资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

5.12 IDEA操作

生成目录树
在这里插入图片描述

5.13 解决遇到的问题

  1. Maven导入依赖
    解决方法:使用低版本Maven依赖
  2. Tomcat闪退
    解决方法:环境变量配置问题
  3. IDEA项目中每次都要重复配置Maven
    解决方法:全局配置
    在这里插入图片描述
    在这里插入图片描述
  4. Maven中的Tomcat无法配置
    解决方法:查看是不是使用了原来版本的Maven
  5. Maven默认web项目中的web.xml版本问题
    解决方法:
    原来的:
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>

替换成Tomcat的webapps文件夹下的ROOT中的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">

  <display-name>Welcome to Tomcat</display-name>
  <description>
    Welcome to Tomcat
  </description>
</web-app>

5.14 Maven仓库的使用

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
初识Servlet
在这里插入图片描述

6 Servlet

6.1 Servlet简介

  1. Servlet就是sun公司开发动态web的技术
  2. sun公司在这些API中提供了一个接口——Servlet, 若想开发一个Servlet程序,只需完成两步
    1. 编写一个类,实现Servlet接口
    2.把开发好的Java类部署到web服务器中

把实现了Servlet接口的Java程序叫做Servlet

6.2 HelloServlet

servlet接口有两个默认的实现类:HttpServlet,GenericServlet

  1. 构建一个普通Maven项目,删掉src目录,以后我们的学习就在这个项目里面建立module;这个空的工程就叫Maven的主工程

  2. 关于Maven父子工程的理解

父项目中:

    <modules>
        <module>servlet-01</module>
    </modules>

子项目中:(parent本来没有,我自己建的)

  <parent>
    <artifactId>javaweb-03-servlet01</artifactId>
    <groupId>pers.dongyang</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>

父项目中的jar包子项目可以直接使用

son extends father
  1. Maven环境优化
    1. 修改web.xml为最新的
    2. 将Maven的结构搭建完整
  2. 编写servlet程序
    1. 编写一个普通类HelloServlet
    2. 实现servlet接口,这里我们直接继承HttpServlet
/* Java

author:GrandNovice  

time:2020/9/27

*/
package pers.dongyang.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {

    // 由于get或者post只是请求实现的不同的方式,可以相互调用,业务逻辑都一样
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // ServletOutputStream outputStream = resp.getOutputStream();
        PrintWriter writer = resp.getWriter();  // 响应流

        writer.print("Hello, Servlet!");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp); // 实现这个调用后,doGet()中的代码就不用在这里重写一遍
    }
}

  1. 编写Servlet的映射
    为什么需要映射?
    写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径
    <!--注册servlet-->
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>pers.dongyang.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
  1. 配置Tomcat
    注意项目发布路径
  2. 启动测试

6.3 servlet原理

servlet是由web服务器调用,web服务器在收到浏览器请求之后,会
在这里插入图片描述
在这里插入图片描述
浏览器发送Http请求给web容器,首次访问会编译出一个servlet的class(在target目录中),web容器产生两个对象——请求Request和响应Response,对象调用servlet接口中的service方法,service可以处理resp和req,service方法的具体实现由我们自己重写,request会从service拿到请求,并且把请求之后的响应交给response。
红色箭头
request发送到service,service中由我们自己实现的方法,方法执行完有一个响应,响应返回到response,最后web容器读取响应信息,响应给客户端输出。

6.4 Mapping问题

  1. 一个Servlet可以指定一个映射路径
    <!--servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
  1. 一个Servlet可以指定多个映射路径
    <!--servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello2</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello3</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello4</url-pattern>
    </servlet-mapping>
  1. 一个Servlet可以指定通用映射路径
    <!--servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
  1. 默认请求路径
    直接跳过index主页进入请求页面
    <!--servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
  1. 指定一些后缀或者前缀等等
    <!--可以自定义后缀实现请求映射
        注意:*前面不能加映射路径  /hello/*.dongyang 会报错
        hello/abd/asdfgh.dongyang 正常————只要是是以*.dongyang结尾即可
        测试的时候可以,写的时候不能写成前面带路径的
    -->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>*.dongyang</url-pattern>
    </servlet-mapping>
  • 优先级问题
    指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求
    <!--404-->
    <servlet>
        <servlet-name>error</servlet-name>
        <servlet-class>pers.dongyang.servlet.ErrorServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>error</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

6.5 ServletContext

6.5.1 共享数据

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

- 共享数据
我在这个servlet中保存的数据,可以在另一个servlet中拿到

写入

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // this.getInitParameter();  初始化参数
        // this.getServletConfig();  servlet配置
        // this.getServletContext();  servlet上下文  重点
        ServletContext servletContext = this.getServletContext();

        String username = "信息";  // 数据
        servletContext.setAttribute("username", username);  // 将一个数据保存在servletContext中,名字为username,值为 "董洋"


        System.out.println("Hello");
    }

}

读取

public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        String username = (String) servletContext.getAttribute("username");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().print("名字" + username);
        System.out.println("username = " + username);  // 这里在控制台输出乱码
    }

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

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">
  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>pers.dongyang.servlet.HelloServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>getContext</servlet-name>
    <servlet-class>pers.dongyang.servlet.GetServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>getContext</servlet-name>
    <url-pattern>/getc</url-pattern>
  </servlet-mapping>

</web-app>

测试访问结果。

6.5.2 获取初始化参数

配置

  <!--配置一些web应用的初始化参数-->
  <context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
  </context-param>

使用

public class servletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();

        String url = servletContext.getInitParameter("url");
        resp.getWriter().print(url);
    }

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

6.5.3 请求转发

重定向和转发的区别
转发路径不变,重定向路径会变。

public class servletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        System.out.println("进入了ServletDemo04");
        /*
        RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp");  // 转发的请求路径
        requestDispatcher.forward(req, resp);  // 调用forward()方法请求转发
        */

        // 上述两句可以合并为一句
        servletContext.getRequestDispatcher("/gp").forward(req, resp);

    }

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

    }
}

在这里插入图片描述

6.5.4 读取资源文件

Properties

  • 在java目录下新建properties——需要在该module下的pom.xml中添加代码才能正常导出.properties文件
<build>
    <resources>
      <!--resources目录下的properties本身就是可以导出的,
      因此在这里我们注释掉对resources目录导出的限制-->
<!--      <resource>-->
<!--        <directory>src/main/resources</directory>-->
<!--        <excludes>-->
<!--          <exclude>*.properties</exclude>-->
<!--          <exclude>**/*.xml</exclude>-->
<!--        </excludes>-->
<!--        <filtering>false</filtering>-->
<!--      </resource>-->
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
  </build>
  • 在resources目录下新建properties——可以正常导出
    发现都被打包到了同一个路径下:target\servlet-02-1.0-SNAPSHOT\WEB-INF\classes,我们称classes路径为类路径classpath
    使用代码更改配置使得java目录中的.properties文件也能导出

在这里插入图片描述
思路:需要一个文件流

/* Java

author:GrandNovice  

time:2020/9/27

*/
package pers.dongyang.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        InputStream resourceAsStream = this.getServletContext().
                //getResourceAsStream("/WEB-INF/classes/db.properties");  // 第一个/代表servlet-02
                //getResourceAsStream("/WEB-INF/classes/pers/dongyang/servlet/a.properties");
                getResourceAsStream("/WEB-INF/classes/a/aa.properties");
        
        Properties properties = new Properties();
        properties.load(resourceAsStream);
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");

        //resp.setCharacterEncoding("utf-8");
        resp.getWriter().print(username + ":" + password);
        System.out.println("username = " + username);
        System.out.println("password = " + password);

    }

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

访问测试即可。

6.6 HttpServletResponse响应

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

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

6.6.1 简单分类

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

  • public ServletOutputStream getOutputStream() throws IOException;
  • 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);

	public void setStatus(int sc);

响应的状态码

    /**
     * Status code (100) indicating the client can continue.
     */
    public static final int SC_CONTINUE = 100;

    /**
     * Status code (101) indicating the server is switching protocols
     * according to Upgrade header.
     */
    public static final int SC_SWITCHING_PROTOCOLS = 101;

    /**
     * Status code (200) indicating the request succeeded normally.
     */
    public static final int SC_OK = 200;

    /**
     * Status code (201) indicating the request succeeded and created
     * a new resource on the server.
     */
    public static final int SC_CREATED = 201;

    /**
     * Status code (202) indicating that a request was accepted for
     * processing, but was not completed.
     */
    public static final int SC_ACCEPTED = 202;

    /**
     * Status code (203) indicating that the meta information presented
     * by the client did not originate from the server.
     */
    public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;

    /**
     * Status code (204) indicating that the request succeeded but that
     * there was no new information to return.
     */
    public static final int SC_NO_CONTENT = 204;

    /**
     * Status code (205) indicating that the agent <em>SHOULD</em> reset
     * the document view which caused the request to be sent.
     */
    public static final int SC_RESET_CONTENT = 205;

    /**
     * Status code (206) indicating that the server has fulfilled
     * the partial GET request for the resource.
     */
    public static final int SC_PARTIAL_CONTENT = 206;

    /**
     * Status code (300) indicating that the requested resource
     * corresponds to any one of a set of representations, each with
     * its own specific location.
     */
    public static final int SC_MULTIPLE_CHOICES = 300;

    /**
     * Status code (301) indicating that the resource has permanently
     * moved to a new location, and that future references should use a
     * new URI with their requests.
     */
    public static final int SC_MOVED_PERMANENTLY = 301;

    /**
     * Status code (302) indicating that the resource has temporarily
     * moved to another location, but that future references should
     * still use the original URI to access the resource.
     *
     * This definition is being retained for backwards compatibility.
     * SC_FOUND is now the preferred definition.
     */
    public static final int SC_MOVED_TEMPORARILY = 302;

    /**
    * Status code (302) indicating that the resource reside
    * temporarily under a different URI. Since the redirection might
    * be altered on occasion, the client should continue to use the
    * Request-URI for future requests.(HTTP/1.1) To represent the
    * status code (302), it is recommended to use this variable.
    */
    public static final int SC_FOUND = 302;

    /**
     * Status code (303) indicating that the response to the request
     * can be found under a different URI.
     */
    public static final int SC_SEE_OTHER = 303;

    /**
     * Status code (304) indicating that a conditional GET operation
     * found that the resource was available and not modified.
     */
    public static final int SC_NOT_MODIFIED = 304;

    /**
     * Status code (305) indicating that the requested resource
     * <em>MUST</em> be accessed through the proxy given by the
     * <code><em>Location</em></code> field.
     */
    public static final int SC_USE_PROXY = 305;

     /**
     * Status code (307) indicating that the requested resource 
     * resides temporarily under a different URI. The temporary URI
     * <em>SHOULD</em> be given by the <code><em>Location</em></code> 
     * field in the response.
     */
    public static final int SC_TEMPORARY_REDIRECT = 307;

    /**
     * Status code (400) indicating the request sent by the client was
     * syntactically incorrect.
     */
    public static final int SC_BAD_REQUEST = 400;

    /**
     * Status code (401) indicating that the request requires HTTP
     * authentication.
     */
    public static final int SC_UNAUTHORIZED = 401;

    /**
     * Status code (402) reserved for future use.
     */
    public static final int SC_PAYMENT_REQUIRED = 402;

    /**
     * Status code (403) indicating the server understood the request
     * but refused to fulfill it.
     */
    public static final int SC_FORBIDDEN = 403;

    /**
     * Status code (404) indicating that the requested resource is not
     * available.
     */
    public static final int SC_NOT_FOUND = 404;

    /**
     * Status code (405) indicating that the method specified in the
     * <code><em>Request-Line</em></code> is not allowed for the resource
     * identified by the <code><em>Request-URI</em></code>.
     */
    public static final int SC_METHOD_NOT_ALLOWED = 405;

    /**
     * Status code (406) indicating that the resource identified by the
     * request is only capable of generating response entities which have
     * content characteristics not acceptable according to the accept
     * headers sent in the request.
     */
    public static final int SC_NOT_ACCEPTABLE = 406;

    /**
     * Status code (407) indicating that the client <em>MUST</em> first
     * authenticate itself with the proxy.
     */
    public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;

    /**
     * Status code (408) indicating that the client did not produce a
     * request within the time that the server was prepared to wait.
     */
    public static final int SC_REQUEST_TIMEOUT = 408;

    /**
     * Status code (409) indicating that the request could not be
     * completed due to a conflict with the current state of the
     * resource.
     */
    public static final int SC_CONFLICT = 409;

    /**
     * Status code (410) indicating that the resource is no longer
     * available at the server and no forwarding address is known.
     * This condition <em>SHOULD</em> be considered permanent.
     */
    public static final int SC_GONE = 410;

    /**
     * Status code (411) indicating that the request cannot be handled
     * without a defined <code><em>Content-Length</em></code>.
     */
    public static final int SC_LENGTH_REQUIRED = 411;

    /**
     * Status code (412) indicating that the precondition given in one
     * or more of the request-header fields evaluated to false when it
     * was tested on the server.
     */
    public static final int SC_PRECONDITION_FAILED = 412;

    /**
     * Status code (413) indicating that the server is refusing to process
     * the request because the request entity is larger than the server is
     * willing or able to process.
     */
    public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;

    /**
     * Status code (414) indicating that the server is refusing to service
     * the request because the <code><em>Request-URI</em></code> is longer
     * than the server is willing to interpret.
     */
    public static final int SC_REQUEST_URI_TOO_LONG = 414;

    /**
     * Status code (415) indicating that the server is refusing to service
     * the request because the entity of the request is in a format not
     * supported by the requested resource for the requested method.
     */
    public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;

    /**
     * Status code (416) indicating that the server cannot serve the
     * requested byte range.
     */
    public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;

    /**
     * Status code (417) indicating that the server could not meet the
     * expectation given in the Expect request header.
     */
    public static final int SC_EXPECTATION_FAILED = 417;

    /**
     * Status code (500) indicating an error inside the HTTP server
     * which prevented it from fulfilling the request.
     */
    public static final int SC_INTERNAL_SERVER_ERROR = 500;

    /**
     * Status code (501) indicating the HTTP server does not support
     * the functionality needed to fulfill the request.
     */
    public static final int SC_NOT_IMPLEMENTED = 501;

    /**
     * Status code (502) indicating that the HTTP server received an
     * invalid response from a server it consulted when acting as a
     * proxy or gateway.
     */
    public static final int SC_BAD_GATEWAY = 502;

    /**
     * Status code (503) indicating that the HTTP server is
     * temporarily overloaded, and unable to handle the request.
     */
    public static final int SC_SERVICE_UNAVAILABLE = 503;

    /**
     * Status code (504) indicating that the server did not receive
     * a timely response from the upstream server while acting as
     * a gateway or proxy.
     */
    public static final int SC_GATEWAY_TIMEOUT = 504;

    /**
     * Status code (505) indicating that the server does not support
     * or refuses to support the HTTP protocol version that was used
     * in the request message.
     */
    public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;

6.6.2 下载文件

  1. 向浏览器输出消息(前面已讲)
  2. 下载/上传文件
    1. 要获取下载文件的路径
    2. 下载的文件名
    3. 先办法让浏览器能够支持下载我们所需的东西
    4. 获取下载文件的输入流
    5. 创建缓冲区
    6. 获取OutputStream对象
    7. 将FileOutputStream流写入到buffer缓冲区
    8. 使用OutputStream将缓冲区中的数据输出到客户端
public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
            1. 要获取下载文件的路径
            2. 下载的文件名
            3. 先办法让浏览器能够支持下载我们所需的东西
            4. 获取下载文件的输入流
            5. 创建缓冲区
            6. 获取OutputStream对象
            7. 将FileOutputStream流写入到buffer缓冲区
            8. 使用OutputStream将缓冲区中的数据输出到客户端
         */

        // 1. 要获取下载文件的路径
        String realPath = "E:\\IDEA_project\\JavaWeb\\javaweb-03-servlet01\\response\\target\\classes\\dog.jpg";
        System.out.println("realPath = " + realPath);

        // 2. 下载的文件名
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);

        // 3. 先办法让浏览器能够支持Content-Disposition下载我们所需的东西
        // 下载文件为中文命名时,需要使用URLEncoder.encode(fileName, "utf-8") 进行编码,否则会出现下载的文件名乱码
        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流写入到buffer缓冲区  8. 使用OutputStream将缓冲区中的数据输出到客户端
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
        in.close();
        out.close();



    }

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

6.6.3 验证码功能

验证码怎么来的?

  • 前端实现
  • 后端实现(需要用到java的图片类,生成一个图片 )
public class ImageServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 如何让浏览器每1秒自动刷新一次
        resp.setHeader("refresh", "1");

        // 在内存中创建图片
        BufferedImage image = new BufferedImage(80, 60, BufferedImage.TYPE_INT_RGB);

        // 操作图片
        Graphics2D graphics = (Graphics2D) image.getGraphics();  // 定义一只画笔
        // 设置图片背景颜色
        graphics.setColor(Color.white);
        // 填充背景颜色,从(0, 0)点到(80, 60)点
        graphics.fillRect(0, 0, 80, 60);
        // 给图片写数据
        graphics.setColor(Color.blue);
        graphics.setFont(new Font(null, Font.BOLD, 20));
        graphics.drawString(generateRandomNumber(), 20, 35);

        // 告诉浏览器这个请求用图片的方式打开
        resp.setContentType("image/jpg");

        // 网站存在缓存,不让浏览器缓存
        resp.setDateHeader("expires", -1);  // expires=-1表示不会被浏览器缓存
        resp.setHeader("Cache-Control", "no-cache");  // 告诉浏览器缓存策略为不缓存
        resp.setHeader("Pragma", "no-cache");

        // 把图片写给浏览器
        ImageIO.write(image, "jpg", resp.getOutputStream());


    }

    // 生成随机数
    private String generateRandomNumber() {
        Random random = new Random();
        String randomNumber = random.nextInt(9999) + "";
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 4 - randomNumber.length(); i++) {
            sb.append("0");
        }
        String string = sb.toString() + randomNumber;
        return string;
    }

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

6.6.4 实现重定向

在这里插入图片描述
一个web资源收到客户端请求后,它会通知客户端去访问另外一个web资源,这个过程叫重定向。
常见场景:

  • 用户登录
    在这里插入图片描述
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
        // 重定向可以拆分为一下两步
        resp.setHeader("Location", "/response_war/image");
        resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
        // resp.setStatus(302);  //这句代码等于上一句
         */
        // 重定向
        resp.sendRedirect("/response_war/image");
    }

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

面试题:
重定向和转发的区别?
相同:

  • 页面都会实现跳转

不同:

  • 请求转发的时候,URL不会发生变化 Http状态码:307
  • 重定向的时候,URL会发生变化 Http状态码:302

6.7 HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,我们可以获得客户端的所有信息。
在这里插入图片描述
在这里插入图片描述

6.7.1 获取前端传递的参数

在这里插入图片描述

6.7.2 请求转发

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 后台接收以及浏览器输出中文乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbys = req.getParameterValues("hobbys");

        System.out.println("===============================");
        System.out.println("username = " + username);
        System.out.println("password = " + password);
        System.out.println(Arrays.toString(hobbys));
        System.out.println("===============================");

        // 通过请求转发
        // 这里的 / 代表当前的web应用
        req.getRequestDispatcher("/success.jsp").forward(req, resp);
    }

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

7 Session和Cookie

7.1 会话

会话:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程称为一次会话。
有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学曾经来过,这个过程称为有状态会话。
你怎么证明你是学校的学生?

  1. 学费发票 学校给你发票
  2. 学校学籍 学校标记你来过了

一个网站怎么证明你来过?
服务端怎么证明客户端已经来过了?

  • 服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了;cookie
  • 服务器登记你来过了,下次你来的时候我来匹配你;session

7.2 保存会话的两种技术

cookie

  • 客户端技术 (响应,请求)

session

  • 服务器技术,利用这个技术可以保存用户的会话信息。我们可以把信息或数据放在session中!

常见场景:
网站登录账户后,下次登录不用再输入用户名和密码,可以直接登录上去。

7.3 Cookie

在这里插入图片描述
第一次访问网站,服务器给客户端发送一个Cookie,请求的时候携带Cookie

  1. 从请求中拿到cookie信息
  2. 服务器响应给客户端cookie
// 保存用户上一次访问的时间
public class CookieDemo01 extends HttpServlet {
    @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中的值
                    String value = cookie.getValue();
                    // 解析成长整型数值,方便下一步转换成时间
                    long lastLoginTime = Long.parseLong(value);
                    Date date = new Date(lastLoginTime);
                    out.write(date.toLocaleString());
                }
            }
        } else {
            out.write("这是您第一次访问本站!");
        }

        // 服务器给客户端响应一个Cookie
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        // cookie有效期为一天
        cookie.setMaxAge(24*60*60);
        // 响应给客户端一个cookie
        resp.addCookie(cookie);
    }

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

cookie一般会保存在本地的用户目录下appdata;
一个网站的cookie是否存在上限?

  • 一个cookie只能保存一个信息
  • 一个web站点可以给浏览器发送多个cookie,每个站点最多存放20个cookie
  • 浏览器cookie上限大概为300个
  • cookie大小有限制

删除cookie

  • 不设置有效期,关闭浏览器,自动失效
  • 设置有效期时间为0
// 保存用户上一次访问的时间
public class CookieDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建一个cookie,名字跟要删除的一致
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        // 将cookie有效期设置为0
        cookie.setMaxAge(0);

        resp.addCookie(cookie);
    }

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

编码解码

// 中文数据传递
public class CookieDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决中文乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

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

        PrintWriter out = resp.getWriter();

        // 判断Cookie是否存在
        if (cookies != null) {
            // 如果存在怎么办
            // 遍历数组
            out.write("你上一次访问的时间是:");

            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                // 获取cookie的名字
                if (cookie.getName().equals("name")) {
                    // System.out.println(cookie.getValue());
                    // 解码
                    out.write(URLDecoder.decode(cookie.getValue(), "utf-8"));
                }
            }
        } else {
            out.write("这是您第一次访问本站!");
        }

        Cookie cookie = new Cookie("name", URLEncoder.encode("冬阳", "utf-8"));  // 编码
        resp.addCookie(cookie);
    }

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

7.4 Session(重点)

在这里插入图片描述

什么是Session?

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

Session和Cookie的区别

  • Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
  • Session是把用户的数据写到用户独占的Session中,服务器端保存(保存重要信息,减少服务器资源的浪费)
  • Session对象由服务器创建

使用场景:

  • 保存一个登录用户的信息
  • 购物车信息
  • 在整个网站中经常会使用的数据,我们将它保存在Session中

使用Session

public class SessionDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

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

        // 给Session中存入东西
//        session.setAttribute("name", "冬阳");
        session.setAttribute("name", new Person("冬阳", 18));

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

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

        // Session创建的时候做了什么事情
        /*
            Cookie cookie = new Cookie("JSESSIONID", sessionId);
            resp.addCookie(cookie);
        */
    }

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

public class SessionDemo02 extends HttpServlet {
    @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");

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

        Person person = (Person) session.getAttribute("name");

        System.out.println(person.toString());

    }

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

public class SessionDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();

        // 从此session中删除与指定名称绑定的对象。 如果session没有与指定名称绑定的对象,则此方法不执行任何操作。
        session.removeAttribute("name");

        // 使该session无效,然后取消绑定到该session的任何对象。手动注销Session
        session.invalidate();
    }

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

xml配置Session自动过期

<!--设置Session默认的失效时间-->
<session-config>
    <!--1分钟后Session自动失效(以分钟为单位)-->
    <session-timeout>1</session-timeout>
</session-config>

shift + tab 可以取消缩进
在这里插入图片描述

8 JSP

8.1 什么是JSP

Java Server Pages:Java服务器端页面,也和servlet一样,用于动态web技术。
特点:

  • 写JSP就像在写HTML
  • 区别:
     - Html只给用户提供静态的数据
     - JSP页面中可以嵌入JAVA代码,为用户提供动态数据

8.2 JSP原理

思路:JSP到底怎么执行?查看target

  • 代码层面没有任何问题
  • 服务器内部工作
     - Tomcat中有一个work目录;
     - IDEA中使用Tomcat的话,会在IDEA的Tomcat中生成一个work目录
    在这里插入图片描述
    本机地址:
C:\Users\dongy\.IntelliJIdea2019.2\system\tomcat\Unnamed_javaweb-session-cookie\work\Catalina\localhost\ROOT\org\apache\jsp

发现页面转变成了Java程序!
在这里插入图片描述
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问servlet
JSP最终也会被转换成为一个Java类!
在这里插入图片描述

在这里插入图片描述
JSP本质上就是一个servlet!

// 初始化
public void _jspInit() {
  }

// 销毁
public void _jspDestroy() {
  }

// JSPservice
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response){
  }
  1. 判断请求
  2. 内置一些对象
final javax.servlet.jsp.PageContext pageContext;  // 页面上下文
javax.servlet.http.HttpSession session = null;  // session
final javax.servlet.ServletContext application; // applicationContext
final javax.servlet.ServletConfig config;  // config
javax.servlet.jsp.JspWriter out = null;  // out
final java.lang.Object page = this;  // page 当前
HttpServletRequest request;  // 请求
HttpServletResponse response; // 响应
  1. 输出页面前增加的代码
response.setContentType("text/html");  // 设置响应的页面类型
pageContext = _jspxFactory.getPageContext(this, request, response,
			null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
  1. 以上的这些对象,我们可以在jsp页面中直接使用
    在这里插入图片描述

在JSP页面中,Java代码原封不动输出, Html代码会被转换,然后渲染到前端

out.write("    <title>Title</title>\r\n");

8.3 JSP的基础语法

任何语言都有自己的语法!
JSP作为Java技术的一种应用,它拥有自己扩充的语法,Java所有语法都支持!

JSP表达式

<%--注释符号不同--%>
<%--JSP表达式  <%= 变量/表达式%>
  作用:将程序的结果输出到客户端
--%>
<%=new java.util.Date()%>

JSP脚本片段

<%--JSP脚本片段--%>
<%
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
        sum += i;
    }
    out.println("<h1>sum=" + sum + "<h1>");
%>

脚本片段的再实现

<%
  int x = 10;
  out.println(x);
%>
<p>这是一个JSP文档</p>
<%
  int y = 20; // x不可重复定义
  out.println(x);
%>
<hr>

<%--在代码中嵌入html元素--%>
<%
  for (int i = 0; i < 5; i++) {

%>
<h1>HelloWorld <%=i%></h1>
<%
  }
%>

JSP声明

<%--可以定义在方法_jspService()外,类index_jsp内--%>
<%!
  static {
    System.out.println("Loading Servlet");
  }

  private int globalVar = 0;

  public void method() {
    System.out.println("进入了方法method()");
  }
%>

在这里插入图片描述
JSP声明会被编译到JSP生成的Java类中!其他的会被编译到方法_jspService()内

在JSP中嵌入Java代码即可

<%%>
<%=%>
<%!%>
<%--注释--%>

JSP的注释不会再客户端代码查看显示,HTML注释会!

8.4 JSP指令

<%--定制错误页面--%>
<%@page errorPage="error/500.jsp" %>
<%--提取公共页面--%>
<%@include file=""%>
<%--
  Created by IntelliJ IDEA.
  User: dongy
  Date: 2020/10/5
  Time: 16:44
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>Title</title>
</head>
<body>

    <%--@include会将两个页面合二为一--%>
    <%@include file="common/header.jsp"%>
    <h1>网页主体</h1>
    <%--

    <%
        int i = 10;  // 重复定义变量i,会在运行时报错
    %>

    --%>

    <%@include file="common/footer.jsp"%>

    <hr>

    <%--jsp标签--%>
    <%--jsp:include会拼接页面,本质还是三个页面,这种方式灵活性更高--%>
    <jsp:include page="common/header.jsp"/>
    <h1>网页主体</h1>
    <%
        int i = 10;  // 不同页面分别定义变量i,不会报错
    %>
    <jsp:include page="common/footer.jsp"/>

</body>
</html>

8.5 九大内置对象

  • PageContext 存东西
  • Request 存东西
  • Response
  • Session 存东西
  • Application [ServletContext] 存东西
  • config [ServletConfig]
  • out
  • page 几乎不用
  • exception
pageContext.setAttribute("name1", "冬阳1号");  // 保存的数据只在一个页面中有效
request.setAttribute("name2", "冬阳2号");  // 保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3", "冬阳3号");  // 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4", "冬阳4号");  // 保存的数据在服务器中有效,从打开服务器到关闭服务器

request:客户端向服务器发送请求产生的数据,用户看完就没用了,比如,新闻等
session:客户端向服务器发送请求产生的数据,用户用完一会还有用,比如,购物车
application:客户端向服务器发送请求产生的数据,一个用户用完了,其他用户还可能使用,比如,聊天数据

8.6 JSP标签、JSTL标签、EL表达式

EL表达式:${}

  • 获取数据
  • 执行运算
  • 获取web开发的常用对象
  • 调用java方法
<!--jstl表达式依赖-->
<dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>

<!--standard标签库依赖-->
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

JSP标签

%--
    http://localhost:8080/jsptag.jsp?name=dongyang&age=12
--%>
<jsp:forward page="jsptag2.jsp">
    <jsp:param name="name" value="dongyang"></jsp:param>
    <jsp:param name="age" value="12"></jsp:param>
</jsp:forward>

JSTL表达式
JSTL标签库的使用是为了弥补HTML标签的不足
它自定义了许多标签,可以供我们使用,标签的功能和Java代码一样

  1. 核心标签
    在这里插入图片描述
    JSTL标签库使用步骤
      1. 引入对应的taglib
      2. 使用其中的方法
      3. 在Tomcat中也需要引入jstl的包,否则会报错:JSTL解析错误
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入jstl核心标签库,我们才能使用jstl标签--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h4>if测试</h4>

<hr>

<form action="coreif.jsp" method="get">
    <%--EL表达式获取表单中的数据value="${param.参数名}"--%>
    <input type="text" name="username" value="${param.username}">
        <input type="submit" value="登录">
</form>

<%--判断如果提交的用户名是管理员则登录成功--%>
<%--
    <%
    if (request.getParameter("username").equals("admin")) {
        out.println("登录成功");
    }
%>
--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
    <c:out value="管理员欢迎您!"></c:out>
</c:if>

<c:out value="${isAdmin}"></c:out>


</body>
</html>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%--定义一个变量score,值为85--%>
<c:set var="score" value="95"></c:set>

<c:choose>
    <c:when test="${score>=90}">
        优秀
    </c:when>
    <c:when test="${score>=80}">
        良好
    </c:when>
    <c:when test="${score>=70}">
        一般
    </c:when>
    <c:when test="${score>=60}">
        一般般
    </c:when>
    <c:when test="${score<60}"></c:when>
</c:choose>

</body>
</html>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%
    ArrayList<String> people = new ArrayList<>();
    people.add(0, "张三");
    people.add(1, "李四");
    people.add(2, "王五");
    people.add(3, "赵六");
    people.add(4, "田七");

    request.setAttribute("list", people);

%>


<%--
    var:每一次遍历出来的变量
    items:要遍历的容器对象(可迭代)
    begin:开始
    end:结束
    step:步长
--%>
<c:forEach var="people" items="${list}">
    <c:out value="${people}"></c:out> <br>
</c:forEach>

<hr>

<c:forEach var="people" items="${list}" begin="1" end="3" step="2">
    <c:out value="${people}"></c:out> <br>
</c:forEach>
</body>
</html>

  • 格式化标签

  • SQL标签

  • XML标签

9 JavaBean

实体类
JavaBean有特定的写法:

  • 一个无参构造
  • 属性必须私有化
  • 必须有对应的get/set方法

一般用来和数据库的字段做映射。ORM;
ORM:对象关系映射

  • 数据库中的表——Java中的类
  • 字段——属性
  • 行记录——对象

people表

idnameageaddress
1冬阳1号16山西
2冬阳2号17山东
3冬阳3号18山南
class People{
	private int id;
	private String name;
	private int age;
	private String address;
}

class A {
	new People(1, "冬阳1", 3, "西安");
	new People(2, "冬阳2", 5, "西安");
	new People(3, "冬阳3", 6, "西安")
}

10 MVC三层架构

什么是MVC?
Model View Controller 模型、视图和控制器

10.1 早期架构

在这里插入图片描述
用户直接访问控制层,控制层就可以直接操作数据库。

servlet -> 增删改查 -> 数据库
弊端:JDBC代码要写道servlet的处理请求中,程序臃肿,不利于维护   
servlet的代码中:处理请求,响应,视图跳转,处理JDBC,处理业务代码,处理逻辑代码 

架构:没有什么是加一层解决不了的
JDBC

MySQL Oracle SqlServer

10.2 MVC三层架构

在这里插入图片描述
Model:

  • 业务处理:业务逻辑(Service)
  • 数据持久层:CRUD增删改查(Dao)

View:

  • 展示数据
  • 提供链接发起Servlet请求(a, form, herf, …)

Controller:(Servlet)

  • 接收用户的请求(Request请求参数、Session信息,…)
  • 交给业务层处理对应的代码
  • 控制视图跳转
登录 ---> 接收用户的登录请求 ---> 处理用户的请求(获取用户登录的参数username,password) ---> 交给业务层处理登录业务(判断用户名密码是否正确,事务) ---> Dao层查询用户名和密码是否正确 ---> 数据库

11 Filter

Filter:过滤器,用来过滤网站的数据;
应用场景:

  • 处理中文乱码
  • 登录验证

Filter开发步骤:

  1. 导包
  2. 编写过滤器

在这里插入图片描述

实现Filter接口,重写三个方法

/* Java

author:GrandNovice  

time:2020/10/5

*/
package pers.dongyang.filter;

import javax.servlet.*;
import java.io.IOException;

public class CharacterEncodingFilter implements Filter {
    // 初始化:web服务器启动的同时初始化,随时等待过滤对象出现
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("CharacterEncodingFilter已经初始化");
    }

    // chain 链
    /**
     * 1.过滤器中的所有代码在过滤特定请求的时候都会执行
     * 2.必须要让过滤器继续通行
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");

        System.out.println("CharacterEncodingFilter执行前");
        // 让我们的请求继续执行,如果不写,程序在这里被拦截停止
        chain.doFilter(request, response);
        System.out.println("CharacterEncodingFilter执行后");
    }

    // 销毁:web服务器关闭的时候过滤器会销毁
    public void destroy() {
        System.out.println("CharacterEncodingFilter已经销毁");
    }
}
  1. 在web.xml中配置Filter
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>pers.dongyang.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <!--只要是/servlet下的任何请求都会经过这个过滤器-->
    <url-pattern>/servlet/*</url-pattern>
</filter-mapping>

12 监听器

实习一个监听器的接口(有n种):

  1. 编写一个监听器
    实现监听器的接口
package pers.dongyang.listener;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

// 统计网站在线人数:实际是统计session
public class OnlineCountListener implements HttpSessionListener {
    // 创建session监听
    // session创建一次,触发一次这个事件
    public void sessionCreated(HttpSessionEvent se) {
        ServletContext ctx = se.getSession().getServletContext();

        System.out.println(se.getSession().getId());

        Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");

        if (onlineCount == null) {
            onlineCount = new Integer(1);
        } else {
            int count = onlineCount.intValue();
            onlineCount = new Integer(count + 1);
        }

        ctx.setAttribute("OnlineCount", onlineCount);
    }

    // 销毁session监听
    // session销毁一次,触发一次这个事件
    public void sessionDestroyed(HttpSessionEvent se) {
        ServletContext ctx = se.getSession().getServletContext();
        Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");

        if (onlineCount == null) {
            onlineCount = new Integer(0);
        } else {
            int count = onlineCount.intValue();
            onlineCount = new Integer(count - 1);
        }

        ctx.setAttribute("OnlineCount", onlineCount);
    }

    /**
     * Session销毁的两种情况:
     *      1. 手动销毁  se.getSession().invalidate();
     *      2. 自动销毁  web.xml配置
     *              <!--自动销毁session-->
     *              <session-config>
     *                  <!--以分钟为单位-->
     *                  <session-timeout>1</session-timeout>
     *              </session-config>
     */
}
  1. 在web.xml中配置监听器
<!--注册监听器-->
<listener>
    <listener-class>pers.dongyang.listener.OnlineCountListener</listener-class>
</listener>
  1. 看情况选择使用

13 过滤器监听器常见应用

监听器:GUI编程中常用

package pers.dongyang.listener;

import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

public class TestPanel {
    public static void main(String[] args) {
        Frame frame = new Frame("中秋节快乐");  // 新建一个窗体
        Panel panel = new Panel(null);  // 面板
        frame.setLayout(null);  // 设置窗体的布局

        frame.setBounds(300, 300, 500, 500);
        frame.setBackground(new Color(0, 0, 255));  // 设置背景颜色

        panel.setBounds(50, 50, 300, 300);  //
        panel.setBackground(new Color(0, 255, 0));  //

        frame.add(panel);
        frame.setVisible(true);

        // 监听事件,监听关闭事件
        frame.addWindowListener(new WindowListener() {
            public void windowOpened(WindowEvent e) {
                System.out.println("打开");
            }

            public void windowClosing(WindowEvent e) {
                System.out.println("关闭ing");
                System.exit(0);
                /*
                * System.exit(0);和System.exit(1);区别
                * 正常终止和非正常终止
                *
                *   System.exit(0) : 将整个虚拟机里的内容都关掉,内存都释放掉!正常退出程序。
                *   System.exit(1) : 非正常退出程序
                *   System.exit(-1) :非正常退出程序
                * */
        }

            public void windowClosed(WindowEvent e) {
                System.out.println("关闭ed");
            }

            public void windowIconified(WindowEvent e) {

            }

            public void windowDeiconified(WindowEvent e) {

            }

            public void windowActivated(WindowEvent e) {
                System.out.println("激活");
            }

            public void windowDeactivated(WindowEvent e) {
                System.out.println("未激活");
            }
        });
    }
}

用户登录之后才能进入主页,用户注销后就不能进入主页了!

  1. 用户登录后向Session中放入用户数据
  2. 进入主页时要判断用户是否已经登录,要求在过滤器中实现,在页面中实现会报错

过滤器

package pers.dongyang.filter;

import pers.dongyang.util.Constant;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SysFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        if (request.getSession().getAttribute(Constant.USER_SESSION)==null) {
            response.sendRedirect("/error.jsp");
        }
        filterChain.doFilter(req, resp);
    }

    public void destroy() {

    }
}

登录实现

package pers.dongyang.servlet;

import pers.dongyang.util.Constant;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取前端请求的参数
        String username = req.getParameter("username");
        if (username.equals("admin")) {
            // 登录成功
            req.getSession().setAttribute(Constant.USER_SESSION, req.getSession().getId());
            resp.sendRedirect("/sys/success.jsp");
        }else {
            // 登录失败
            resp.sendRedirect("/error.jsp");
        }
    }

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

登录页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登录 </h1>
<form action="/servlet/login" method="post">
    <input type="text" name="username">
    <input type="submit">
</form>

</body>
</html>

主页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>


<h1>主页</h1>
<p><a href="/servlet/logout">注销</a></p>

</body>
</html>

注销实现

package pers.dongyang.servlet;

import pers.dongyang.util.Constant;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LogoutServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object user_session = req.getSession().getAttribute(Constant.USER_SESSION);  // 属性没了,session还在

        if (user_session!=null) {
            req.getSession().removeAttribute(Constant.USER_SESSION);
            resp.sendRedirect("/Login.jsp");
        }else {
            resp.sendRedirect("/Login.jsp");
        }
    }

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

Session常量

/* Java

author:GrandNovice  

time:2020/10/6

*/
package pers.dongyang.util;

public class Constant {
    public final static String USER_SESSION = "USER_SESSION";
}

错误页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h1>错误页面</h1>
<p><a href="/Login.jsp">返回登录页面</a></p>
</body>
</html>

web.xml

    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>pers.dongyang.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/servlet/login</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>LogoutServlet</servlet-name>
        <servlet-class>pers.dongyang.servlet.LogoutServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LogoutServlet</servlet-name>
        <url-pattern>/servlet/logout</url-pattern>
    </servlet-mapping>
    
    <filter>
        <filter-name>SysFilter</filter-name>
        <filter-class>pers.dongyang.filter.SysFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>SysFilter</filter-name>
        <url-pattern>/sys/*</url-pattern>
    </filter-mapping>

在这里插入图片描述
扩展:VIP123页面划分

14 JDBC

什么是JDBC?
Java DataBase Connectivity
在这里插入图片描述
需要jar包支持

  • java.sql
  • javax.sql
  • mysql-connector-java…

实验环境搭建

CREATE TABLE users(
	id INT PRIMARY KEY,
	`name` VARCHAR(40),
	`password` VARCHAR(40),
	`email` VARCHAR(60),
	`birthday` DATE
);

INSERT INTO `users`(`id`, `name`, `password`, `email`, `birthday`)
VALUES(1, '张三', '123456', 'zs@qq.com', '2000-01-01');
INSERT INTO `users`(`id`, `name`, `password`, `email`, `birthday`)
VALUES(2, '李四', '123456', 'ls@qq.com', '2000-01-01');
INSERT INTO `users`(`id`, `name`, `password`, `email`, `birthday`)
VALUES(3, '王五', '123456', 'ww@qq.com', '2000-01-01');

SELECT * FROM `users`;

导入数据库依赖

<dependencies>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
</dependencies>

IDEA中连接
在这里插入图片描述
在这里插入图片描述
JDBC固定步骤:

  1. 加载驱动
  2. 连接数据库
  3. 向数据库发送SQL的对象Statement 做CRUD
  4. 编写SQL
  5. 执行(查询)SQL,(返回ResultSet结果集)
  6. 关闭连接,释放资源(必须做) 先开后关
public class TestJDBC {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 配置信息
        // useUnicode=true&characterEncoding=utf-8 解决中文乱码
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
        String username = "root";
        String password = "123456";

        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.连接数据库,代表数据库对象
        Connection connection = DriverManager.getConnection(url, username, password);

        // 3.向数据库发送SQL的对象Statement 做CRUD
        Statement statement = connection.createStatement();

        // 4.编写SQL
        String sql = "select * from users";

        // 5.执行(查询)SQL,(返回ResultSet结果集)
        ResultSet resultSet = statement.executeQuery(sql);

        while (resultSet.next()) {
            System.out.println("id="+ resultSet.getObject("id"));
            System.out.println("name="+ resultSet.getObject("name"));
            System.out.println("password="+ resultSet.getObject("password"));
            System.out.println("email="+ resultSet.getObject("email"));
            System.out.println("birthday="+ resultSet.getObject("birthday"));
        }

        // 6.关闭连接,释放资源(必须做) 先开后关
        resultSet.close();
        statement.close();
        connection.close();
    }
}

详细查看MySQL

事务
要么都成功,要么都失败!
ACID原则:保证数据安全

  • 开启事务
  • 事务提交 commit()
  • 事务回滚 rollback()
  • 关闭事务

Junit单元测试
依赖

<!--单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

@Test注解只有在方法上有效,只要加了这个注解的方法,就可以直接运行!

@Test
public void test01() {
    System.out.println("hello");
}

成功
在这里插入图片描述
失败

在这里插入图片描述

搭建一个环境

@Test
public void test03() throws Exception {
    // 配置信息
    // useUnicode=true&characterEncoding=utf-8 解决中文乱码
    String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
    String username = "root";
    String password = "123456";

    Connection connection = null;
    
    // 1.加载驱动
    try {
        Class.forName("com.mysql.jdbc.Driver");

        // 2.连接数据库,代表数据库对象
        connection = DriverManager.getConnection(url, username, password);

        // 3.通知数据库开启事务
        connection.setAutoCommit(false);  // 关闭自动提交,即开启事务

        String sql1 = "update account\n" +
                "set money = money - 100\n" +
                "where name = 'a';";
        connection.prepareStatement(sql1).executeUpdate();

        // 制造错误
        // int i = 1/0;

        String sql2 = "update account\n" +
                "set money = money + 100\n" +
                "where name = 'b';";
        connection.prepareStatement(sql2).executeUpdate();
        connection.commit();  // 以上两条SQL都执行成功了就提交事务
        System.out.println("提交成功");
    }catch (Exception e) {
        try {
            // 如果出现异常,就通知数据库回滚事务
            connection.rollback();
        }catch (SQLException e1) {
            e1.printStackTrace();
        }
        e.printStackTrace();
    }finally {
        connection.close();
    }

}

15 SMBMS超市订单管理系统

在这里插入图片描述
数据库:
在这里插入图片描述

项目如何搭建?
考虑是否使用Maven?依赖还是导jar包

15.1 项目搭建准备工作(八步)

  1. 搭建一个MavenWeb项目

  2. 配置Tomcat

  3. 测试项目是否能够运行

  4. 导入项目中会依赖的jar包   jsp servlet mysql驱动 jstl standard

  5. 创建项目包结构

    dao:data access object:数据访问对象,主要做的事情就是对数据库单表进行增删改操作,查的有可能是多表管理查询

    service: 对应界面上的操作,增删改查,至于这个业务涉及到几个dao,就调用几个dao

    servlet
        1、接受用户请求,进行处理(doget/dopost)调用service,得到数据
        2、做出响应(HTML):通过PrintWriter out = response.getWriter();通过out输出 HTML代码

    业务流程
        一个业务都是从后到前,第一步:设计数据库
        dao-service(调用多个dao)-servlet(接受用户请求,做出响应)--------HTML(JSP技术)
    在这里插入图片描述

  6. 编写实体类
    ORM映射:数据库中的表和pojo包中的实体类映射

  7. 编写基础公共类
     1. 数据库配置文件
    在这里插入图片描述

     2. 编写数据库的公共类

package pers.dongyang.dao;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

// 操作数据库的公共类
public class BaseDao {

    private static String driver;
    private static String url;
    private static String username;
    private static String password;

    // 静态代码块,在类加载的时候初始化
    static {
        Properties properties = new Properties();
        // 通过类加载器读取对应的资源
        InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.properties");

        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }

        driver = properties.getProperty("driver");
        url = properties.getProperty("url");
        username = properties.getProperty("username");
        password = properties.getProperty("password");

    }

    //获取数据库连接
    public static Connection getConnection() {
        Connection connection = null;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    // 编写查询公共类
    public static ResultSet execute(Connection connection, String sql, Object[] params, ResultSet resultSet, PreparedStatement preparedStatement) throws SQLException {
        // 获得连接,预编译sql
        preparedStatement = connection.prepareStatement(sql);

        // 传递参数
        for (int i = 0; i < params.length; i++) {
            // setObject占位符从1开始,但是我们的数组是从0开始
            preparedStatement.setObject(i + 1, params[i]);
        }

        resultSet = preparedStatement.executeQuery();
        return resultSet;
    }

    // 编写增删改公共方法
    public static int execute(Connection connection, String sql, Object[] params, PreparedStatement preparedStatement) throws SQLException {
        // 获得连接
        preparedStatement = connection.prepareStatement(sql);

        // 传递参数
        for (int i = 0; i < params.length; i++) {
            // setObject占位符从1开始,但是我们的数组是从0开始
            preparedStatement.setObject(i + 1, params[i]);
        }

        int updateRows = preparedStatement.executeUpdate();
        return updateRows;
    }

    // 释放资源
    public static boolean closeResource(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
        boolean flag = true;

        if (resultSet != null) {
            try {
                resultSet.close();

                // GC回收
                resultSet = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }

        if (connection != null) {
            try {
                connection.close();

                // GC回收
                connection = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }

        if (preparedStatement != null) {
            try {
                preparedStatement.close();

                // GC回收
                preparedStatement = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }

        return flag;
    }

}

 3.编写字符编码过滤器

package pers.dongyang.filter;

import javax.servlet.*;
import java.io.IOException;

public class CharacterEncodingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        chain.doFilter(request,response);
    }

    public void destroy() {

    }
}

注册

<!--注册字符编码过滤器-->
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>pers.dongyang.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 导入静态资源
    在这里插入图片描述

15.2 登录功能实现

在这里插入图片描述

  1. 编写前端页面
  2. 设置首页
<!--设置首页-->
<welcome-file-list>
    <welcome-file>login.jsp</welcome-file>
</welcome-file-list>
  1. 编写dao层得到用户登录的接口
package pers.dongyang.dao.user;

import pers.dongyang.pojo.User;

import java.sql.Connection;

public interface UserDao {

    // 得到要登录的用户
    public User getLoginUser(Connection connection, String userCode) throws Exception;
    
}
  1. 编写dao接口的实现类
public class UserDaoImpl implements UserDao {
    public User getLoginUser(Connection connection, String userCode) throws Exception {

        PreparedStatement pstm = null;
        ResultSet rs = null;
        User user = null;

        if (connection !=null) {
            String sql = "select * from smbms_user where userCode=?";
            Object[] params = {userCode};

            rs = BaseDao.execute(connection, pstm, rs, sql, params);
            if (rs.next()) {
                user = new User();
                user.setId(rs.getInt("id"));
                user.setUserCode(rs.getString("userCode"));
                user.setUserName(rs.getString("userName"));
                user.setUserPassword(rs.getString("userPassword"));
                user.setGender(rs.getInt("gender"));
                user.setBirthday(rs.getDate("birthday"));
                user.setPhone(rs.getString("phone"));
                user.setAddress(rs.getString("address"));
                user.setUserRole(rs.getInt("userRole"));
                user.setCreatedBy(rs.getInt("createdBy"));
                user.setCreationDate(rs.getTimestamp("creationDate"));
                user.setModifyBy(rs.getInt("modifyBy"));
                user.setModifyDate(rs.getTimestamp("modifyDate"));
            }
            BaseDao.closeResource(null, pstm, rs);
        }
        return user;

    }
}
  1. 业务层接口
public interface UserService {
    // 用户登录
    public User login(String userCode, String password);
}
  1. 业务层实现类
public class UserServiceImpl implements UserService {
    // 业务层都会调用dao层,所以我们要引入dao层
    private UserDao userDao;
    public UserServiceImpl() {
        userDao = new UserDaoImpl();
    }

    public User login(String userCode, String password) {
        Connection connection = null;
        User user = null;

        try {
            connection = BaseDao.getConnection();
            // 通过业务层调用对应的具体的数据库操作
            user = userDao.getLoginUser(connection, userCode);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            BaseDao.closeResource(connection, null, null);
        }

        return user;
    }

    /*
        @Test
        public void test01() {
            UserServiceImpl userService = new UserServiceImpl();
            User admin = userService.login("admin", "1234567");
            System.out.println(admin.getUserPassword());
        }
     */
}
  1. 编写servlet
public class LoginServlet extends HttpServlet {

    // Servlet:控制层,调用业务层代码
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("LoginServlet start...");

        // 获取用户名和密码
        String userCode = req.getParameter("userCode");
        String userPassword = req.getParameter("userPassword");

        // 和数据库中的密码进行对比,调用业务层
        UserServiceImpl userService = new UserServiceImpl();
        User user = userService.login(userCode, userPassword);  // 这里已经把登录的人查出来了

        if (user !=null) {  // 查有此人,可以登录
            // 将用户的信息放到Session中
            req.getSession().setAttribute(Constants.USER_SESSION, user);

            // 跳转到主页
            resp.sendRedirect("jsp/frame.jsp");
        } else {  // 查无此人,无法登录
            // 转发回登录页面,顺带提示它,用户名或密码错误
            req.setAttribute("error", "用户名或密码不正确");
            req.getRequestDispatcher("login.jsp").forward(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  1. 注册Servlet
<!--注册Servlet-->
<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>pers.dongyang.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login.do</url-pattern>
</servlet-mapping>
  1. Tomcat启动测试访问,确保以上功能成功

15.3 登录功能优化

注销功能:
思路:移除Session,返回登录页面

public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 移除用户的Session
        req.getSession().removeAttribute(Constants.USER_SESSION);

        // 回到登录页面
        resp.sendRedirect(req.getContextPath() + "/login.jsp");
    }

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

注册xml

<servlet>
    <servlet-name>LogoutServlet</servlet-name>
    <servlet-class>pers.dongyang.servlet.user.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LogoutServlet</servlet-name>
    <url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>

登录拦截优化(退出后,不输入用户名密码,直接输入首页网址,不允许进入)

编写一个过滤器

public class SysFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;  // 为了获取Session
        HttpServletResponse response = (HttpServletResponse) resp;  // 为了重定向

        // 过滤器,从Session获取用户
        User user = (User) request.getSession().getAttribute(Constants.USER_SESSION);

        if (user == null) {  // 说明该用户已被移除或注销或未登录
            response.sendRedirect("/smbms/error.jsp");
        }else {
            chain.doFilter(req, resp);
        }

    }

    public void destroy() {

    }
}

注册

<!--用户登录拦截过滤器-->
<filter>
    <filter-name>SysFilter</filter-name>
    <filter-class>pers.dongyang.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SysFilter</filter-name>
    <url-pattern>/jsp/*</url-pattern>
</filter-mapping>

15.4 密码修改

  1. 导入前端素材
<li><a href="${pageContext.request.contextPath }/jsp/pwdmodify.jsp">密码修改</a></li>
  1. 写项目,建议从底层向上写
    在这里插入图片描述
  2. UserDao接口
// 修改当前用户密码(增删改返回值都是int)
public int updatePwd(Connection connection, int id, int password) throws Exception;
  1. UserDao接口实现类
// 修改当前用户密码(增删改返回值都是int)
public int updatePwd(Connection connection, int id, int password) throws Exception {

    PreparedStatement pstm = null;  // 3.创建PreparedStatement

    // 8.考虑到返回值在if内定义无法return,所以将其定义提到if外
    int updateRows = 0;

    // 7.出于安全性考虑,进行判断
    if (connection!=null) {
        // 1.先写SQL语句
        String sql = "update smbms_user set userPassword = ? where id = ?";  // 2.要执行sql语句需要创建PreparedStatement进行预编译
        // 5.创建BaseDao参数params,跟问号按顺序对应
        Object[] params = {password, id};

        // 4.调用BaseDao去执行(参数没有的去创建,比如params)
        updateRows = BaseDao.execute(connection, pstm, sql, params);

        // 6.释放资源
        BaseDao.closeResource(connection, pstm, null);
    }

    // 9.返回被修改的行数
    return updateRows;
}
  1. UserService层
// 根据用户id修改密码(在数据库持久化操作中增删改返回值都是int,表示的是修改的数据的行数,在业务层这里使用boolean表示是否修改成功)
public boolean updatePwd(int id, int password);
  1. UserService实现类
public boolean updatePwd(int id, int password) {
    // 1.业务层可能存在事务,失败要回滚,把Connection提前
    Connection connection = null;
    // 4.设置flag判断是否修改成功,默认失败
    boolean flag = false;

    // 2.获取数据库连接
    connection = BaseDao.getConnection();

    // 3.修改密码
    // 如果被修改的行数大于0,说明SQL语句执行成功, 将异常try catch
    try {
        if (userDao.updatePwd(connection, id, password) > 0) {
            // 5.说明修改成功
            flag = true;
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 6.关闭资源
        BaseDao.closeResource(connection, null, null);
    }
    return flag;
}
  1. Servlet记得实现复用需要提取出方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getParameter("method");
    if (method.equals("savepwd") && method != null) {
        this.updatePwd(req, resp);
    }
}
public void updatePwd(HttpServletRequest req, HttpServletResponse resp) {
    // 1.从Session中获取用户id
    Object o = req.getSession().getAttribute(Constants.USER_SESSION);

    // 2.获取前端参数  取newpassword更新替换原来密码
    String newpassword = req.getParameter("newpassword");

    // 6.设置flag判断是否修改成功,默认失败
    boolean flag = false;

    // 3.判断
    if (o != null && !StringUtils.isNullOrEmpty(newpassword)) { // 如果对象不为空,并且密码不存在或密码不为空
        // if (o != null && newpassword != null && newpassword.length() != 0) 这个if判断等效于上一句,但是上一句逼格高
        // 4.调用Service层
        UserService userService = new UserServiceImpl();
        // 5.修改密码,返回修改成功与否的boolean值
        flag = userService.updatePwd(((User) o).getId(), newpassword);

        if (flag) {
            // 6.如果修改成功,返回一句话
            req.setAttribute("message", "修改密码成功,请使用新密码重新登录");
            // 7.密码修改成功,移除当前Session
            req.getSession().removeAttribute(Constants.USER_SESSION);

        } else {
            // 8.如果修改成功,返回一句话
            req.setAttribute("message", "密码修改失败");
        }
    } else {
        // 9.如果新密码有问题,返回一句话
        req.setAttribute("message", "新密码格式不正确");
    }

    // 10.修改完转发
    try {
        req.getRequestDispatcher("pwdmodify.jsp").forward(req, resp);
    } catch (ServletException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
<!--UserServlet修改密码-->
<servlet>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>pers.dongyang.servlet.user.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>UserServlet</servlet-name>
    <url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping>
  1. 测试

Ajax优化密码修改

  1. 阿里的fastjson
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.68</version>
</dependency>
  1. 后台代码修改
// 修改密码
public void updatePwd(HttpServletRequest req, HttpServletResponse resp) {
    // 1.从Session中获取用户id
    Object o = req.getSession().getAttribute(Constants.USER_SESSION);

    // 2.获取前端参数  取newpassword更新替换原来密码
    String newpassword = req.getParameter("newpassword");

    // 6.设置flag判断是否修改成功,默认失败
    boolean flag = false;

    // 3.判断
    if (o != null && !StringUtils.isNullOrEmpty(newpassword)) { // 如果对象不为空,并且密码不存在或密码不为空
        // if (o != null && newpassword != null && newpassword.length() != 0) 这个if判断等效于上一句,但是上一句逼格高
        // 4.调用Service层
        UserService userService = new UserServiceImpl();
        // 5.修改密码,返回修改成功与否的boolean值
        flag = userService.updatePwd(((User) o).getId(), newpassword);

        if (flag) {
            // 6.如果修改成功,返回一句话
            req.setAttribute("message", "修改密码成功,请使用新密码重新登录");
            // 7.密码修改成功,移除当前Session
            req.getSession().removeAttribute(Constants.USER_SESSION);

        } else {
            // 8.如果修改成功,返回一句话
            req.setAttribute("message", "密码修改失败");
        }
    } else {
        // 9.如果新密码有问题,返回一句话
        req.setAttribute("message", "新密码格式不正确");
    }

    // 10.修改完转发
    try {
        req.getRequestDispatcher("pwdmodify.jsp").forward(req, resp);
    } catch (ServletException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// 验证旧密码,Session中有用户的密码
public void pwdmodify(HttpServletRequest req, HttpServletResponse resp) {
    // 1.从Session中获取用户id
    Object o = req.getSession().getAttribute(Constants.USER_SESSION);

    // 2.获取前端参数  取oldpassword更新替换原来密码
    String oldpassword = req.getParameter("oldpassword");

    // 5.使用万能的Map封装 true false sessionerror error结果集
    HashMap<String, String> resultMap = new HashMap<String, String>();

    if (o==null) {  // 3.如果Session失效或过期
        // 4.告诉前端出现问题
        resultMap.put("result", "sessionerror");
    }else if (StringUtils.isNullOrEmpty(oldpassword)) {  // 6.输入的密码为空
        resultMap.put("result", "error");
    }else {
        String userPassword = ((User) o).getUserPassword();  // Session中用户的密码
        if (oldpassword.equals(userPassword)) { // 如果oldpassword和Session中的密码一样
            resultMap.put("result", "true");
        }else {  // 密码错误
            resultMap.put("result", "false");
        }
    }

    try {
        // 使用json格式接收
        resp.setContentType("application/json");
        // 创建流
        PrintWriter writer = resp.getWriter();
        // JSONArray 阿里的JSON工具类,转换格式时使用
        /**
         * resultMap = ["result", "true", "result", "false", "result", "sessionerror", "result", "error"]
         * JSON格式:{key:value}
         */
        writer.write(JSONArray.toJSONString(resultMap));  // 把结果集变成JSON字符串格式
        writer.flush();
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

15.5 用户管理实现

思路:一个Servlet查询三个东西,用户列表,角色列表和分页
在这里插入图片描述

  1. 导入分页的工具类
    在这里插入图片描述

  2. 用户列表页面导入

实现步骤

1. 获取用户数量

(1) UserDao

// 查询用户总数
public int getUserCount(Connection connection, String userName, int userRole) throws Exception;

(2) UserDaoImpl

// 根据用户名或用户角色查询用户总数[最难理解的SQL语句]
public int getUserCount(Connection connection, String userName, int userRole) throws Exception {
    PreparedStatement pstm = null;
    ResultSet rs = null;
    int count = 0;

    if (connection != null) {
        StringBuffer sql = new StringBuffer();  // 考虑到这里的查询比较复杂需要使用sql拼接,所以使用StringBuffer存储sql
        sql.append("select count(1) as count from smbms_user u,smbms_role r where u.userRole = r.id");
        ArrayList<Object> list = new ArrayList<Object>();// 存放我们的参数(即?)

        if (!StringUtils.isNullOrEmpty(userName)) {  // 如果用户名不为空,继续append
            sql.append(" and u.userName like ?");  // ? 表示模糊查询字符串,格式 %....
            list.add("%" + userName + "%");  // list.index=0
        }

        if (userRole > 0) {  // 判断角色序号是否大于0(角色序号为1 2 3,大于0表示选定了角色)
            sql.append(" and u.userRole = ?");
            list.add(userRole);  // list.index=1
        }

        // 把list转换成数组
        Object[] params = list.toArray();

        // 输出完整的SQL语句
        System.out.println("UserDaoImpl-> getUserCount:" + sql.toString());

        // 执行sql语句
        rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params);

        // 从结果集中获取最终的数量
        if (rs.next()) {
            count = rs.getInt("count");
        }

        // 释放资源
        BaseDao.closeResource(null, pstm, rs);
    }

    return count;
}

(3) UserService

// 查询记录数
public int getUserCount(String userName, int UserRole);

(4) UserServiceImpl

// 查询记录数
public int getUserCount(String userName, int UserRole) {
    Connection connection = null;
    int count = 0;
    
    try {
        connection = BaseDao.getConnection();
        count = userDao.getUserCount(connection, userName, UserRole);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        BaseDao.closeResource(connection, null, null);
    }

    return count;
}

2. 获取用户列表
(1) userDao

// 获取用户列表,通过条件查询-userList(实现分页)
public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize) throws Exception;

(2) userDaoImpl

public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize) throws Exception {
    PreparedStatement pstm = null;
    ResultSet rs = null;
    List<User> userList = new ArrayList<User>();

    if (connection != null) {
        StringBuffer sql = new StringBuffer();
        sql.append("select u.*, r.roleName as userRoleName \n" +
                "from smbms_user u, smbms_role r \n" +
                "where u.userRole = r.id");
        List<Object> list = new ArrayList<Object>();
        if (!StringUtils.isNullOrEmpty(userName)) {
            sql.append(" and u.userName like ?");
            list.add("%" + userName + "%");
        }
        if (userRole>0) {
            sql.append(" and u.userRole = ?");
            list.add(userRole);
        }

        /*
        * 在数据库中,分页使用limit
        * 参数:startIndex, pageSize
        * startIndex当前页起始元素索引 = (当前页码 - 1) * 页面大小
        * 假设每页5个元素
        * 参数:0, 5  页码:1  数据库起始元素索引:0   总元素:0  1  2  3  4
        * 参数:5, 5  页码:2  数据库起始元素索引:5   总元素:5  6  7  8  9
        * 参数:10, 5 页码:3  数据库起始元素索引:10  总元素:10 11 12 13 14
        * ...
        * */
        sql.append(" order by creationDate DESC limit ?,?");
        currentPageNo = (currentPageNo - 1) * pageSize;
        list.add(currentPageNo);
        list.add(pageSize);

        Object[] params = list.toArray();
        System.out.println("sql ----> " + sql.toString());
        rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params);

        while (rs.next()) {
            User _user = new User();
            _user.setId(rs.getInt("id"));
            _user.setUserCode(rs.getString("userCode"));
            _user.setUserName(rs.getString("userName"));
            _user.setGender(rs.getInt("gender"));
            _user.setBirthday(rs.getDate("birthday"));
            _user.setPhone(rs.getString("phone"));
            _user.setUserRole(rs.getInt("userRole"));
            _user.setUserRoleName(rs.getString("userRoleName"));
            userList.add(_user);
        }

        BaseDao.closeResource(null, pstm, rs);
    }

    return userList;
}

(3) userService

// 根据条件查询用户列表
public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize);

(4) userServiceImpl

public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize) {
    Connection connection = null;
    List<User> userList = null;

    System.out.println("queryUserName ----> " + queryUserName);
    System.out.println("queryUserRole ----> " + queryUserRole);
    System.out.println("currentPageNo ----> " + currentPageNo);
    System.out.println("pageSize ----> " + pageSize);

    try {
        connection = BaseDao.getConnection();
        userList = userDao.getUserList(connection, queryUserName, queryUserRole, currentPageNo, pageSize);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        BaseDao.closeResource(connection, null, null);
    }

    return userList;
}

3. 获取角色操作

为了统一,可以把角色的操作单独建包,和pojo包中实体类对应

(1) RoleDao

package pers.dongyang.dao.role;

import pers.dongyang.pojo.Role;

import java.sql.Connection;
import java.util.List;

public interface RoleDao {
    // 获取角色列表
    public List<Role> getRoleList(Connection connection) throws Exception;
}

(2) RoleDaoImpl

package pers.dongyang.dao.role;

import pers.dongyang.dao.BaseDao;
import pers.dongyang.pojo.Role;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class RoleDaoImpl implements RoleDao{
    // 获取角色列表
    public List<Role> getRoleList(Connection connection) throws Exception {
        PreparedStatement pstm = null;
        ResultSet rs = null;
        ArrayList<Role> roleList = new ArrayList<Role>();

        if (connection!=null) {
            String sql = "select * from smbms_role";
            Object[] params = {};

            rs = BaseDao.execute(connection, pstm, rs, sql, params);

            while (rs.next()) {
                Role _role = new Role();
                _role.setId(rs.getInt("id"));
                _role.setRoleCode(rs.getString("roleCode"));
                _role.setRoleName(rs.getString("roleName"));
                roleList.add(_role);
            }

            BaseDao.closeResource(null, pstm, rs);
        }
        return roleList;
    }
}

(3) RoleService

package pers.dongyang.service.role;

import pers.dongyang.pojo.Role;


import java.util.List;

public interface RoleService {
    // 获取角色列表
    public List<Role> getRoleList();
}

(4) RoleServiceImpl

package pers.dongyang.service.role;

import pers.dongyang.dao.BaseDao;
import pers.dongyang.dao.role.RoleDao;
import pers.dongyang.dao.role.RoleDaoImpl;
import pers.dongyang.pojo.Role;

import java.sql.Connection;
import java.util.List;

public class RoleServiceImpl implements RoleService{
    // 引入Dao
    private RoleDao roleDao;

    // 实例化
    public RoleServiceImpl() {
        roleDao = new RoleDaoImpl();
    }

    public List<Role> getRoleList(){
        Connection connection = null;
        List<Role> roleList = null;

        try {
            connection = BaseDao.getConnection();
            roleList = roleDao.getRoleList(connection);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            BaseDao.closeResource(connection, null, null);
        }
        return roleList;
    }
}

4. 用户显示的Servlet
(1) 获取用户前端的数据(查询)
(2) 判断请求是否需要执行,看参数的值判断
(3) 为了实现分页,需要计算出当前页面和总页面,页面大小
(4) 用户列表展示
(5) 返回前端

// 重点难点
public void query(HttpServletRequest req, HttpServletResponse resp) {
    // 查询用户列表
    // 从前端获取的临时变量
    String queryUserName = req.getParameter("queryUserName");
    String queryUserRoleTemp = req.getParameter("queryUserRole");
    String pageIndex = req.getParameter("pageIndex");

    int queryUserRole = 0;  // 存放前端字符串queryUserRoleTemp解析出来的数字123对应不同的role

    // 获取用户列表
    UserServiceImpl userService = new UserServiceImpl();
    List<User> userList = null;

    // 第一次走这个请求一定是首页,并且页面大小固定
    int pageSize = 5;  // 建议把pageSize写到配置文件中方便后期修改
    int currentPageNo = 1;  // 当前页码默认是第一页

    if (queryUserName == null) {  // 如果名字为空,则手动赋值为""
        queryUserName = "";
    }
    if (queryUserRoleTemp != null && !queryUserRoleTemp.equals("")) {  // 如果下拉框角色名不为空,则进行字符串解析
        queryUserRole = Integer.parseInt(queryUserRoleTemp);
    }
    if (pageIndex != null) {
        currentPageNo = Integer.parseInt(pageIndex);  // 解析当前页面页码
    }

    // 获取用户总数 (分页:存在上一页,下一页的情况,使用pageSupport工具类)
    int totalUserCount = userService.getUserCount(queryUserName, queryUserRole);

    // 使用pageSupport工具类设置值
    PageSupport pageSupport = new PageSupport();
    pageSupport.setCurrentPageNo(currentPageNo); // 设置当前页码,值来自前端字符串解析
    pageSupport.setPageSize(pageSize);  // 设置页面大小,值来自自定义,后续建议从配置文件读入
    pageSupport.setTotalUserCount(totalUserCount);  // 设置用户总数,值来自调用业务层方法返回

    int totalPageCount = pageSupport.getTotalPageCount();  // 通过工具类获取页面总数

    // 控制首页和尾页
    if (currentPageNo<1) { // 控制页码小于1时停留在第一页
        currentPageNo = 1;
    } else if (currentPageNo>totalPageCount) {  // 控制页码大于最后一页时停留在最后一页
        currentPageNo = totalPageCount;
    }

    // 获取用户列表展示
    userList = userService.getUserList(queryUserName, queryUserRole, currentPageNo, pageSize);  // 上面的操作都是为了获取这个方法的参数
    req.setAttribute("userList", userList);  // 将用户列表信息传给前端,匹配前端item后面的变量名

    // 获取角色列表展示
    RoleServiceImpl roleService = new RoleServiceImpl();
    List<Role> roleList = roleService.getRoleList();
    req.setAttribute("roleList", roleList);  // 将用户列表信息传给前端,匹配前端item后面的变量名

    // 传递其他信息给前端
    req.setAttribute("totalUserCount", totalUserCount);
    req.setAttribute("totalPageCount", totalPageCount);
    req.setAttribute("currentPageNo", currentPageNo);
    req.setAttribute("queryUserName", queryUserName);
    req.setAttribute("queryUserRole", queryUserRole);

    // 返回前端
    try {
        req.getRequestDispatcher("userlist.jsp").forward(req, resp);
    } catch (ServletException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

自言自语小黄鸭调试法!
可以使用IDEA对.class文件反编译直接打开,操作步骤:直接将target目录中生成的class文件放到IDEA中打开即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值