servlet

HelloServlet

1.创建一个maven项目

  • 先创建jar包管理的父项目,再创建webapp子项目
  • 优化maven环境
  • 更新web.xml
  • webapp的maven项目默认没有java和resourses这两个文件夹,一般要加上

更新后的web.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  5. http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  6. version="4.0"
  7. metadata-complete="true">
  8. </web-app>

2.编写servlet

servlet程序编写步骤:

  • 写一个普通类
  • 实现servlet接口(sun公司有两个默认的servlet接口我们可以使用)

编写一个普通的类

  1. package com.xiao.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class HelloServlet extends HttpServlet {
  9. @Override
  10. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  11. PrintWriter writer = resp.getWriter();
  12. writer.println("Hello,Servlet");
  13. }
  14. @Override
  15. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  16. super.doPost(req, resp);
  17. }
  18. }

编写Servlet映射

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  5. http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  6. version="4.0"
  7. metadata-complete="true">
  8. <servlet>
  9. <servlet-name>hello</servlet-name>
  10. <servlet-class>com.xiao.servlet.HelloServlet</servlet-class>
  11. </servlet>
  12. <servlet-mapping>
  13. <servlet-name>hello</servlet-name>
  14. <url-pattern>/hello</url-pattern>
  15. </servlet-mapping>
  16. </web-app>

配置TomCat(IDEA里面基础TomCat)

启动

我对这一过程的理解:

  • 在之前的TomCat里面我们可以看到,网页上的资源实际上是电脑上某个文件夹里面的文件
  • 我们依赖于TomCat这个服务器来发送请求或接受响应,前提是这个WebApp是部署(deploy)在TomCat的管理之下的,
  • 我们知道,TomCat启动时会加载所有WebApps下面的文件,这是最传统的方式,
  • 然而在idea中却不是这样的,因为在项目启动后TomCat的WebApps里面没有这个项目的信息,但只要可以运行就一定是被TomCat加载成功的,那是怎么样的呢?
  • 这一段的下面描述了idea如何将web项目部署在TomCat里面
  • …………
  • 完成部署后,就可以运行了。
  • 但是还有两个问题:
  • 为什么要在web.xml里面配置与
  • 服务器通过与找到servlet实现类后是怎么完成工作的

我的理解:

问题1:

  • 正如之前在TomCat所说,每一份资源实际上就是一份文件,比如我们要获取服务器管理的某份资源,就要在URI中加上这个文件的名称,如http://baidu.com/index.html,获取到的就是index.html这个文件。
  • 也就是说:我们需要指定目标文件的目录才可以获取到资源。
  • 同样的我们要通过一个Servlet实现类完成请求,获取响应,也就需要一个路径来让TomCat可以执行这个类的方法,
  • 但是我们只是写了一个类,TomCat是无法知道的(包括位置与功能),
  • 为此我们需要为这个类分配至少一个路径,这个路径就相当于是一个映射(mapping),这里面有两个信息
  • servlet-name:为要指定的Servlet类起一个别名(一般与类名有关)
  • url-pattern:为这个路径起一个名字
  • 通过这个映射的servlet-name可以找到一个有相同的servlet-name的,这里面也有两个信息
  • servlet-name:与上面路径的servlet-name必须一致
  • servlet-class:这就是我们最终要定位的类
  • 在URI中指定一个路径后,TomCat会通过这个路径名所在的里面的servlet-name定位到对应的,继而找到servlet-class,就可以执行里面的方法了

问题2:

  • 找到servlet-class对应的类后,
  • 查看这个类里面实现了(或重写了什么方法)
  • 如果在表单的提交中设置了是post方法,那就是doPost,默认是doGet.

执行过程:

补充:idea如何将web项目部署在TomCat里面

IDEA 部署 WEB 项目的方式比较特别,它对每一套应用系统都新建了一份服务器配置,使得各个应用环境互不影响,且无需修改 Tomcat 原生配置 ${CATALINA_HOME}/conf/,保证了 IDEA 项目开发不影响服务器配置,减少服务器配置问题

IDEA 部署步骤:

  1. 注意 Artifact exploded 类型
  2. 将编译好的 JAVA 字节码与资源文件整理好,拷贝到: {项目工作空间}\out\artifacts\ResidentManager_war_exploded 文件夹下(默认)
  3. 新建一个文件夹以存放一份单独的服务器配置,默认为 C:\Users\Administrator.IntelliJIdea2017.1\system\tomcat\Unnamed_ResidentManager
  4. 拷贝 Tomcat 原生配置:将 {CATALINA_HOME}/conf/ 文件夹内容全部拷贝至 C:\Users\Administrator.IntelliJIdea2017.1\system\tomcat\Unnamed_ResidentManager\conf\
  5. 新建 XML 配置以链接项目资源:C:\Users\Administrator.IntelliJIdea2017.1\system\tomcat\Unnamed_ResidentManager\conf\Catalina\localhost\ResidentManager.xml
  6. 6 . 在服务器启动之前,IDEA 将环境变量 {CATALINA_BASE} 设置为第二步新建的文件夹:C:\Users\Administrator.IntelliJIdea2017.1\system\tomcat\Unnamed_ResidentManager\
  7. 7 . 启动 Tomcat,其会自动读取环境变量 {CATALINA_BASE},进而找到第四步新建的文件,读取 XML 配置,加载项目资源
  8. 8 .至此,打开浏览器访问
  1. <!--这个文件是核心!-->
  2. <Context path="/s1" docBase="G:\java\javaWeb\out\artifacts\servlet01_war_exploded" />

文件位置:

关于servlet-Mapping

servlet-Mapping里面的url-pattern有一些其他的写法

  • 可以为一个servlet类配多个路径,每一个都可以定位到servlet类
  1. <servlet>
  2. <servlet-name>hello</servlet-name>
  3. <servlet-class>com.xiao.servlet.HelloServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>hello</servlet-name>
  7. <url-pattern>/hello</url-pattern>
  8. </servlet-mapping>
  9. <servlet-mapping>
  10. <servlet-name>hello</servlet-name>
  11. <url-pattern>/hello2</url-pattern>
  12. </servlet-mapping>
  • 通配路径,这样的话会直接跳到指定的servlet类返回的页面,而不会到index.jsp
  1. <servlet-mapping>
  2. <servlet-name>hello</servlet-name>
  3. <url-pattern>/*</url-pattern>
  4. </servlet-mapping>
  • 指定后缀的路径格式(finfvvbfbvk.xiao等等都可以访问)
  1. <servlet-mapping>
  2. <servlet-name>hello</servlet-name>
  3. <url-pattern>*.xiao</url-pattern>
  4. </servlet-mapping>
  • 优先级的问题:
  • 如果有通配路径,会默认进入对应页面
  • 固定的路径的优先级是最高的

ServletContext介绍及用法

ServletContext官方叫servlet上下文。服务器会为每一个工程创建一个对象,这个对象就是ServletContext对象。这个对象全局唯一,而且工程内部的所有servlet都共享这个对象。所以叫全局应用程序共享对象。

作用

  • 是一个域对象

  • 可以读取全局配置参数

  • 可以搜索当前工程目录下面的资源文件

  • 可以获取当前工程名字(了解)

servletContext是一个域对象

域对象介绍

  1. 域对象是服务器在内存上创建的存储空间,用于在不同动态资源(servlet)之间传递与共享数据。
  1. /*这个请求在servletContext里面添加一条信息*/
  2. package com.xiao.servlet;
  3. import javax.servlet.ServletContext;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.io.IOException;
  9. public class HelloServlet extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  12. System.out.println("Hello Servlet");
  13. ServletContext servletContext = this.getServletContext();
  14. servletContext.setAttribute("userName","肖云飞");
  15. }
  16. @Override
  17. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  18. doGet(req, resp);
  19. }
  20. }
  1. /*这个请求可以获取servletContext的属性*/
  2. package com.xiao.servlet;
  3. import javax.servlet.ServletContext;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.io.IOException;
  9. import java.io.PrintWriter;
  10. public class GetName extends HttpServlet {
  11. @Override
  12. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  13. resp.setCharacterEncoding("utf-8");
  14. resp.setContentType("text/html");
  15. ServletContext servletContext = this.getServletContext();
  16. String userName = (String) servletContext.getAttribute("userName");
  17. PrintWriter writer = resp.getWriter();
  18. writer.print(userName);
  19. }
  20. @Override
  21. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  22. doGet(req, resp);
  23. }
  24. }

注册

  1. <servlet>
  2. <servlet-name>hello</servlet-name>
  3. <servlet-class>com.xiao.servlet.HelloServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>hello</servlet-name>
  7. <url-pattern>/hello</url-pattern>
  8. </servlet-mapping>
  9. <servlet>
  10. <servlet-name>get</servlet-name>
  11. <servlet-class>com.xiao.servlet.GetName</servlet-class>
  12. </servlet>
  13. <servlet-mapping>
  14. <servlet-name>get</servlet-name>
  15. <url-pattern>/get</url-pattern>
  16. </servlet-mapping>

注意:必须要先发送添加属性的请求才可以在第二个里面获取到

应用1(获取初始化参数)

1.在web.xml里面设置初始化参数

  1. <context-param>
  2. <!-- 这是一个mysql连接信息-->
  3. <param-name>jdbc_url</param-name>
  4. <param-value>jdbc:mysql:///jdbcstudy?characterEncoding=utf8&amp;useUnicode=true&amp;
  5. useSSL=false&amp;serverTimezone=UTC</param-value>
  6. </context-param>

2.Servlet实现类

  1. package com.xiao.servlet;
  2. import javax.servlet.ServletContext;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.io.PrintWriter;
  9. public class GetParamServlet extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  12. /*获取初始化参数*/
  13. ServletContext servletContext = this.getServletContext();
  14. String jdbc_url = servletContext.getInitParameter("jdbc_url");
  15. PrintWriter writer = resp.getWriter();
  16. writer.write(jdbc_url);
  17. }
  18. @Override
  19. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  20. doGet(req, resp);
  21. }
  22. }

3.配置路径

  1. <servlet>
  2. <servlet-name>jdbc</servlet-name>
  3. <servlet-class>com.xiao.servlet.GetParamServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>jdbc</servlet-name>
  7. <url-pattern>/jdbc</url-pattern>
  8. </servlet-mapping>

获取Propetries配置信息

1.配置Propetries

  1. url=jdbc:mysql:///jdbcstudy?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
  2. user=root
  3. pwd=123456

2.在pom.xml里面配置资源过滤(不是必要的,只有当Propetries文件不在resources里面才需要配置)

  1. /*这是maven项目的资源过滤设置*/
  2. <build>
  3. <resources>
  4. <resource>
  5. <directory>src/main/java</directory>
  6. <includes>
  7. <include>**/*.properties</include>
  8. <include>**/*.xml</include>
  9. </includes>
  10. <filtering>false</filtering>
  11. </resource>
  12. <resource>
  13. <directory>src/main/resources</directory>
  14. <includes>
  15. <include>**/*.properties</include>
  16. <include>**/*.xml</include>
  17. </includes>
  18. <filtering>false</filtering>
  19. </resource>
  20. </resources>
  21. </build>

3.Servlet实现类

  1. package com.xiao.servlet;
  2. import javax.servlet.ServletContext;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.io.PrintWriter;
  10. import java.util.Properties;
  11. public class GetPropertiesServlet extends HttpServlet {
  12. @Override
  13. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  14. ServletContext servletContext = this.getServletContext();
  15. InputStream resourceAsStream = servletContext.getResourceAsStream("/WEB-INF/classes/db.properties");
  16. Properties properties = new Properties();
  17. properties.load(resourceAsStream);
  18. String url = properties.getProperty("url");
  19. String user = properties.getProperty("user");
  20. String pwd = properties.getProperty("pwd");
  21. PrintWriter writer = resp.getWriter();
  22. writer.println(url);
  23. writer.println(user);
  24. writer.println(pwd);
  25. }
  26. @Override
  27. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  28. doGet(req, resp);
  29. }
  30. }

4.配置路径

  1. <servlet>
  2. <servlet-name>pro</servlet-name>
  3. <servlet-class>com.xiao.servlet.GetPropertiesServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>pro</servlet-name>
  7. <url-pattern>/pro</url-pattern>
  8. </servlet-mapping>

请求转移:在这个Servlet里面实现另一个Servlet的请求

  1. package com.xiao.servlet;
  2. import javax.servlet.RequestDispatcher;
  3. import javax.servlet.ServletContext;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.io.IOException;
  9. public class DispatcherServlet extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  12. ServletContext servletContext = this.getServletContext();
  13. /*获得一个请求转换器,需要指定转换的路径*/
  14. RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/pro");
  15. /*前往指定的路径获取req和resp对象*/
  16. requestDispatcher.forward(req,resp);
  17. }
  18. @Override
  19. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  20. doGet(req, resp);
  21. }
  22. }

配置路径

  1. <servlet>
  2. <servlet-name>pro2</servlet-name>
  3. <servlet-class>com.xiao.servlet.DispatcherServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>pro2</servlet-name>
  7. <url-pattern>/pro2</url-pattern>
  8. </servlet-mapping>

HttpServletResponse应用

response实现下载文件

步骤:

  1. 指定要下载的文件的路径
  2. 指定要下载的文件的名字
  3. 配置响应头信息,指明要下载的文件名
  4. 通过文件路径获取输入流对象(FileInputStream)
  5. 通过response获得输出流对象(OutPutStream)
  6. 配一个缓冲区
  7. FileInputStream输如到缓存区
  8. OutPutStream从缓冲区读取到后输出
  9. 关闭io资源

1.首先要有文件(test.md)

2.写Servlet类

  1. package com.xiao.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.ServletOutputStream;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.FileInputStream;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. public class Rensponse extends HttpServlet {
  11. @Override
  12. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  13. // 1. 指定要下载的文件的路径
  14. String path = "G:\\java\\javaWeb\\servlet-02\\target\\servlet-02\\WEB-INF\\classes\\test.md";
  15. // 2. 指定要下载的文件的名字
  16. String name = path.substring(path.lastIndexOf("\\")+1);
  17. // 3. 配置响应头信息,指明要下载的文件名(注意attachment后面是分号)
  18. resp.setHeader("Content-disposition","attachment;filename="+name);
  19. // 4. 通过文件路径获取输入流对象(FileInputStream)
  20. InputStream in = new FileInputStream(path);
  21. // 5. 通过response获得输出流对象(OutPutStream)
  22. ServletOutputStream out = resp.getOutputStream();
  23. // 6. 配一个缓冲区
  24. byte[] buffer = new byte[1024];
  25. int len;
  26. // 7. FileInputStream输如到缓存区
  27. // 8. OutPutStream从缓冲区读取到后输出
  28. while((len=in.read(buffer))>0){
  29. out.write(buffer,0,len);
  30. }
  31. //9.关闭资源
  32. out.close();
  33. in.close();
  34. }
  35. @Override
  36. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  37. doGet(req, resp);
  38. }
  39. }

3.注册路径

  1. <servlet>
  2. <servlet-name>fdl</servlet-name>
  3. <servlet-class>com.xiao.servlet.Rensponse</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>fdl</servlet-name>
  7. <url-pattern>/fdl</url-pattern>
  8. </servlet-mapping>

4.结果(一进入/fdl这个路径就会显示)

如果是中文名称的文件呢?这时就必须将filename用utf-8编译

  1. String path = "G:\\java\\javaWeb\\servlet-02\\target\\servlet-02\\WEB-INF\\classes\\测试.md";
  2. resp.setCharacterEncoding("utf-8");/*不是必要的*/
  3. resp.setHeader("Content-disposition","attachment;filename="+ URLEncoder.encode(name,"utf-8"));

结果(访问/fdl路径)

简单练习:在页面上每3面显示一个随机的6位数字

  1. package com.xiao.servlet;
  2. import javax.imageio.ImageIO;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.awt.*;
  8. import java.awt.image.BufferedImage;
  9. import java.io.IOException;
  10. import java.util.Random;
  11. public class RendomCode extends HttpServlet {
  12. @Override
  13. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  14. /*设置页面每3秒刷新一次*/
  15. resp.setHeader("refresh","3");
  16. /*现在在内存中生成了一张图片*/
  17. BufferedImage image = new BufferedImage(100,30,BufferedImage.TYPE_INT_RGB);
  18. /*获得一支画笔*/
  19. Graphics2D graphics = (Graphics2D)image.getGraphics();
  20. /*填充背景*/
  21. graphics.setColor(Color.CYAN);
  22. graphics.fillRect(0,0,100,30);
  23. graphics.setColor(Color.MAGENTA);
  24. graphics.setFont(new Font(null,Font.ITALIC,25));
  25. graphics.drawString(getNum(),10,20);
  26. /*让浏览器以jpg图片的方式打开图片*/
  27. resp.setContentType("image/jpg");
  28. /*让浏览器禁止缓存当前的内容*/
  29. resp.setDateHeader("expries", -1);
  30. resp.setHeader("Cache-Control", "no-cache");
  31. resp.setHeader("Pragma", "no-cache");
  32. ImageIO.write(image,"jpg",resp.getOutputStream());
  33. }
  34. public String getNum(){
  35. Random random = new Random();
  36. int i = random.nextInt(999999);/*随机生成一个0到999999的数字*/
  37. String num = i+"";
  38. StringBuffer stringBuffer = new StringBuffer(num);
  39. /*确保生成的一定是一个6位数*/
  40. for (int n = 0;n < 6-num.length();n++){
  41. stringBuffer.append(new Random().nextInt(9));
  42. }
  43. String s = stringBuffer.toString();
  44. return s;
  45. }
  46. @Override
  47. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  48. doGet(req, resp);
  49. }
  50. }

配置路径

  1. <servlet>
  2. <servlet-name>code</servlet-name>
  3. <servlet-class>com.xiao.servlet.RendomCode</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>code</servlet-name>
  7. <url-pattern>/code</url-pattern>
  8. </servlet-mapping>

结果(每隔3秒更新一次):

response实现重定向[重点]

客户端访问服务端后,服务端会通知客户端前往另一个URL的地址(比如登录页面)。

使用的方法

    void sendRedirect(String var1) throws IOException;
package com.xiao.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 ReDirect extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendRedirect("/s2/pro");
    }

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

可以看到实际上就是==resp.sendRedirect(“/s2/pro”);==实现

原理:

  • resp.setStatus(302);
    resp.setHeader("location","/s2/pro");
    

重定向与转移的区别:

  • 相同:两者都可以通过访问某一个页面转到另一个页面
  • 不同:

    • 重定向时URL会发生变化
    • 转移时URL不会变化(在本页面完成向另一个页面的请求)

HttpServletRequest

初次尝试

1.先在index.jsp里面添加一个表单

<html>
<body>

<form action="${pageContext.request.contextPath}/login" method="get">
    <p>姓名:<input type="text" name="username"></p>
    <p>密码:<input type="password" name="password"></p>
    <p>submit:<input type="submit"></p>
</form>
</body>
</html>

2.Servlet类

package com.xiao.request;

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 RequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入请求");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println(username+":"+password);
        resp.sendRedirect("/s3/success.jsp");
    }

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

3.(进入后跳转的页面)success.jsp

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

结果:

repuest作用

当客户端向服务器发送HTTP请求时,所有的请求信息都会被封装在HttpServletRequest这个对象里面,通过这个对象可以获得客户端的有关请求的所有信息

应用

  • 获取属性

    常用:

String password = req.getParameter("password");//获取单个值的参数
String[] hobbies = req.getParameterValues("hobbies");//获取多个值的参数


1.index.jsp

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

<div>
    <form action="${pageContext.request.contextPath}/login" method="post">
        <p>姓名:<input type="text" name="username"></p>
        <p>密码:<input type="password" name="password"></p>
        <p>爱好:<input type="checkbox" name="hobbies" value="唱歌">唱歌</p>
        <p>     <input type="checkbox" name="hobbies" value="代码">代码</p>
        <p>     <input type="checkbox" name="hobbies" value="跳舞">跳舞</p>
        <p>submit:<input type="submit"></p>
    </form>
</div>

</body>
</html>

2.Servlet类

package com.xiao.request;

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 RequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入请求");
        /*设置请求与响应的编码方式防止出现乱码*/
        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);
    }
}

/login路径配置

  <servlet>
    <servlet-name>login</servlet-name>
    <servlet-class>com.xiao.request.RequestTest</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/login</url-pattern>
  </servlet-mapping>

测试:

请求转移与重定向的对比(重点)

首先是这两个方法的实现

/*请求转移*/
//方法1:通过Context对象(少用)
    RequestDispatcher getRequestDispatcher(String var1);

    RequestDispatcher getNamedDispatcher(String var1);
//方法2:通过request对象(常用)
    RequestDispatcher getRequestDispatcher(String var1);
/*以上两个方法获得了将要转移到的请求(var1是同一个WebApp下面的另一个路径)
  然后调用forward方法或include方法转移到对应页面执行
  这里又有一个问题:forward方法或include方法有什么区别(在这个代码块下面解决)
*/
    void forward(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    void include(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

/*重定向*/
    resp.sendRedirect("http://www.baidu.com");
    void sendRedirect(String var1) throws IOException;
/*通过response对象的sendRedirect方法实现前往指定的URL执行
  (注意,不同于上面的请求转移,这个方法可以前往非本WebApp的路径,比如百度)
*/

forward方法或include方法区别

  • forward是向前的意思,表示当前这个请求已经完成了,要前往下一个请求,由转移到的请求完成最后的响应。

  • include是包含的意思,表示这个在当前这个请求里面需要先完成一个其他的请求,该请求接收到转移到的请求的响应后连同该请求的响应一同返回。

  • 注意1:forward方法下即使是在开始转移之前(forward()方法调用之前)就让response对象输出数据(write或print),也不会响应,forward一定是由最后转移到的页面完成响应

  • 注意2:include方法下如果在开始转移之前(include()方法调用之前)就让response对象输出数据(write或print),会出现问题

验证如下:

forward方法

(只写方法体)[注意:这里在转移之前就调用了writer.println(“转移”)]

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入请求");
        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));
        PrintWriter writer = resp.getWriter();
        writer.println("转移");
        /*通过请求转发*/
        req.getRequestDispatcher("/success.jsp").forward(req,resp);
        writer.println("成功");
    }

结果(转移和成功都没有显示):

include方法

(只写方法体)[注意:这里在转移之前就调用了writer.println(“转移”)]

   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入请求");
        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));
        PrintWriter writer = resp.getWriter();
        writer.println("转移");
        /*通过请求转发*/
        req.getRequestDispatcher("/success.jsp").include(req,resp);
        writer.println("成功");
    }

结果(两个页面的请求都响应了,但由于在转移前就输出,出现了这样的问题):

这是正常情况(只在include方法之后写数据):

请求转移与重定位区别

  1. 请求转移(顾名思义)是由请求实现的,而重定位是响应实现的(会返回3xx的状态码)
  2. 请求转移是路径不会跳转到转移的目的地,而重定向时会
  3. 请求转移只能跳转到当前的webApp下的路径,而重定向可以跳转到其他的webApp下的路径(比如百度)
  4. 由两个方法的参数可以看到:请求转移后forward或include需要req和resp两个参数,而重定向只需要目标路径
  5. 通过 4 可以分析出:请求转移时两个请求实际上使用的是相同的req和resp,而重定向时两个请求使用不同的req和resp
  6. 通过 5 可以分析出:重定向时前一个request和response包含的内容后一个不能获取,比如req.setAttribute(String var1, Object var2)的属性就无法在新的请求中获取[这个问题实际上与作用域有关系,这个问题后面再说]

Cookie与Session(重点)

一、共同之处

  cookie和session都是用来跟踪浏览器用户身份的会话方式。

二.工作原理

  • cookie
    (1)浏览器端第一次发送请求到服务器端(这时没有cookie的信息)
    (2)服务器端创建Cookie,该Cookie中包含用户的信息,然后将该Cookie发送到浏览器端
    (3)浏览器端再次访问服务器端时会携带服务器端创建的Cookie
    (4)服务器端通过Cookie中携带的数据区分不同的用户
  • session
    (1)浏览器端第一次发送请求到服务器端,服务器端创建一个Session,同时会创建一个特殊的Cookie(name为JSESSIONID的 固定值,value为session对象的ID),然后将该Cookie发送至浏览器端
    (2)浏览器端发送第N(N>1)次请求到服务器端,浏览器端访问服务器端时就会携带该name为JSESSIONID的Cookie对象
    (3)服务器端根据name为JSESSIONID的Cookie的value(sessionId),去查询Session对象,从而区分不同用户。

关于Cookie的理解与使用方法

Cookie原意是饼干,在这里的含义可以理解为会员信息卡,

  • 当会员与主办方成功取得联系后为了避免多次确定会员身份浪费时间,就为来过的会员发一张会员信息卡,并且主办方将会员的信息记录下来。
  • 以后每次会员来的时候,只需要出示会员卡比对身份就可以畅通了,
  • 有这张会员信息卡,会员可以在主办方的所有其他设施快速通信
  • 当然主办方可以设置会员卡的期限,超过这个期限,会员卡就失效了(需要重新找到主办方获得)。

在Web层面实际上也是这样的:

  • 用户第一次访问服务器时不携带cookie,服务器的响应头中会加入一个叫set-cookie的字段信息,
  • 用户接受到后就会设置一个cookie文件在内存里面(如果设置了cookie的),在以后的请求里面添加上cookie字段的信息(比如登录状态,购物车的情况等等)
  • 当用户进行其他的请求时服务器端将传来的cookie与session对比就知道这个用户的信息了

常用方法(首先一个在servlet将cookie理解为一个键值对,客户的cookie实际上就是多个这样的键值对):

    /*设置|获取域名*/
    public void setDomain(String domain) {this.domain = domain.toLowerCase(Locale.ENGLISH);}
    public String getDomain() {return this.domain;}
    /*设置|获得cookie的最大存在时间(如果这个有被设置的话就会把cookie存放在内存里面,否则当session中断是就删除)*/
    public void setMaxAge(int expiry) {this.maxAge = expiry;}
    public int getMaxAge() {return this.maxAge;}
    /*设置|获得路径*/
    public void setPath(String uri) {this.path = uri;}
    public String getPath() {return this.path;}
    /*设置|获得是否支持SSL加密*/
    public void setSecure(boolean flag) {this.secure = flag;}
    public boolean getSecure() {return this.secure;}
    /*获取这个cookie的键值对中键的名字*/
    public String getName() {return this.name;}
    /*设置|获取这个cookie的键值对中值的属性*/
    public void setValue(String newValue) {this.value = newValue;}
    public String getValue() {return this.value;}

实例:

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*设置编码格式*/
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        /*设置新的cookie*/
        Cookie cookie1 = new Cookie("name","肖云飞");
        Cookie cookie2 = new Cookie("lastLoginInTime",System.currentTimeMillis()+"");
        /*通过resp添加cookie*/
        resp.addCookie(cookie1);
        resp.addCookie(cookie2);
        /*通过req获取cookie*/
        Cookie[] cookies = req.getCookies();
        PrintWriter writer = resp.getWriter();
        /*遍历,输出*/
        for (Cookie cookie:cookies){
            System.out.println(cookie.getName()+":"+cookie.getValue());
            writer.println(cookie.getName()+":"+cookie.getValue());
        }
    }

结果:

关于Session的理解与使用方法

session的意思是会话,一次会话就是打开浏览器进入一个webApp里面,在关闭浏览器之前,所有在这个webApp里面的操作都是这次会话的内容。

  • 正如上面cookie里面的比喻,session就是存放用户信息的一张表,
  • 这张表存放在主办方那里,也就是在服务器端保存,以一种类似于HashMap的结构存放
  • 当然,客户端可以请求查看这张表的信息(通过request对象获得)
  • session是用户首次向服务器端发送请求时服务器端生成的
  • session的核心内容时一个叫做ISESSIONID的键值对(首次请求时通过某种算法得到的),存放的实际上就是一个uid,这条信息会放在响应头的set-cookie里面,然后成为cookie里面的内容
  • 为什么说这条信息是核心呢?在一次会话期间,这个ISESSIONID是惟一的(当然可以手动注销这个session)!服务器端实际上是通过将客户端的cooike里面的ISESSIONID与自己的session信息里面的ISESSIONID对比来核实身份的。
  • 不同于cookie,session一般不会存放在服务器端很久(毕竟会有很多访问,如果不清理内存直接爆炸),
  • 注意:当会话结束后服务器端的session不会立即删除,只是失去关联,还会存放一小段时间,只是永远不可能再调用了。

常用方法:

    /*获取创建时间*/
    long getCreationTime();
    /*获取JSESSONID*/
    String getId();
    /*获取最近一次请求时间*/
    long getLastAccessedTime();
    /*获取Servlet上下文*/
    ServletContext getServletContext();
    /*获取|设置这条sesson的最大存在时间*/
    void setMaxInactiveInterval(int var1);
    int getMaxInactiveInterval();

    /*通过名字获取属性值*/
    Object getAttribute(String var1);
    /*获取所有属性名,返回一个枚举类型*/
    Enumeration<String> getAttributeNames();
    /*设置属性*/
    void setAttribute(String var1, Object var2);
    /*移除属性*/
    void removeAttribute(String var1);
    /*注销当前会话的session*/
    void invalidate();
    /*判断这个sesson是不是新的*/
    boolean isNew();

测试:

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*设置编码格式*/
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        /*通过request对象获得session*/
        HttpSession session = req.getSession();
        PrintWriter writer = resp.getWriter();
        if (session.isNew()){
            writer.println("当前session是新建的,id是:"+session.getId());
        }
        else {
            writer.println("当前session不是新建的,id是:"+session.getId());
        }
    }

配置路径(配置路径为通配,保证这个Servlet是第一个访问的页面):

  <servlet>
    <servlet-name>SessionDemo1</servlet-name>
    <servlet-class>com.xiao.request.SessionDemo1</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>SessionDemo1</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

结果:

刷新一次(表示再次请求)

发现:

  • 只有首次进入一个WedApp时才是新的session,后面的都是直接用原来的
  • 两次的Id是一样的

测试属性的设置与时删除:

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*设置编码格式*/
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        /*通过request对象获得session*/
        HttpSession session = req.getSession();
        PrintWriter writer = resp.getWriter();
        session.setAttribute("name","肖云飞");

        Enumeration<String> attributeNames = session.getAttributeNames();

        while (attributeNames.hasMoreElements()){
            String s = attributeNames.nextElement();
            String attribute = (String)session.getAttribute(s);
            System.out.println(s);
            writer.write(attribute);
        }
    }

结果:

void invalidate(); 这个方法就相当于关闭浏览器并且删除session

疑问:session是保存在服务器端的,为什么不是resopnse对象获取而是request对象获取?

我的理解:正如下面这幅如所示,每次客户端请求时都会由request调用getSession()方法,如果是第一次请求就新创建一个,并且将必要信息(如ISESSIONID)响应回来让客户端保存在cookie里面,之后再发送请求时就会通过ISESSIONID来匹配session,找到已存在的session,由此可见,实际上session是依赖于请求的,只有当请求发送时才会调用,对于response来说session没有意义,所以是通过request来调用。

关于不同的setAttribute()方法的作用

这几种对象设置的属性的区别实际上只是作用域的区别

  • context对象(页面上下文)的setAttribute()
  • context的作用域是最大的,它 代表这个webApp,所有想这个webApp发送的请求都共用一个context,所以这个对象的context可以存放webApp访问的人数等等全局性的属性.
  • session对象(会话)的setAttribute()
  • session的作用域仅次于context,它代表一次会话,是当前会话中所以这个wbbApp的页面共享,所以这个变量可以存放这次会话的访问次数等。
  • request对象(请求)的setAttribute()
  • request的作用域次于session,只有使用同一个request对象的两个Servlet可以共用。

JSP

Jsp的本质

Jsp的本质实际上就是一个Servlet,它是Servlet的简化版

JSP文件实际上还是一个java文件,编译之后生成字节码文件

我的电脑上的路径C:\Users\Lenovo.IntelliJIdea2019.1\system\tomcat\Unnamed_javaWeb_2\work\Catalina\localhost\s3\org\apache\jsp

打开这个java文件

包名与导入信息()

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

8大内置对象

    final javax.servlet.jsp.PageContext pageContext; //页面上下文,最小的作用域(可以用来获取其他的内置对象)
    javax.servlet.http.HttpSession session = null;   //session对象
    final javax.servlet.ServletContext application;  //应用上下文,最大的作用域
    final javax.servlet.ServletConfig config;        //配置  
    javax.servlet.jsp.JspWriter out = null;             //输出流
    final java.lang.Object page = this;                 //相当于servlet中的this,即JSP页面本身
    final javax.servlet.http.HttpServletRequest request;    //请求
    final javax.servlet.http.HttpServletResponse response;  //响应

实际输出

try {
      response.setContentType("text/html;charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                  null, true, 8192, true);  //得到pageContext对象,用来获取其他的对象
      _jspx_page_context = pageContext;   
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\n");
      out.write("<html>\n");
      out.write("<body>\n");
      out.write("\n");
      out.write("<div>\n");
      out.write("<form action=\"");
      out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${pageContext.request.contextPath}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null));
      out.write("/login\" method=\"post\">\n");
      out.write("<p>姓名:<input type=\"text\" name=\"username\"></p>\n");
      out.write("<p>密码:<input type=\"password\" name=\"password\"></p>\n");
      out.write("<p>爱好:<input type=\"checkbox\" name=\"hobbies\" value=\"女孩\">女孩</p>\n");
      out.write("<p><input type=\"checkbox\" name=\"hobbies\" value=\"代码\">代码</p>\n");
      out.write("<p><input type=\"checkbox\" name=\"hobbies\" value=\"唱歌\">唱歌</p>\n");
      out.write("<p><input type=\"checkbox\" name=\"hobbies\" value=\"跳舞\">跳舞</p>\n");
      out.write("<p>submit:<input type=\"submit\"></p>\n");
      out.write("</form>\n");
      out.write("</div>\n");
      out.write("\n");
      out.write("</body>\n");
      out.write("</html>\n");
}...

可以看出,实际上就是把JSP页面里面的内容用Servlet输出,只是用Servlet这样写太麻烦,所以用JSP简化。

包的准备

<!-- Servlet的依赖 -->
<dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>javax.servlet-api</artifactId>
     <version>4.0.1</version>
     <scope>provided</scope>
</dependency>

 <!--JSP的依赖 -->
<dependency>
     <groupId>javax.servlet.jsp</groupId>
     <artifactId>javax.servlet.jsp-api</artifactId>
     <version>2.3.1</version>
     <scope>provided</scope>
</dependency>

<!-- JSTL表达式的依赖-->
<dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>

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

基本语法

JSP表达式<%=%>

JSP表达式
语法<%= 变量或表达式%>
作用:将程序的输出完整地写到页面上
等效于:out.println(new java.util.Date());
    <%= new java.util.Date()%>

JSP脚本片段<%%>

    <%
        int i = 0;
        for (i = 0; i < 100; i++) {
            out.println(i);
        }
    %>

在java代码里面嵌入HTML

<%
    for (int y = 0; y < 5; y++) {
%>
    <h1>Hello World!</h1>
<%
    }
%>

JSP声明<%!%>

上面的代码都是在jspservice这个方法里写的
实际上可以在它的外面写,即JSP声明,它会被编译到类里面
<%!
 static{
     System.out.println("Loading...");
 }

 private int global = 100;

 protected void xiao(){
     System.out.println("Xiao");
 }
%>

JSP指令%@%

page实例:自定义500错误页面

<%@page contentType="text/html; charset=UTF-8" language="java" %>
<%@page errorPage="WEB-INF/error/error500.jsp" %>
<html>
<body>
<h2>Hello World!</h2>

<%= 1/0 %>

</body>
</html>

error500.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>500错误页面</title>
</head>
<body>

<img src="../imag/500.png" name="错误">

</body>
</html>

还有一种配置方法(在web.xml里面配置):

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

include实例:

include指令类似于请求转移时的include,会把另一个页面的响应嵌套在当前页面里面

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

9大内置对象与4大作用域

内置对象:

  • pageContext

  • session

  • application

  • config[ServletConfig]

  • out

  • page [几乎不用]

  • request

  • response

  • exception [java的异常]

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    application.setAttribute("name1","jspContext");
    session.setAttribute("name2","session");
    request.setAttribute("name3","request");
    pageContext.setAttribute("name4","pageContext");
%>

<%
    String name1 = (String) pageContext.getAttribute("name1");
    String name2 = (String) pageContext.getAttribute("name2");
    String name3 = (String) pageContext.getAttribute("name3");
    String name4 = (String) pageContext.getAttribute("name4");
%>

${name1}</br>
${name2}</br>
${name3}</br>
${name4}</br>

</body>
</html>

结果:

在另一个JSP里面尝试获取

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

<%
    String name1 = (String) application.getAttribute("name1");
    String name2 = (String) application.getAttribute("name2");
    String name3 = (String) application.getAttribute("name3");
    String name4 = (String) application.getAttribute("name4");
%>

${name1}</br>
${name2}</br>
${name3}</br>
${name4}</br>

</body>
</html>

结果:

实际上pageContext有两个获取Attribute的方法:

  • findAttribute();
  • getAttribute();

官方文档说,

  • findAttribute的获取机制类似于JVM的双亲委派机制,会依次从pageContext,request,session,application作用域里面寻找,找到就返回,没找到就返回null。
  • 而getAttribute只会在pageContext作用域里面寻找.找到就返回,没找到就返回null。

但是,在上面的测试中,我发现用findAttribute和getAttribute的效果是一样的,所以我尝试用session的getAttribute来获取,发现可以获取到application与session的属性!猜想,getSession可以获取到高作用域的Attribute.

为了解决上面的问题,做这样的一个测试:

在application与session里面set相同名字的Attribute,用session去获取

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

<%
    application.setAttribute("name1","application");
    session.setAttribute("name1","session");
    session.setAttribute("name2","session");
%>
<%
    String name1 = (String) session.getAttribute("name1");
    String name2 = (String) session.getAttribute("name2");
%>

${name1}</br>
${name2}</br>
</body>
</html>

结果:

说明:当两个不同作用域里面设置了相同名字的Arrtibute并且用低作用域去获取时,只会获取到低作用域的属性

接着测试,把上面的用session获取改为用application获取

<%
    String name1 = (String) application.getAttribute("name1");
    String name2 = (String) application.getAttribute("name2");
%>

结果:

结果出人意料:application没有获取到自己作用域的属性,而是获取到了低级作用域的属性

猜想:getArrtibute()方法不论是谁调用,都会从最低作用域开始寻找,直到找到application

顺着猜想验证:

  • application,session与request里面都有name1,用application去获取name1
  • session与request里面都有name2,用session去获取name2
<%
    application.setAttribute("name1","application");
    session.setAttribute("name1","session");
    session.setAttribute("name2","session");
    request.setAttribute("name1","request");
    request.setAttribute("name2","request");
%>
<%
    String name1 = (String) application.getAttribute("name1");
    String name2 = (String) session.getAttribute("name2");
%>

${name1}</br>
${name2}</br>

结果:

结果符合猜想!

我们可以想到,JSP实际上就是一个Servlet,是不是Servlet也是这样呢?

测试:

  • ServletContext(等效于application)与req(即request)里面都有name,用ServletContext和request获取name.
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        System.out.println("Hello Servlet");
        ServletContext servletContext = this.getServletContext();
        servletContext.setAttribute("name","肖云飞");
        req.setAttribute("name","张三");
        String name = (String) servletContext.getAttribute("name");
        String name1 = (String) req.getAttribute("name");
        PrintWriter writer = resp.getWriter();
        writer.println(name);
        writer.println(name1);
    }

结果:

  • ServletContext获取到了ServletContext里面的name
  • request获取到了request里面的name
  • 说明Servlet里面的获取不是从头开始的,而是的的确确会获取到自己的属性

作用域

  • application(web应用)
  • session(会话)
  • request(请求)
  • pageContext(当前jsp页面)

EL表达式,JSP标签,JSTL标签

EL表达式 ${ }

作用:

  • 获取数据
  • 执行运算
  • 获取web开发的常用对象

JSP标签

<jsp:forward page="jsp4.jsp">
    <jsp:param name="name" value="肖云飞"/>
    <jsp:param name="age" value="18"/>
</jsp:forward>

注意:使用JSP指令的include与JSP标签的include有区别

  • JSP指令的include实际上是把两个或多个JSP页面拼接为一个,这也就导致两个JSP页面里面不能定义相同的变量名。
  • JSP标签的include实际上是接收include的JSP标签的响应作为响应的一部分,所以可以在两个JSP页面里面使用相同的变量名
<jsp:include page="jsp3.jsp"></jsp:include>

第一个代码块里面的param就相当于http使用get请求时跟在http后面的信息,所以可以取出来:

<%
    request.setCharacterEncoding("utf-8");
    response.setCharacterEncoding("utf-8");
    String name = request.getParameter("name");
    String age = request.getParameter("age");
    out.println(name);
    out.println(age);
%>

JSTL表达式

JSTL(Java server pages standarded tag library,即JSP标准标签库)是由JCP(Java community Proces)所制定的标准规范,它主要提供给Java Web开发人员一个标准通用的标签库。开发人员可以利用这些标签取代JSP页面上的Java代码,从而提高程序的可读性,降低程序的维护难度。

通过JSP指令引入核心标签库

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

核心标签(重要)

标签 描述
< c:out > 用于在JSP中显示数据,就像<%= … >
< c:set > 用于保存数据
< c:remove > 用于删除数据
< c:catch > 用来处理产生错误的异常状况,并且将错误信息储存起来
< c:if > 与我们在一般程序中用的if一样
< c:choose > 本身只当做< c:when >和< c:otherwise >的父标签
< c:when > < c:choose >的子标签,用来判断条件是否成立
< c:otherwise > < c:choose >的子标签,接在< c:when >标签后,当< c:when >标签判断为false时被执行
< c:import > 检索一个绝对或相对 URL,然后将其内容暴露给页面
< c:forEach > 基础迭代标签,接受多种集合类型
< c:for Tokens > 根据指定的分隔符来分隔内容并迭代输出
< c:param > 用来给包含或重定向的页面传递参数
< c:redirect > 重定向至一个新的URL.
< c:url > 使用可选的查询参数来创造一个URL

< c:if >标签使用:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="jsp8.jsp" method="get">
        用户名:<input type="text" name="username"></br>
        submit<input type="submit">
    </form>

<c:if test="${param.username=='xiao' or param.username=='肖'}" var="isXiao">
    <c:out value="welcom"/>
    <c:out value="${isXiao}"/>
</c:if>
</c:if>
</body>
</html>

结果:

test是判断条件,

  • 实际上test里面的必须是true或false,
  • EL表达式的作用就是将判断的结果转化为false或false。

var是判断返回的结果(类似javaScript的变量声明),即true或false,结果用“isXiao”包装,用的时候EL表达式取出:javaWeb - 图1{变量名 }== false.

set,choose,when,otherwise综合使用

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

    <form action="jsp9.jsp" method="get">
        成绩:<input type="text" name="score"/></br>
        submit:<input type="submit">
    </form>

    <c:set var="score" value="${param.score}"></c:set>

    <c:choose>
        <c:when test="${score>=90}">
            <c:out value="优秀"/>
        </c:when>
        <c:when test="${score>=80 and score <90}">
            <c:out value="良好"/>
        </c:when>
        <c:when test="${score>=70 and score <80}">
            <c:out value="及格"/>
        </c:when>
        <c:otherwise>
            <c:out value="不及格"/>
        </c:otherwise>
    </c:choose>
</body>
</html>

结果:

forEach使用

<%@ 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>forEach</title>
</head>
<body>

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

<c:forEach var="person" items="${list}">
    <c:out value="${person}"></c:out></br>
</c:forEach>
</body>
</html>

结果:

JavaBean

javaBean本质上就是myBatis或是JDBC的实体类,实际上就是数据库向实体类的映射

JavaBean要求:

  • 必须有一个无参构造器
  • 属性必须私有
  • 必须有setter与getter

MVC三层架构

MVC指的是model(模型),view(视图),control(控制)三层架构,是一种经典的软件或网页设计模式

注意:Servlet与JSP都可以写JAVA代码,但是为了维护,约定

  • Servlet专注于处理请求,响应与视图跳转(所以Servlet处理控制层)
  • JSP专注于展示(所以JSP是视图层)

Model

  • 业务处理
  • 数据持久层CRUD(mapper)

View(JSP)

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

Controller(Servlet)

  • 接收用户的请求
  • 交给业务层处理
  • 控制试图跳转

举例:

  • 网页首页(View)有一个登录界面,用户填写登录信息,按下登录,登录信息发送到服务器的,
  • 控制层(Controller)收到登录信息,于是向模型层(Model)发出查询用户信息及权限等指令,
  • 通过JDBC连接数据库并查询返回查询结果,存放在数据持久层(mapper),
  • 然后将需要的结果返回给控制层(Controller),根据结果,
  • 跳转到对应的页面(View)[比如查询结果有这个人就跳转到登录成功的页面,否则提示登录失败]

Filter

Filter意思是过滤器,顾名思义,是完成对请求或响应的过滤作用

测试(用过滤器解决中文乱码问题):

Servlet类

package com.xiao.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 FilterServlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        writer.println("肖云飞");
    }

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

Filter[实现javax.servlet.filter]

package com.xiao.Filter;

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

public class FilterTest1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        servletResponse.setContentType("text/html;charset=utf-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("Filter注销");
    }
}

注意:filterChain.doFilter(servletRequest,servletResponse);必须有,它的作用实际上是放行,意思是过滤完成

配置路径:

Servlet配置两个路径用于对比:

  • /servlet/ft1
  • /ft1

Filter只去过滤/servlet路径下面的Servlet

    <servlet>
        <servlet-name>FilterServlet1</servlet-name>
        <servlet-class>com.xiao.servlet.FilterServlet1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FilterServlet1</servlet-name>
        <url-pattern>/servlet/ft1</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>FilterServlet1</servlet-name>
        <url-pattern>/ft1</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>CharsetFilter</filter-name>
        <filter-class>com.xiao.Filter.FilterTest1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CharsetFilter</filter-name>
        <url-pattern>/servlet/*</url-pattern>
    </filter-mapping>

结果:

  • /ft1
  • /servlet/ft1

在TomCat(web服务器启动时Filter就调用了init()方法,直到与服务器断开连接才调用destroy()方法注销)

Filter实现初步权限管理

为什么要有权限管理?

  • 我们知道,我们向webApp发送某个请求的方式就是添加路径
  • 这就导致了一个问题:
  • 现在有一个登陆界面,有用户权限才可以进入success.jsp这个页面,
  • 但是,我可以直接给出success.jsp的路径从而进入这个页面,
  • 这就好比是没有权限的人可以随意进出银行金库,是很危险的
  • 为了解决上面的问题,我们就要确保如果想要通过路径直接访问一个页面(实际上就是发送一个请求),就必须对这些操作进行过滤
  • 显然,Filter正是可以完成这个任务的最好选择。它可以指定过滤的目录,如果想要访问有过滤器的页面,就用过滤器先判断当前的用户是否有权限。
  • 而能辨别用户的一个可靠手段就是Session或是Cooike(我认为Sesson更安全一些,毕竟Cookie可以保存在客户端,不受服务端的控制,而对用户权限的管理应该是服务端的权利。)

登录界面:

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

<h1>登录界面</h1>
<div>
    <form action="${pageContext.request.contextPath}/login" method="post">
        <p>用户名:<input type="text" name="username"></p>
        <p>submit:<input type="submit"></p>
    </form>
</div>

</body>
</html>

login路径下的Servlet

  • 接收用户登录信息

    • 如果为”肖”或”xiao”,就跳转到success.jsp
    • 否则跳转到failure.jsp
package com.xiao.request;

import com.xiao.constant.Constant;

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;
import java.util.Arrays;

public class Login 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");
        PrintWriter writer = resp.getWriter();
        if (username.equals("xiao")||username.equals("肖")){
            /*登录成功*/
            req.getSession().setAttribute(Constant.USER_NAME,username);
            writer.println("欢迎你"+" : "+username);
            resp.sendRedirect("user/success.jsp");
        }else {
            /*登录失败*/
            resp.sendRedirect("user/failure.jsp");
        }
    }

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

success.jsp

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

<a href="${pageContext.request.contextPath}/logout">注销</a>
</body>
</html>

failure.jap

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登录失败</h1>
<h1>该用户没有权限</h1>
<a href="${pageContext.request.contextPath}/index.jsp">重新登录</a>
</body>
</html>

logout路径下的Servlet

package com.xiao.request;

import com.xiao.constant.Constant;

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;

public class Logout extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        session.removeAttribute(Constant.USER_NAME);
        resp.sendRedirect("index.jsp");
    }

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

存放字段常量名的工具类[目的是为了方便维护]

package com.xiao.constant;

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

Filter(每次对想要访问success.jsp的用户进行排查)

package com.xiao.filter;

import com.xiao.constant.Constant;

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

//判断要访问success.jsp的用户是否有权限
public class PrivilegeFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        //需要Session里面的字段,而servletRequest获取不到
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String userName = (String) request.getSession().getAttribute(Constant.USER_NAME);
        if (userName == null){
            //当前用户没有权限,跳转到failure.jsp
            response.sendRedirect("failure.jsp");
        }
        else if(userName.equals("xiao")||userName.equals("肖")){
            //当前用户有权限,可以放行
            System.out.println("可以访问");
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
    @Override
    public void destroy() {

    }
}
  • 注意:这里一定要用else if,如果用if的话,不论userName是啥会尝试所有的if判断,
  • 一旦userName是null,即便申明userName是String类型,null.equals()直接报错,
  • 所以要使用userName.equals()必须先确保userName不是null.

配置路径:

  <servlet>
    <servlet-name>login</servlet-name>
    <servlet-class>com.xiao.request.Login</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/login</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>logout</servlet-name>
    <servlet-class>com.xiao.request.Logout</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>logout</servlet-name>
    <url-pattern>/logout</url-pattern>
  </servlet-mapping>

  <filter>
    <filter-name>PrivilegeFilter</filter-name>
    <filter-class>com.xiao.filter.PrivilegeFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>PrivilegeFilter</filter-name>
    <url-pattern>/user/success.jsp</url-pattern>
  </filter-mapping>

关于文件上传

常用包:

  • common-io
  • commons-fileupload
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>