JavaWeb开发

JavaWeb

1. 基本概念

web开发:

  • 静态web:html,css

  • 动态web:技术栈(Servlet/ISP,ASP,PHP)

1.2 web应用程序

可以提供浏览器访问的程序。

web应用组成:

  • html,css,js

  • jsp,servlet

  • Java程序

  • jar包

  • 配置文件(properties)

web应用程序编写完毕后,若想提供给外界访问,需要一个服务器来统一管理。

1.3 静态web

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

  

  • 缺点:

    • web页面无法动态更新,所有用户看到的都是同一个页面;

      • 轮播图,点击特效(伪动态)

      • JavaScript(最常用)

      • VBScript

    • 无法与数据库交互(数据无法持久化,用户无法交互)

1.4 动态web

  • 页面会动态展示:页面展示效果因人而异

  

  • 缺点

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

      • 停机维护

  • 优点:

    • Web页面可以动态更新,所有用户看到都不是同一个页面

    • 它可以与数据库交互(数据持久化:注册,商品信息,用户信息………)

2. web服务器

2.1 技术讲解

ASP:

  • 微软:国内最早流行的就是ASP;

  • 在HTML中嵌入了VB的脚本,ASP+COM;

  • 在ASP开发中,基本一个页面都有几干行的业务代码,页面极其换乱;

  • 维护成本高!

  • C#

  • IIS

php:

  • PHP开发速度很快,功能很强大,跨平台,代码很简单(70%,WP)

  • 无法承载大访问量的情况(局限性)

jSP/Servlet: B/S:浏览和服务器

C/S:客户端和服务器

  • sun公司主推的B/S架构

  • 基于Java语言的(所有的大公司,或者一些开源的组件,都是用Java写的)

  • 可以承载三高问题(高并发,高可用,高性能)带来的影响;

  • 语法像ASP,ASP->JSP,加强市场强度

2.2 web服务器

服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息

IIS,Tomcat

3. Tomcat

3.1 安装Tomcat

官网:http://tomcat.apache.org/

下载完成后解压即可。

启动/关闭Tomcat:双击apache-tomcat-9.0.41in目录下的startup.bat/shutdown.bat。

访问测试:浏览器输入http://127.0.0.1:8080/访问。

可能遇到的问题:

  1. Java环境变量没有配置

  2. 闪退问题:需要配置兼容性

  3. 乱码问题:配置文件中设置

可以修改 conf/logging.properties 中的 java.util.logging.ConsoleHandler.encoding = GBK 解决乱码问题

3.2 Tomcat配置

服务器核心配置文件:apache-tomcat-9.0.41confserver.xml

可以配置启动的端口号

  • tomcat的默认端口号为:8080

  • mysql:3306

  • http:80

  • https:443

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

可以配置主机的名称

  • 默认的主机名为:localhost(127.0.0.1)

  • 默认网站应用存放的位置为:webapps

<Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

3.3 发布网站

将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了

网站结构:

  • webapps :Tomcat服务器的web目录

    • ROOT

    • webname :网站的目录名

      • WEB-INF

        • classes : java程序

        • lib:web应用所依赖的jar包

        • web.xml :网站配置文件

      • index.html 默认的首页

      • static

        • css

          • style.css

        • js

        • img

    • .....

4. HTTP

4.1 什么是HTTP

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

  • 文本:html,字符串,…

  • 超文本:图片,音乐,视频,定位,地图.……

  • HTTP默认端口:80

  • HTTPs默认端口:443

4.2 两个时代

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

  • HTTP/1.1:客户端可以与web服务器连接后,可以获得多个web资源。

4.3 HTTP请求

  • 客户端 – 发请求(Request)– 服务器

例:请求百度

General:

Request URL: https://www.baidu.com/     // 请求地址
Request Method: GET                     // get方法/post方法
Status Code: 200 OK                     // 状态码
Remote Address: 110.242.68.3:443

Request Headers:

Accept: text/html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: keep-alive

请求行

  • 请求行中的请求方式:GET

  • 请求方式:GetPost,HEAD,DELETE,PUT,TRACT.…

    • get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效;

    • post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。

消息头

Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持哪种编码格式  GBK   UTF-8   GB2312  ISO8859-1
Accept-Language:告诉浏览器,它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机

4.4 HTTP响应

  • 服务器 – 响应(Response) - 客户端

例:百度响应

Response Headers:

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

响应体

Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持哪种编码格式  GBK   UTF-8   GB2312  ISO8859-1
Accept-Language:告诉浏览器,它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机
Refresh:告诉客户端,多久刷新一次
Location:让网页重新定位

响应状态码

200:请求响应成功 3xx:请求重定向 4xx:找不到资源(404:资源不存在) 5xx:服务器代码错误(500:在服务器端的源代码出现错误时出现;502:网关错误)

常见面试题: 当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?

5. Maven

一个项目架构管理工具,方便jar包导入

Maven的核心思想:约定大于配置,有约束,不要去违反。Maven会规定好你该如何去编写我们Java代码,必须要按照这个规范来。

5.1 Maven安装

官网:Maven – Welcome to Apache Maven

下载完成后,解压即可。

配置环境变量

在我们的系统环境变量中配置如下配置:

  • M2_HOME:maven目录下的bin目录

  • MAVEN_HOME:maven的目录

  • 在系统的path中配置:%MAVEN_HOME%in

配置阿里云镜像

在D:Environmentapache-maven-3.6.3confsettings.xml中,添加镜像。

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

本地仓库

建立本地仓库:

<localRepository>D:Environmentapache-maven-3.6.3maven-repo</localRepository>

5.2 在IDEA中使用Maven

  1. 启动IDEA,创建一个MavenWeb项目;

    

     

     

  1. 等待项目初始化完毕,直到BUILD SUCCESS,说明项目搭建成功;

  2. 观察maven-repo,发现多了很多包;

  3. IDEA中的Maven配置。

    注意:在IDEA中经常会出现项目自动创建完成后,使用IDEA默认的Maven home的问题,需要手动修改为本地Maven版本。

    至此,Maven在IDEA中的配置和使用已经完成。

5.3 在IDEA中配置Tomcat

6. Servlet

6.1 简介

Servlet就是sun公司开发动态web的一门技术;Sun在这些API中提供一个接口——Servlet,如果你想开发一个Servlet程序,只需要完成两个步骤:

  • 编写一个类,实现Serlet接口

  • 把开发好java类部署到web服务器中。

实现了Servlet接口的Java程序叫Servlet。

6.2 HelloServlet

Serlvet接口Sun公司有两个默认的实现类:HttpServlet,GenericServled。

  1. 构建一个普通的Maven项目,等理面的sc目录,以后我们的学习就在这个项目里面建立Moudel;这个空的工程就题Maven主工程;

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

    父项目中:

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

    子项目中:

        <parent>
            <artifactId>javaweb-02-servlet</artifactId>
            <groupId>com.wang</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>

    父项目中的java子项目可以直接使用。

  3. Maven环境优化

    • 修改web.xml为最新的

    • 将maven的结构搭建完整

  4. 编写一个Servlet程序

    • 编写一个普通类HelloServlet

    • 直接继承HttpServlet以实现Servlet接口

    package com.wang.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.PrintWriter;
    
    public class HelloServlet extends HttpServlet {
        // 由于get或者post只是请求实现的不同的方式,可以相互调用,业务逻辑都一样
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter writer = resp.getWriter();      // 响应流
            writer.print("Hello, Servlet!");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
  5. 编写Servlet的映射(为什么需要映射:我们写的是JAVA程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要再web服务中注册我们写的Servlet,还需给他一个浏览器能够访问的路径);

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        
    <!--    注册Servlet-->
        <servlet>
            <servlet-name>hello</servlet-name>
            <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
        </servlet>
    <!--    Servlet的请求路径-->
        <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
        
    </web-app>
  6. 配置Tomcat 注意:配置项目发布的路径就可以了

  7. 启动测试

6.3 servlet原理

6.4 mapping

  1. 一个Servlet可以指定一个映射路径;

      <!--    注册Servlet-->
      <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
      </servlet>
      <!--    Servlet的请求路径-->
      <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
      </servlet-mapping>
  2. 一个servlet可以指定多个映射路径;

    <!--    注册Servlet-->
    <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--    Servlet的请求路径-->
    <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/hello1</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>/hello</url-pattern>
    </servlet-mapping>
  3. 一个servlet可以指定通用映射路径;

    <!--    注册Servlet-->
    <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--    Servlet的请求路径-->
    <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
  4. 默认请求路径;

    <!--    注册Servlet-->
    <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--    Servlet的请求路径-->
    <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/*</url-pattern>
    </servlet-mapping>
  5. 指定一些后缀或者前缀等等;

    注:*前不能加项目映射的路径。

    <!--    注册Servlet-->
    <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--    Servlet的请求路径-->
    <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>*.hihihi</url-pattern>
    </servlet-mapping>
  6. 优先级问题:指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求。

    <!--    注册Servlet-->
    <servlet>
      <servlet-name>hello</servlet-name>
      <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--    Servlet的请求路径-->
    <servlet-mapping>
      <servlet-name>hello</servlet-name>
      <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
    <servlet>
      <servlet-name>error</servlet-name>
      <servlet-class>com.wang.servlet.ErrorServlet</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>error</servlet-name>
      <url-pattern>/*</url-pattern>
    </servlet-mapping>
    package com.wang.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.PrintWriter;
    
    public class ErrorServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/html");
            resp.setCharacterEncoding("utf-8");
    
            PrintWriter writer = resp.getWriter();
            writer.println("<h1>404</h1>");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doGet(req, resp);
        }
    }

6.5 ServletContext对象

注:创建新的子项目时,发现webapp文件夹没有小蓝点,配置tomcat时,发现并没有该项目相关的artifacts,观察Maven窗口,发现该子项目呈现灰色,可能是该项目忽略了此子模块,可以Setting->Maven->Ignored Files,查看此子模块是否被勾选。

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

6.5.1 应用(4项)

  • 共享数据

写入数据:

package com.wang.servlet;

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

// 用于写入ServletContext对象(全局使用)
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 context = this.getServletContext();

        String username = "甜酒果";
        context.setAttribute("username", username);
    }
}

读取数据:

package com.wang.servlet;

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

// 用于读取ServletContext对象中的内容
public class GetHelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String username = (String) context.getAttribute("username");

        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().println("name: " + username);
    }

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

注册Servlet:

<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>getHello</servlet-name>
    <servlet-class>com.wang.servlet.GetHelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getHello</servlet-name>
    <url-pattern>/getHello</url-pattern>
</servlet-mapping>

测试结果:

 

  • 获取初始化参数

配置一些web应用初始化参数:

<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>

获取参数:

package com.wang.servlet;

import javax.servlet.ServletContext;
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 GetInitParam extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String url = context.getInitParameter("url");
        resp.getWriter().print(url);
    }

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

注册Servlet:

<servlet>
    <servlet-name>getInitParam</servlet-name>
    <servlet-class>com.wang.servlet.GetInitParam</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getInitParam</servlet-name>
    <url-pattern>/getInitParam</url-pattern>
</servlet-mapping>

测试结果:

  • 请求转发

请求转发:

package com.wang.servlet;

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

public class RequestForward extends HelloServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        System.out.println("进入了RequestForward");
        //RequestDispatcher requestDispatcher = context.getRequestDispatcher("/getInitParam"); //转发的请求路径
        //requestDispatcher.forward(req,resp); //调用forward实现请求转发;
        context.getRequestDispatcher("/getInitParam").forward(req,resp);
    }

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

}

注册Servlet:

<servlet>
    <servlet-name>requestForward</servlet-name>
    <servlet-class>com.wang.servlet.RequestForward</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>requestForward</servlet-name>
    <url-pattern>/requestForward</url-pattern>
</servlet-mapping>

测试结果:

  • 读取资源文件

创建资源文件:

username=root
password=123456

读取资源文件:

package com.wang.servlet;

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

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

        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");

        Properties prop = new Properties();
        prop.load(is);
        String user = prop.getProperty("username");
        String pwd = prop.getProperty("password");

        resp.getWriter().print(user+":"+pwd);

    }

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

注册Servlet:

<servlet>
    <servlet-name>getResources</servlet-name>
    <servlet-class>com.wang.servlet.GetResources</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getResources</servlet-name>
    <url-pattern>/getResources</url-pattern>
</servlet-mapping>

测试结果:

6.6 HttpServletResponse

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

  • 如果要获取客户端请求过来的参数:找HttpServletRequest

  • 如果要给客户端响应一些信息:找HttpServletResponse

6.6.1 简单分类

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

    ServletOutputStream getOutputStream() throws IOException;
    PrintWriter getWriter() throws IOException;
  • 负责向浏览器发送响应头的方法:

    void setCharacterEncoding(String var1);
    void setContentLength(int var1);
    void setContentLengthLong(long var1);
    void setContentType(String var1);
    
    void setDateHeader(String var1, long var2);
    void addDateHeader(String var1, long var2);
    void setHeader(String var1, String var2);
    void addHeader(String var1, String var2);
    void setIntHeader(String var1, int var2);
    void addIntHeader(String var1, int var2);
  • 响应的状态码:

    int SC_CONTINUE = 100;
    int SC_SWITCHING_PROTOCOLS = 101;
    int SC_OK = 200;
    int SC_CREATED = 201;
    int SC_ACCEPTED = 202;
    int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    int SC_NO_CONTENT = 204;
    int SC_RESET_CONTENT = 205;
    int SC_PARTIAL_CONTENT = 206;
    int SC_MULTIPLE_CHOICES = 300;
    int SC_MOVED_PERMANENTLY = 301;
    int SC_MOVED_TEMPORARILY = 302;
    int SC_FOUND = 302;
    int SC_SEE_OTHER = 303;
    int SC_NOT_MODIFIED = 304;
    int SC_USE_PROXY = 305;
    int SC_TEMPORARY_REDIRECT = 307;
    int SC_BAD_REQUEST = 400;
    int SC_UNAUTHORIZED = 401;
    int SC_PAYMENT_REQUIRED = 402;
    int SC_FORBIDDEN = 403;
    int SC_NOT_FOUND = 404;
    int SC_METHOD_NOT_ALLOWED = 405;
    int SC_NOT_ACCEPTABLE = 406;
    int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
    int SC_REQUEST_TIMEOUT = 408;
    int SC_CONFLICT = 409;
    int SC_GONE = 410;
    int SC_LENGTH_REQUIRED = 411;
    int SC_PRECONDITION_FAILED = 412;
    int SC_REQUEST_ENTITY_TOO_LARGE = 413;
    int SC_REQUEST_URI_TOO_LONG = 414;
    int SC_UNSUPPORTED_MEDIA_TYPE = 415;
    int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    int SC_EXPECTATION_FAILED = 417;
    int SC_INTERNAL_SERVER_ERROR = 500;
    int SC_NOT_IMPLEMENTED = 501;
    int SC_BAD_GATEWAY = 502;
    int SC_SERVICE_UNAVAILABLE = 503;
    int SC_GATEWAY_TIMEOUT = 504;
    int SC_HTTP_VERSION_NOT_SUPPORTED = 505;

6.6.2 应用(4项)

  • 向浏览器输出消息

  • 下载文件

    1. 要获取下载文件的路径

    2. 要下载的文件名

    3. 设置让浏览器能够支持下载我们需要的东西

    4. 获取下载文件的输入流

    5. 创建缓冲区

    6. 获取OutputStream对象

    7. 将FileOutputStream流写入到bufer缓冲区

    8. 使用OutputStream将缓冲区中的数据输出到客户端!

代码示例(不要忘记注册Servlet,后面的Servlet注册过程省略):

package com.wang.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.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

public class FileDownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 要获取下载文件的路径
        String path = "E:\Java\Code\JavaWeb\javaweb-02-servlet\response\target\classes\jk.jpg";
        // 要下载的文件名
        String fileName = path.substring(path.lastIndexOf("\") + 1);
        // 设置让浏览器能够支持下载(Content-Disposition)我们需要的东西;中文文件名URLEncoder.encode编码,否则有可能乱码
        resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
        // 获取下载文件的输入流
        FileInputStream inputStream = new FileInputStream(path);
        // 创建缓冲区
        int len = 0;
        byte[] buffer = new byte[1024];
        // 获取OutputStream对象
        ServletOutputStream outputStream = resp.getOutputStream();
        // 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
        while ((len = inputStream.read(buffer)) > 0){
            outputStream.write(buffer, 0, len);
        }
        outputStream.close();
        inputStream.close();
    }

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

测试结果:

  • 验证码功能

package com.wang.servlet;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

public class ImageServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //如何让浏览器3秒自动刷新一次;
        resp.setHeader("refresh","3");

        //在内存中创建一个图片
        BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
        //得到图片
        Graphics2D g = (Graphics2D) image.getGraphics(); ////设置图片的背景颜色
        g.setColor(Color.white);
        g.fillRect(0,0,80,20);
        //给图片写数据
        g.setColor(Color.BLUE);
        g.setFont(new Font(null,Font.BOLD,20));
        g.drawString(makeNum(),0,20);

        //告诉浏览器,这个请求用图片的方式打开
        resp.setContentType("image/jpeg");
        //网站存在缓存,不让浏览器缓存
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");

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

    //生成随机数
    private String makeNum(){
        Random random = new Random();
        String num = random.nextInt(9999999) + "";
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 7-num.length() ; i++) {
            sb.append("0");
        }
        num = sb.toString() + num;
        return num;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • 实现重定向(最重要)

package com.wang.servlet;

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

// 输入网址http://localhost:8080/resp/red,重定向到http://localhost:8080/resp/img
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /* 分开书写
        resp.setHeader("Location", "/resp/img");
        resp.setStatus(HttpServletResponse.SC_FOUND);
         */
        resp.sendRedirect("/resp/img");
    }

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

面试题:重定向和请求转发的区别?

  • 相同点:

    页面都会实现跳转;

  • 不同点:

    • 请求转发时地址栏的URL不会改变(307);

    • 重定向时地址栏的URL会改变(302)。

案例:实现登陆时重定向。

案例实现

  1. 导入依赖的jar包;

    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
        </dependency>
    </dependencies>
  2. 编写index.jsp,即登录页面;

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <body>
    <h2>Hello World!</h2>
    
    <%--${pageContext.request.contextPath}代表当前项目--%>
    <form action="${pageContext.request.contextPath}/req-red" method="get">
        用户名:<input type="text" name="username"> <br>
        密  码:<input type="password" name="password"> <br>
        <input type="submit">
    </form>
    
    </body>
    </html>
  3. 编写重定向后的登录成功界面success.jsp;

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>succes</title>
    </head>
    <body>
        <h2>Login Successfully!</h2>
    </body>
    </html>
  4. 编写RequestRedirectSevlet.java;

    package com.wang.servlet;
    
    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 RequestRedirectServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String username = req.getParameter("username");
            String password = req.getParameter("password");
    
            System.out.println(username + ": " + password);
    
            resp.sendRedirect("/resp/success.jsp");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
  5. Servlet注册。

    <servlet>
        <servlet-name>requestRedirect</servlet-name>
        <servlet-class>com.wang.servlet.RequestRedirectServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>requestRedirect</servlet-name>
        <url-pattern>/req-red</url-pattern>
    </servlet-mapping>

案例测试

填写表单:

点击提交,实现重定向:

后台获取请求参数:

6.7 HttpServletRequest

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

6.7.1 应用

获取参数,请求转发

实现和测试过程与6.6.2中重定向类似。

package com.wang.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.util.Arrays;

public class RequestForwardServlet 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[] hobbies = req.getParameterValues("hobbies");

        System.out.println(username + ": " + password);
        System.out.println(Arrays.toString(hobbies));

        // 请求转发,注意路径和重定向有所区别
        req.getRequestDispatcher("/success.jsp").forward(req, resp);
    }

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

7. Cookie、Session

7.1 技术介绍

会话:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这整个过程可以称之为会话。

有状态会话:一个同学来过教室,下次再来教室,我们知道这个同学曾经来过,称之为有状态会话。

一个网站,怎么证明你来过?

客户端 服务端

  1. 服务端给客户端一个 信件,客户端下次访问服务端带上信件就可以了; cookie

  2. 服务器登记你来过了,下次你来的时候我来匹配你; seesion

保存会话的两种技术:

cookie

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

session

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

常见场景:网站登录之后,你下次不用再登录了,第二次访问直接就上去了!

7.2 Cookie

客户端请求时携带Cookie。

设置Cookie:

package com.wang.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

public class CookieDemo01 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");

        PrintWriter out = resp.getWriter();

        // Cookie,服务器端从客户端获取
        Cookie[] cookies = req.getCookies();

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

            for (int i = 0; i < cookies.length; i++){
                Cookie cookie = cookies[i];

                if (cookie.getName().equals("lastLoginTime")){
                    long lastLoginTime = Long.parseLong(cookie.getValue());
                    Date date = new Date(lastLoginTime);
                    out.write(date.toLocaleString());
                }
            }
        } else {
            out.write("第一次访问");
        }

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

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

删除Cookie:

package com.wang.servlet;

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

public class CookieDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建一个cookie,名字必须和要删除的一致
        Cookie cookie = new Cookie("lastLoginTime", String.valueOf(System.currentTimeMillis()));
        // 将cookie有效期设为0,立刻过期
        cookie.setMaxAge(0);
        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大小有限制4kb;

  • 浏览器上限:300个cookie。

7.3 Session(重点)

什么是Session:

  • 服务器会给每一个用户(浏览器)创建一个Seesion对象;

  • 一个Seesion独占一个浏览器,只要浏览器没有关闭,这个Session就存在;

  • 用户登录之后,整个网站它都可以访问!如可以保存一个登录用户的信息、购物车信息、在整个网站中经常会使用的数据,我们将它保存在Session中。

得到session,并往里面存数据:

package com.wang.servlet;

import com.wang.pojo.Person;

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

// 得到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的ID
        String sessionId = session.getId();
        // 给session中存数据
        session.setAttribute("name", new Person("Jungkook", 18));

        // 判断session是不是新建的
        if (session.isNew()){
            resp.getWriter().write("session创建成功,ID为" + sessionId);
        } else {
            resp.getWriter().write("seesion已经存在,ID为" + sessionId);
        }
    }

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

获取session中存储的数据:

package com.wang.servlet;

import com.wang.pojo.Person;

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

// 获取session中存储的数据
public class SessionDemo02 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");

        HttpSession session = req.getSession();
        Person person = (Person) session.getAttribute("name");
        resp.getWriter().write(person.toString());
    }

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

手动撤销session:

package com.wang.servlet;

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

// 撤销session,撤销后sessionId就会变
public class SessionDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        session.removeAttribute("name");

        session.invalidate();
    }

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

除手动撤销session外,还可以再xml配置中设置session自动过期:

<!--设置session默认的失效时间-->
<session-config>
    <!--失效时间以分钟为单位-->
    <session-timeout>5</session-timeout>
</session-config>

Session和cookie的区别:

  • cookie数据存放在客户的浏览器上,session数据放在服务器上

  • 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)

  • 两者最大的区别在于生存周期,一个是浏览器启动到浏览器关闭(session),一个是预先设置的生存周期,或永久的保存于本地的文件(cookie)。

8. JSP

8.1 JSP介绍及原理

Java Server Pages : Java服务器端页面,也和Servlet一样,用于动态Web技术。

与HTML类似,但HTML只给用户提供静态的数据,而JSP页面中可以嵌入JAVA代码,为用户提供动态数据。

原理:

tomcat中有一个work目录,IDEA中使用Tomcat时会在IDEA的tomcat中生成一个work目录,如:

C:Userswang_AppDataLocalJetBrainsIntelliJIdea2020.1	omcatUnnamed_javaweb-cookie-sessionwork

JSP最终也会被转换成为一个Java类(如下),JSP 本质上就是一个Servlet。

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

public void _jspDestroy() {        // 销毁
    }

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) {        // JSP服务
    ……
    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;                    // 当前
    3. 输出页面前增加的代码;
          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;
    4. 以上的这些个对象我们可以在JSP页面中直接使用。
    }

8.2 JSP基础语法

<%     %>        JSP脚本
<%=    %>        JSP表达式
<%! %>        JSP声明
<%-- --%>    JSP注释

例:

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

  <%--1. JSP表达式(<%= 变量或者表达式%>)--%>
  <%= new java.util.Date()%>
  
  <%--2. jsp脚本片段--%>
  <%
    int sum = 0;
    for (int i = 1; i <=100 ; i++) {
      sum+=i;
    }
    out.println("<h1>Sum="+sum+"</h1>");
  %>

  <%--3. jsp声明--%>
  <%!
    static {
      System.out.println("Loading Servlet!");
    }

    private int globalVar = 0;

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

  </body>
</html>

JSP声明会被编译到JSP生成Java的类中;而其他的就会被生成到Java类的_jspService方法中!

8.3 JSP指令

<%@page …… %>

<%--定制错误页面--%>
<%@page errorPage="error/500.jsp" %>

也可以使用xml配置错误页面:

<error-page>
    <error-code>500</error-code>
    <location>/error/500.jsp</location>
</error-page>

<%@include file="……"%>

<%@include file="common/head.jsp"%>
<h1>body</h1>
<%@include file="common/foot.jsp"%>

也可以使用JSP标签实现其它页面的引用:

<jsp:include page="common/head.jsp"/>
<h1>body</h1>
<jsp:include page="common/foot.jsp"/>

但,前者会将两个页面合二为一,后者只是拼接页面,本质上还是两个页面。

<%@taglib prefix="……"%>

详见8.5

8.4 9大内置对象

  • Request 存东西

  • Response

  • PageContext 存东西

  • Session 存东西

  • Application 【SerlvetContext】 存东西

  • config 【SerlvetConfig】

  • out

  • page ,不用了解

  • exception

pageContext:保存的数据只在一个页面中有效; request:保存的数据只在一次请求中有效,请求转发会携带这个数据,如新闻; session:保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器,如购物车; application:保存的数据只在服务器中有效,从打开服务器到关闭服务器,如聊天数据。

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

EL表达式 ${ }

  • 获取数据

  • 执行运算

  • 获取web开发的常用对象

JSP标签

例:

<jsp:forward page="/jsptag2.jsp">
    <jsp:param name="name" value="Jungkook"/>
    <jsp:param name="age" value="18"/>
</jsp:forward>

JSTL(JSP标准标签库)

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

  • 核心标签(重点)

  • 格式化标签

  • SQL 标签

  • XML 标签

  • JSTL 函数

JSTL标签库使用步骤:1. 引入对应的 taglib;2. 使用其中的方法

注:在Tomcat 也需要引入 jstl的包,否则会报错:JSTL解析错误

coreif:

<%@ 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>coreif</title>
</head>
<body>
<h1>JSTL CORE IF 测试</h1>
<form action="coreif.jsp" method="get">
    <input type="text" name="username" value="${param.username}">
    <input type="submit" value="登录">
</form>

<c:if test="${param.username == 'admin'}" var="isAdmin">
    <c:out value = "Welcome!"/>
</c:if>

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

corewhen:

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

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

coreforeach:

<%@ 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>coreforeach</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}"/> <br>
</c:forEach>

<hr>

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

</body>
</html>

9. JavaBean

JavaBean有特定的写法:

  • 必须要有一个无参构造

  • 属性必须私有化

  • 必须有对应的get/set方法

ORM :对象关系映射:表-->类、字段-->属性、行记录-->对象

<%@ page import="com.wang.pojo.People" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>javabean</title>
</head>
<body>

<%--
    java语句
    People people = new People();
    people.setName("JungKook");
    people.setId(1);
    people.setAge(18);
    String name = people.getName();
    int id = people.getId();
    int age = people.getAge();
--%>
<jsp:useBean id="people" class="com.wang.pojo.People" scope="page"/>

<jsp:setProperty name="people" property="name" value="JungKook"/>
<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="age" value="18"/>

姓名:<jsp:getProperty name="people" property="name"/>
ID:<jsp:getProperty name="people" property="id"/>
年龄:<jsp:getProperty name="people" property="age"/>
</body>
</html>

10. MVC三层架构

Model View Controller

Model

  • 业务处理 :业务逻辑(Service)

  • 数据持久层:CRUD(Dao - 数据持久化对象)

View

  • 展示数据

  • 提供链接发起Servlet请求(a,form,img…)

Controller(Servlet)

  • 接收用户的请求 (req:请求参数、Session信息….)

  • 交给业务层处理对应的代码

  • 控制视图的跳转

例:登录--->接收用户的登录请求 ---> 处理用户的请求(获取用户登录的参数,username,password) ---> 交给业务层处理登录业务(判断用户名密码是否正确:事务) ---> Dao层查询用户名和密码是否正确 --> 数据库

11. Filter(重点)

过滤器 ,用来过滤网站的数据,在服务器请求Servlet、JSP、HTML、静态资源等时使用,如

  • web服务器有一些垃圾请求,后台不应该处理或者应该报错;

  • 处理乱码问题。

Filter开发步骤,以中文乱码过滤为例:

  1. 添加Maven依赖;

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
        </dependency>
    </dependencies>
    编写过滤器和含有中文输出的Demo
  2. 导包时一定要导入javax.servlet.Filter

    package com.wang.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初始化");
        }
    
        // 1. 过滤中的所有代码,在过滤特定请求的时候都会执行
        // 2. 必须要让过滤器继续通行,chain.doFilter(request,response);
        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销毁");
        }
    }
    package com.wang.servlet;
    
    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 Demo extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.getWriter().write("你好!");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
  3. 在web.xml中配置过滤器,注册servlet

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <servlet>
            <servlet-name>hello</servlet-name>
            <servlet-class>com.wang.servlet.Demo</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/servlet/hello</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>encodingFilter</filter-name>
            <filter-class>com.wang.filter.CharacterEncodingFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>encodingFilter</filter-name>
            <url-pattern>/servlet/*</url-pattern>
        </filter-mapping>
    </web-app>
  4. 测试

    发现请求过滤后的路径(http://localhost:8080/sf/servlet/hello)时可以正确输出中文字符,未过滤的路径(http://localhost:8080/sf/hello)则会输出乱码。

注:Maven项目突然报错:程序包不存在,但Maven依赖导入成功,可能是IDEA版本有bug,之前使用的是2020.1.1,改为最新的2020.3.2后不会target文件,最后安装2020.1.4运行成功。

案例:用户登录之后才能进入主页,用户未登录或者注销后则不能进入主页。

  1. 编写登录、登陆成功、登录失败页面

    • 登录页面:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>login</title>
    </head>
    <body><form action="/sf/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>success</title>
    </head>
    <body><h1>Success!</h1>
    <p><a href="/sf/servlet/logout">注销</a> </p></body>
    </html>
    • 登录失败页面:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>errorPage</title>
    </head>
    <body><h1>Error!</h1>
    <p><a href="/sf/servlet/logout">返回</a> </p></body>
    </html>
  2. 编写登录和注销的Servlet代码

    • 登录:

    package com.wang.servlet;
    ​
    import com.wang.utils.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.UserSession, req.getSession().getId());
                resp.sendRedirect("/sf/sys/success.jsp");
            } else {
                resp.sendRedirect("/sf/error.jsp");
            }
        }
    ​
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    • 注销:

    package com.wang.servlet;
    ​
    import com.wang.utils.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 userSession = req.getSession().getAttribute(Constant.UserSession);
            if (userSession != null) {
                req.getSession().removeAttribute(Constant.UserSession);
            }
            resp.sendRedirect("/sf/login.jsp");
        }
    ​
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
  3. 编写过滤器

    package com.wang.filter;
    ​
    import com.wang.utils.Constant;
    ​
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    ​
    public class LoginFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    ​
        }
    ​
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;
    ​
            if (req.getSession().getAttribute(Constant.UserSession) == null){
                resp.sendRedirect("/sf/error.jsp");
            }
    ​
            chain.doFilter(req, resp);
        }
    ​
        @Override
        public void destroy() {
    ​
        }
    }
  4. 注册Servlet

    <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.wang.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/servlet/login</url-pattern>
    </servlet-mapping><servlet>
        <servlet-name>logout</servlet-name>
        <servlet-class>com.wang.servlet.LogoutServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>logout</servlet-name>
        <url-pattern>/servlet/logout</url-pattern>
    </servlet-mapping>
  5. 注册Filter

    <filter>
        <filter-name>loginFilter</filter-name>
        <filter-class>com.wang.filter.LoginFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>loginFilter</filter-name>
        <url-pattern>/sys/*</url-pattern>
    </filter-mapping>
  6. 测试:只有用户名为“admin”时才会跳转到登陆成功页面,用户名错误或者注销后跳转到登陆失败页面。

12. 监听器

12. 监听器

  1. 编写监听器

    package com.wang.listener;
    
    import com.wang.utils.Constant;
    
    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    
    // 统计网站在线人数,即统计Seesion
    public class OnlineCountListener implements HttpSessionListener {
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            ServletContext servletContext = se.getSession().getServletContext();
            Integer onlineCount = (Integer) servletContext.getAttribute(Constant.OnlineCount);
    
            if (onlineCount == null){
                onlineCount = new Integer(1);
            } else {
                int count = onlineCount.intValue();
                onlineCount = new Integer(count + 1);
            }
    
            servletContext.setAttribute(Constant.OnlineCount, onlineCount);
        }
    
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            ServletContext servletContext = se.getSession().getServletContext();
            Integer onlineCount = (Integer) servletContext.getAttribute(Constant.OnlineCount);
    
            if (onlineCount == null){
                onlineCount = new Integer(0);
            } else {
                int count = onlineCount.intValue();
                onlineCount = new Integer(count - 1);
            }
    
            servletContext.setAttribute(Constant.OnlineCount, onlineCount);
        }
    }
  2. 编写显示页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$</title>
      </head>
      <body>
    
      <h1>当前有 <span><%=this.getServletConfig().getServletContext().getAttribute("OnlineCount")%></span> 人在线</h1>
    
      </body>
    </html>
  3. 注册监听器

    <!--注册监听器-->
    <listener>
        <listener-class>com.wang.listener.OnlineCountListener</listener-class>
    </listener>
  4. 测试

    运行Tomcat时可能不止一个用户在线,重新部署后就会变成一个用户。同一个浏览器为一个用户,因为一个浏览器公用一个Session。

 附:狂神b站视频链接

原文地址:https://www.cnblogs.com/java-learning-xx/p/14613405.html