主要内容:Tomcat、servlet;JSP、MVC、cookie&session ; EL&JSTL ; filter&listener

文章目录

学习目标

1. Tomcat & Servlet

  • 能够理解WEB动态资源概念
  • 能够安装、启动和关闭Tomcat服务器
  • 掌握运用Tomcat服务器部署WEB项目的三种方式
  • 能够说出Servlet的编写流程
  • 能够说出Servlet的生命周期
  • 能够使用注解开发Servlet
  • 能够理解HTTP协议请求内容的格式
  • 能够处理HTTP请求参数的乱码问题
  • 能够使用Request对象获取HTTP协议请求头的值

2. cookie& session

  • 能够说出会话的概念
  • 能够说出两种会话技术的区别
  • 能够创建、发送、接收、删除cookie
  • 能够说出cookie执行的原理
  • 能够获取session对象、添加、删除、获取session
  • 能够说出session执行的原理
  • 能够说出session的创建、销毁机制

3. EL && JSTL

  • 能够说出el表达式的作用
  • 能够使用el表达式获取javabean的属性
  • 能够使用jstl标签库的if标签
  • 能够使用jstl标签库的foreach标签

5. filter & listener

  • 能够说出过滤器的作用
  • 能够编写过滤器
  • 能够说出过滤器声明周期相关方法
  • 能够根据过滤路径判断指定的过滤器是否起作用
  • 能够理解什么是过滤器链
  • 能够完成filter完成用户登录验证案例
  • 能够理解动态代理对类的方法进行增强
  • 能够了解listener概念

day13 Tomcat&Servlet

1. web服务器软件

1. 服务器

安装了服务器软件的计算机

2. 服务器软件

接收用户的请求,处理请求,做出响应

web服务器软件:接收用户的请求,处理请求,做出响应

  • 在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
  • web容器
3. 常见java相关的web软件
  • webLogic: oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费
  • webSphere: IBM
  • JBOSS: JBOSS公司
  • Tomcat: Apache基金组织,中小型,支持少量javaEE规范servlet/jsp,开源的,免费的
4. javaEE:

java语言在企业级开发中使用的技术规范的综合,一共规定了13项大的规范。

2. Tomcat

目录结构

  • /bin:存放Windows或Linux平台上启动和关闭Tomcat的脚本文件。
  • /conf:存放Tomcat的各种全局配置文件,其中最重要的是server.xml。
  • /lib:存放Tomcat以及所有Web应用都可以访问的JAR文件。
  • /logs:存放Tomcat执行时产生的日志文件。
  • /work:存放JSP编译后产生的Class文件。
  • /webapps:Tomcat的Web应用目录,默认情况下把Web应用放在这个目录下
各类日志

运行日志,主要记录运行过程中的一些信息,尤其是一些异常错误日志信息
访问日志,它记录访问的时间、IP地址、访问的路径等相关信息。

  • catalina.***.log 主要是记录Tomcat启动过程的信息,在这个文件可以看到启动的JVM参数以及操作系统等日志信息。
  • localhost.**.log 主要记录Web应用在初始化过程中遇到的未处理的异常,会被Tomcat捕获而输出这个日志文件
  • localhost_access_log.**.txt 存放访问Tomcat的请求日志,包括IP地址以及请求的路径、时间、请求协议以及状态码等信息

1. 安装卸载相关

  1. 下载
  2. 安装
  3. 卸载
  4. 启动
    • bin/startup.bat,双击运行该文件即可
    • 访问:浏览器输入:http://localhost:8080 http:别人的ip:8080
  5. 关闭
    • 正常关闭 ctrl+c 或者打开shutdown.bat
    • 暴力关闭 直接点close
  6. 配置

启动可能遇到的问题:

  1. 黑窗口一闪而过:
    • 原因:没有正确配置JAVA_HOME环境变量,或者CATALINA_HOME之类的环境变量错误
    • 解决方案:正确配置JAVA_HOME环境变量,或者在CMD命令行启动,会报相应的错
    • 或者编辑 startup.bat,在末尾加一个pause
  2. 启动报错
    • 暴力:找到占用的端口号,并且找到对应的进程,杀死该进程
      • netstat -ano
    • 温柔:修改自己的端口号
      • conf/server.xml
      • <Connector port="8888" protocol="HTTP/1.1"
      • 一般会将tomcat的默认端口号修改为80。80端口号是http协议的默认端口号。

2. 配置 部署项目的方式

直接将项目放到webapps目录下

(使用的较多,方便,针对小项目)

  • /hello:项目的访问路径–> 虚拟目录
  • 简化部署:将项目打包为war包,再将war包放到webapps目录下
    • war包会自动解压缩
配置conf/server.xml文件

(强烈不建议使用)

<Host>标签体中配置 <Context docBase="D:/hello" path="/hehe" />

docBase 项目存放的位置,path虚拟目录

热部署

在conf/Catalina\localhost创建任意名称的xml文件,在文件中编写

<Context docBase="D:\hello">

  • 虚拟目录:xml文件的名称 热部署,想删除,修改扩展名为xxx_bat即可

3. 静态项目和动态项目

1. java动态项目的目录结构

一般,我们以web应用程序的方式部署servlet,而根据servlet规范,web应用程序有一定的目录结构,在这个目录下分别放置servlet的类文件、配置文件和静态资源,servlet容器通过读取配置文件,找到并加载servlet。

-- 项目的根目录
	-- WEB-INF目录
		-- web.xml:web项目的核心配置文件
		-- classes目录: 放置字节码文件的目录
		-- lib目录: 放置依赖的jar包

在这里插入图片描述

2. Tomcat 集成到idea

集成到idea,常见javaEE的项目,部署项目

热部署,如何集成到idea

3. Servlet

1. 概念:

运行在服务器端的小程序

servlet就是一个接口,定义了java类被浏览器访问到(tomcat识别)的规则

一个Java类,要想被浏览器访问到(Tomcat识别到),必须实现servlet接口

我们自己定义一个类,实现Servlet接口,复写方法

在这里插入图片描述

2. 快速入门

  1. 创建javaEE项目

  2. 定义一个类,实现servlet接口

    public class ServletDemo01 implements Servlet

  3. 实现接口中的抽象方法

  4. 配置servlet

        <!--配置servlet-->
        <servlet>
            <servlet-name>demo1</servlet-name>
            <servlet-class>cn.itcast.web.servlet.ServletDemo01</servlet-class>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>demo1</servlet-name>
            <url-pattern>/demo1</url-pattern>
        </servlet-mapping>
    </web-app>
    

3. 执行原理

  1. 当服务器接收到客户端浏览器的请求后,会解析请求url路径,获取访问的servlet的资源路径
  2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容
  3. 如果有,则再找对应的<servlet-class>全类名
  4. Tomcat会将字节码文件加载进内存Class.forName("全类名"),并且创建其对象cls.newInstance()
  5. 调用service方法
    在这里插入图片描述
Servletconfig类

作用是封装servlet的初始化参数,在web.xml给servlet配置的参数,在程序中通过getServletConfig方法获取这些参数。

4. servlet的生命周期

1. 被创建

执行init方法,只执行一次

  • servlet什么时候被创建?
    • 默认情况,第一次被访问时,创建
    • 可以配置指定servlet的创建时机
      • 在servlet标签下配置

指定servlet的创建时机

  1. 第一次被访问时,创建<load-on-startup>值为负数
  2. 在服务器启动时,创建<load-on-startup>值为0或者正数

servlet的init方法,只执行一次,说明一个servlet在内存中只存在一个对象,servlet是单例的

  • 多个用户同时访问时,可能存在线程安全问题
  • 解决:尽量不要在servlet中定义成员变量,即使定义成员变量,也不要对值进行修改

可能在init方法中初始化一些资源,如spring MVC的DispatcherServlet在init方法里创建自己的spring容器。

2. 提供服务

执行service方法,执行多次

3. 被销毁

执行destroy方法,只执行一次

只有服务器正常关闭时,才会执行,一般用于释放资源

5. servlet3.0

好处:支持注解配置,可以不需要web.xml

步骤:

  1. 创建JavaEE项目,选择Servlet版本3.0以上,可以不创建web.xml
  2. 定义一个类,实现servlet接口
  3. 复写方法
  4. 在类上使用@WebService注解,进行配置 @WebServlet("资源路径")

6. IDEA与Tomcat的相关配置

  1. IDEA会为每一个Tomcat部署的项目单独建立一份配置文件
    • 查看控制台的log:“C:\Users\Lenovo.IntelliJIdea2018.3\system\tomcat_javaweb2”
  2. 工作空间项目 和 Tomcat部署的web项目
    • Tomcat真正访问的是“Tomcat部署的web项目”,而“Tomcat部署的web项目”对应着“工作空间项目”的web目录下的所有资源
    • WEB-INF目录下的资源不能被浏览器直接访问

7. servlet相关配置

  1. urlpattern: servlet访问路径
    1. 一个servlet可以定义多个访问路径:@WebServlet({"/d4","/dd4","/ddd4"})
    2. 路径定义规则:
      1. /xxx: 路径匹配 “/demo1”
      2. /xxx/xxx : 多层路径,目录结构 “/user/demo1”
      3. *.do: 扩展名匹配 *.action 为什么写.do呢?
    3. 优先级: 绝对匹配 /xxx > /* > *.do

[外链图片转存失败(img-DbFgY8Kl-1563071065997)(img\url_pattern.png)]

servlet的体系结构

genericServlet类:将servlet接口中其他方法做了默认空实现,只将service()方法作为抽象

将来定义servlet类时,可以继承GenericServlet,实现service()方法即可

HttpServlet: 对http协议的封装,简化操作

  1. 定义继承

Ctrl+H 查看继承

GenericServlet类:
 public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();//调用init()方法,将init()方法里的方法体内容输出
    }

 public void init() throws ServletException {
  }
 Servlet接口:
 public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

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

    String getServletInfo();

    void destroy();
}
public abstract class HttpServlet extends GenericServlet  
为什么GenericServlet重写了Servlet的init方法后,自己又写了个空参的init()方法
ServletContext

Servlet规范里定义了ServletContext这个接口来对应一个Web应用。Web应用部署好后,Servlet容器在启动时会加载Web应用,并为每个Web应用创建唯一的ServletContext对象。你可以把ServletContext看成是一个全局对象,一个Web应用可能有多个Servlet,这些Servlet可以通过全局的ServletContext来共享数据,这些数据包括Web应用的初始化参数、Web应用目录下的文件资源等。由于ServletContext持有所有Servlet实例,你还可以通过它来实现Servlet请求的转发

day14-18 HTTP

1. 概念

Hyper Text Transfer Protocol超文本传输协议

传输协议:定义了客户端和服务器端通信时,发送数据的格式

特点:

  1. 基于TCP/IP的高级协议
  2. 默认端口:80
  3. 基于请求/响应模型的:一次请求对应一次响应
  4. 无状态的:每次请求之间相互独立,不能交互数据

历史版本

  • 1.0:每一次请求都会响应都会建立新的连接
  • 1.1:复用连接

发展史:

  • 0.9版本:只能传输HTML,只支持get命令,基于TCP
  • 1.0版本:1996年,不仅可以传输HTML的文本,
  • 1.1版本:1997年,增加持久连接
  • 2.0版本:做了多工的处理

在java开发中不管用线程,数据库,网路请求,建立的连接,都是宝贵的,不要随意乱用和浪费

2. request

1. request消息数据格式

[外链图片转存失败(img-IHOzOqME-1563071065999)(img\图一 HTTP协议的请求部分.bmp)]

1. 请求行

请求方式 请求url 请求协议/版本

GET /login.html HTTP/1.1

2. 请求头

客户端浏览器告诉服务器一些信息

请求头名称:请求头值

常见的请求头:

  1. User-Agent: 浏览器告诉服务器,我访问你使用的浏览器版本信息
    • 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
  2. Referer: http://localhost/login.html
    • 告诉服务器,我(当前请求)从哪里来
    • 作用:1. 防盗链 2. 统计工作
3. 请求空行

空行,分隔post请求头和请求体

4. 请求体(正文)

封装POST请求消息的请求参数的

字符串格式

		POST /login.html	HTTP/1.1
		Host: localhost
		User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
		Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
		Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
		Accept-Encoding: gzip, deflate
		Referer: http://localhost/login.html
		Connection: keep-alive
		Upgrade-Insecure-Requests: 1

2. request对象

1. request对象和response对象的原理

  1. request和response对象是由服务器创建的,我们来使用它们
  2. request对象是来获取请求消息,response对象是来设置响应消息
  1. Tomcat服务器会根据请求url中的资源路径,创建对应的servletDemo1的对象
  2. Tomcat服务器,会创建request和response对象,request对象中封装请求消息数据
  3. Tomcat将request和response对象传递给service方法,并且调用service方法
  4. 程序员,可以通过request对象获取请求消息数据,通过response对象设置响应消息数据
  5. 服务器再给浏览器做出响应之前,会从response对象中拿程序员设置的响应消息数据

2. request对象的继承体系结构

ServletRequest  --接口
	|
HttpServletRequest  -- 接口
	|
org.apache.catalina.connector.RequestFacade类实现


3. request功能

1. 获取请求行消息数据

获取请求行数据 GET /day14/demo1?name=zhangsan HTTP/1.1

  1. 获取请求方式 GET String getMethod()
  2. 获取虚拟目录 /day14 String getContextPath()
  3. 获取Servlet路径 /demo1 String getServletPath()
  4. 获取get方式请求参数 name=zhangsan String getQueryString()
  5. 获取请求URI: /day14/demo1
    1. String getRequestURI() /day14/demo
    2. String getRequestURL() http://localhost/day14/demo
  6. 获取协议及版本:HTTP/1.1 String getProtocol
  7. 获取客户机的IP地址: String getRemoteAddr()
2. 获取请求头数据

方法:

  • String getHeader(String name) 通过请求头的名称获取请求头的值
  • Enumeration<String> getHeaderNames() 获取所有的请求头名称
3. 获取请求体数据

请求体:只有post请求方式,才有请求体,在请求体中封装post请求的请求参数

步骤:

  1. 获取流对象
    • BufferedReader getReader() : 获取字符输入流,只能操作字符数据
    • ServletInputStream getInputStream(): 获取字节输入流,可以操作所有数据,在文件上传知识点讲解
  2. 再从流对象中拿数据
4. 其他功能
4.1获取请求参数通用方式
  1. String getParameter(String name) 根据参数名称获取参数值
  2. String[] getParameterValues(String name) 根据参数名称获取参数值的数组,常见复选框
  3. Enumeration<String> getParameterNames() 获取所有请求的参数名称
  4. Map<String, String[]> getParameterMap() 获取所有参数的map集合
  • 中文乱码问题:
    • get方式:Tomcat 8已经将get方式乱码问题解决了
    • post方式:会乱码
    • 解决:在获取参数之前,设置request的编码方式request.setCharacterEncoding("utf-8");
4.2 请求转发

一种在服务器内部的资源跳转方式

请求次数:代表的是浏览器到服务器端的次数,不包含服务器内部跳转

  1. 步骤:
    1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
    2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request,ServletResponse response)
  2. 特点:
    1. 浏览器地址栏路径不发生变化
    2. 只能转发到当前服务器内部资源中
    3. 转发是一次请求
4.3 共享数据
  • 域对象:一个有作用范围的对象,可以在范围内共享数据
  • request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
  • 方法:
    • void setAttribute(String name,Object obj) 存储数据
    • Object getAttribute(String name) 通过键获取值
    • void removeAttribute(String name) 通过键移除键值对
4.4 获取ServletContext
  • ServletContext getServletContext()

3. request案例

1. 用户登录案例需求:

1.编写login.html登录页面
	username & password 两个输入框
2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
3.使用JdbcTemplate技术封装JDBC
4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

2. 分析

在这里插入图片描述

3. 开发步骤

  1. 创建项目,导入HTML项目,配置文件,jar包

  2. 创建数据库环境

    CREATE DATABASE day14;
    
    USE day14;
    
    CREATE TABLE USER(
    
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	username VARCHAR(32) UNIQUE NOT NULL,
    	PASSWORD VARCHAR(32) NOT NULL
    );
    
  3. 创建包cn.itcast.domain创建类User

  4. 创建包cn.itcast.dao 创建类UserDao 操作数据库,提供login的方法

  5. 创建工具吧con.itcast.util创建JDBCUtils工具类,使用Druid连接池,获取连接池对象和连接对象

    public class JDBCUtils {
        private static DataSource ds;
    
        static {
            try {
                // 加载配置文件
                Properties pro = new Properties();
                InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
                pro.load(in);
                // 初始化连接池对象
                ds = DruidDataSourceFactory.createDataSource(pro);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        // 获取连接池对象
        public static DataSource getDataSource(){
            return ds;
        }
    
        // 获取连接connection对象
        public static Connection getConnection() throws SQLException {
            return ds.getConnection();
        }
    }
    
  6. 完善UserDao的方法

    public class UserDao {
    
        // 声明JDBCTemplate对象共用
        private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    
   /**
    *  登录方法
    * @param loginUser 只有用户名和密码
    * @return user包含用户全部数据,没有,返回null值
    */
   public User login(User loginUser){
       try {
           // 1. 编写SQL
           String sql = "select * from user where username = ? and password = ?";
           // 2. 调用query方法
           User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginUser.getUsername(), loginUser.getPassword());
           return user;
       } catch (DataAccessException e) {
           e.printStackTrace();//记录日志
           return null;
       }
   }

}

7. 测试方法,看Dao代码运行是否正常,创建包`cn.itcast.test`,测试类`UserDaoTest`

   ```java
   public class UserDaoTest {
       @Test
       public void testLogin(){
           // 创建登录用户对象
           User loginUser = new User();
           loginUser.setUsername("superbaby");
           loginUser.setPassword("123111");

           // 查看数据库是否有该对象
           UserDao dao = new UserDao();
           User user = dao.login(loginUser);
           System.out.println(user);

       }
   }

发现问题,如果用户名密码不一致,会出现异常,不应该抛出异常,应该返回null,用try/catch包裹(ctrl+alt+t)

  1. 编写cn.itcast.web.servlet.LoginServlet

    @WebServlet("/loginServlet")
    public class LoginServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 设置编码
            request.setCharacterEncoding("utf-8");
            // 获取请求参数
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            // 封装user对象
            User loginUser = new User();
            loginUser.setUsername(username);
            loginUser.setPassword(password);
    
            // 调用Dao的login方法
            UserDao dao = new UserDao();
            User user = dao.login(loginUser);
    
            // 判断user
            if(user == null){
                //登录失败
                //转发
                request.getRequestDispatcher("/failServlet").forward(request,response);
            }else{
                // 登录成功
                // 存储数据
                request.setAttribute("user",user);
                request.getRequestDispatcher("/successServlet").forward(request,response);
            }
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
  2. 编写成功和失败的跳转页面

    @WebServlet("/failServlet")
    public class FailServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            // 给页面写一句话
            // 设置编码
            response.setContentType("text/html;charset=utf-8");
            // 输出
            response.getWriter().write("登录失败,用户名或密码错误");
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    

    @WebServlet("/successServlet")
    public class SuccessServlet extends HttpServlet {

   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // 获取request域中共享的user对象
       User user =(User) request.getAttribute("user");
       if (user!=null) {
           // 给页面写一句话
           // 设置编码
           response.setContentType("text/html;charset=utf-8");
           // 输出
           response.getWriter().write("登录成功,"+user.getUsername()+"欢迎您!");
       }
   }

   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       this.doPost(request,response);
   }

}

### 4. 错误日志

问题:登录后跳转找不到网页,转不到loginServlet的页面

解决方法:部署的问题,在idea该项目的部署中,将虚拟目录改为`/day14_test`,之前误改为`/day14`

### 5. 优化

使用Apache的BeanUtils包封装loginUser对象,修改部分如下

​```java
/*// 获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // 封装user对象
        User loginUser = new User();
        loginUser.setUsername(username);
        loginUser.setPassword(password);*/

        // 2. 获取所有请求参数
        Map<String, String[]> map = request.getParameterMap();
        // 3. 创建User对象
        User loginUser = new User();
        // 3.2 使用BeanUtils封装
        try {
            BeanUtils.populate(loginUser,map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

6. BeanUtils工具类

简化数据封装,用于封装JavaBean,原理:内省机制

1. Javabean

标准的java类

要求:

  1. 类必须被public修饰
  2. 必须提供空参构造器
  3. 成员变量必须使用private修饰
  4. 提供公共setter和getter方法

功能:封装数据

2. 概念

成员变量

属性:setter和gettter方法截取后的产物

例如:getUsername() --> Username --> username

3. 方法
  1. setProperty(“key”) 属性名 != 成员变量
  2. getProperty()
  3. populate(Object obj, Map map) 将map集合的键值对信息,封装到对应的javabean对象。

4. response

响应消息:

服务器端发送给客户端的数据

数据格式:
  1. 响应行
    1. 组成:协议/版本 响应状态码 状态码描述
    2. 响应状态码:见下个标题
  2. 响应头
    1. 格式: 头名称:值
    2. 常见的响应头:
      1. Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
      2. Content-disposition: 服务器告诉客户端以什么格式打开响应体数据
        • 值1: in-line; 默认值,在当前页面内打开
        • 值2: attachment;filename=xxx 以附件形式打开响应体,文件下载
  3. 响应空行
  4. 响应体: 传输的数据

响应字符串格式

headers:

HTTP/1.1 200 
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Date: Sat, 09 Mar 2019 04:35:22 GMT

response:
<html>
  <head>
    <title>response</title>
  </head>
  <body>
  hello, response
  </body>
</html>
响应状态码

服务器告诉客户端浏览器本次请求和响应的一个状态

状态码都是3位数字

分类:

  1. 1xx: 服务器接收客户端消息,但没有接收完成,等待一段时间后,发送1xx状态码

  2. 2xx:成功 代表:200

  3. 3xx: 重定向 代表:302(重定向),304(访问缓存)

  4. 4xx: 客户端错误

    代表:

    • 404(请求路径没有对应的资源)
    • 405(请求方式没有对应的doXxx方法)
  5. 5xx: 服务器端错误 代表:500(服务器内部出现异常)

5. response对象

1. 功能

设置响应消息

1. 设置响应行
  1. 格式:HTTP/1.1 200 OK
  2. 设置状态码:setStatus(int sc)
2. 设置响应头

setHeader(String name, String value)

3. 设置响应体

使用步骤:

  1. 获取输出流
    • 字符输出流:PrintWriter getWriter()
    • 字节输出流:ServletOutputStream getOutputStream()
  2. 使用输出流,将数据输出到客户端浏览器

2. 案例

1. 重定向

资源的跳转方式

代码实现

	   // 访问responseDemo01会自动跳转到/responseDemo02资源
       /* // 1. 设置状态码为302
        response.setStatus(302);
        // 2. 设置响应头location
        response.setHeader("location","/day15_response/responseDemo02");*/

        // 简单的重定向方法
        response.sendRedirect("/day15_response/responseDemo02");

重定向redirect的特点:

  1. 地址栏发生变化
  2. 重定向可以访问其他站点(服务器)的资源
  3. 重定向是两次请求

转发forward的特点:

  1. 浏览器地址栏路径不发生变化
  2. 只能转发到当前服务器内部资源中
  3. 转发是一次请求

路径写法

  • 相对路径:通过相对路径不可以确定唯一资源

    • 如: ./index.html
    • 不以/开头,以.开头路径
    • 规则:找到当前资源和目标资源之间的相对位置关系
      • ./: 当前目录
      • ../:后退一级目录
  • 绝对路径:通过绝对路径可以确定唯一资源

    • 如:http://localhost:8080/day15_response/responseDemo02

    • /开头的路径

    • 规则:判断定义的路径是给谁用的,判断请求将来从哪里发出

      • 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
        • 建议虚拟目录动态获取:request.getContextPath()
        • <a>,<form>重定向…
      • 给服务器使用:不需要加虚拟目录
        • 转发路径

2. 服务器输出字符数据到浏览器

步骤:

  1. 获取字符输出流
  2. 输出数据

注意:

  • 乱码问题:

    1. PrintWriter pw = response.getWriter();获取的流的默认编码格式为ISO-8859-1
    2. 设置该流的默认编码
    3. 告诉浏览器响应体使用的编码
    // 简单的形式,设置编码,在获取流之前设置
     response.setContentType("text/html;charset=utf-8");
    
3. 服务器输出字节数据到浏览器

步骤:

  1. 获取字节输出流

  2. 输出数据

     response.setContentType("text/html;charset=utf-8");
     // 1. 获取字节输出流
     ServletOutputStream sos = response.getOutputStream();
     sos.write("你好".getBytes("utf-8"));
    
4. 验证码

本质:图片

目的:防止恶意表单注册

@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width = 100;
        int height = 50;
        // 1. 创建一个对象,在内存中图片(验证码图片对象)
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        // 2. 美化图片
        // 2.1 填充背景色
        Graphics g = image.getGraphics();// 画笔对象
        g.setColor(Color.pink);
        g.fillRect(0,0,width,height);

        // 2.2 画边框
        g.setColor(Color.BLUE);
        g.drawRect(0,0,width-1,height-1);

        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        // 生成随机角标
        Random random = new Random();
        for (int i = 1; i <= 4; i++) {
            int index = random.nextInt(str.length());
            // 获取字符
            char ch = str.charAt(index);
            // 2.3 写验证码
            g.drawString(ch+"",width/5*i,height/2);
        }

        // 2.4 画干扰线
        g.setColor(Color.gray);
        // 随机生成坐标点
        for (int i = 0; i < 10; i++) {
            int x1 = random.nextInt(width);
            int x2 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int y2 = random.nextInt(height);
            g.drawLine(x1,y1,x2,y2);
        }

        // 3. 将图片输出到页面展示
        ImageIO.write(image, "jpg", response.getOutputStream());

    }


        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
    }
}

6. ServletContext对象

1. 概念

代表整个web应用,可以和程序的容器(服务器)来通信。

2. 功能

  1. 获取MIME类型
  2. 域对象:共享数据
  3. 获取文件的真实(服务器)路径
MIME类型

在互联网通信过程中定义的一种文件数据类型

格式: 大类型/小类型 text/html image/jpeg

域对象

共享数据

  1. setAttribute(String name,Object value)
  2. getAttribute(String name)
  3. removeAttribute(String name)

ServletContext对象范围:所有用户所有请求的数据

获取文件的真实(服务器)路径

方法:String getRealPath(String path)

String realPath = servletContext.getRealPath("/b.txt");// Web目录下的文件
String c = servletContext.getRealPath("/WEB-INF/c.txt");// WEB-INF目录下的文件
String a = servletContext.getRealPath("/WEB-INF/classes/c.txt");//src目录下的文件

7. response案例

需求:

文件下载

  1. 页面显示超链接
  2. 点击超链接后弹出下载提示框
  3. 完成图片文件下载

步骤

  1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
  2. 定义Servlet
    1. 获取文件名称
    2. 使用字节输入流加载文件进内存(需要获取文件真实路径)
    3. 指定response的响应头:content-disposition:attachment;filename=xxx
    4. 将数据写出到response输出流

问题

问题1. :为什么直接拖视频文件到img目录下,没有真正复制到out\artifacts\day15_response_war_exploded\img

问题2:中文文件问题:

解决思路:

  1. 获取客户端使用的浏览器版本信息
  2. 根据不同的版本信息,设置filename的编码方式不同
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置request的编码方式
        request.setCharacterEncoding("utf-8");

        // 1. 获取请求参数
        String filename = request.getParameter("filename");
        // 2. 使用字节输入流加载文件进内存
        // 2.1 找到文件的服务器路径
        String realPath = this.getServletContext().getRealPath("/img/" + filename);
        // 2.2 用字节流关联
        FileInputStream fis = new FileInputStream(realPath);

        // 3. 设置response响应头
        // 3.1 设置response响应头的类型 content-type
        String mimeType = this.getServletContext().getMimeType(filename);
        // 3.2 设置响应头的打开方式 content-disposition

        // 解决中文文件名问题
        // 1. 获取user-agent请求头
        String agent = request.getHeader("user-agent");
        // 2. 使用工具类方法编码文件名即可
        String fileName = DownLoadUtils.getFileName(agent, filename);

        response.setHeader("content-disposition","attachment;filename="+fileName);
        // 4. 将输入流的数据写到输出流
        ServletOutputStream os = response.getOutputStream();
        byte[] buff = new byte[1024*8];
        int len;
        while((len=fis.read(buff))!=-1){
            os.write(buff,0,len);
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

day16 cookie&session

1. 会话技术

1. 概念

一次会话中包含多次请求和响应。

一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止。

2. 功能

在一次会话的范围内的多次请求间,共享数据

3. 方式

  1. 客户端会话技术:cookie
  2. 服务器端会话技术:session

2. cookie

1. 概念

客户端会话技术,将数据保存到客户端

2. 快速入门

使用步骤:

  1. 创建cookie对象,绑定数据
    • new Cookie(String name,String value)
  2. 发送Cookie对象
    • response.addCookie(Cookie cookie)
  3. 获取Cookie,拿到数据
    • Cookie[] request.getCookies()

3. 实现原理

基于响应头set-cookie和请求头cookie实现

cookie应用场景

访问京东商城,购物,看上了一个商品加入购物车(并未登录的情况下),可以将商品信息通过cookie的方式存储到浏览器端,在结算的时候,查询所有的cookie,列出我们的商品

4. cookie的细节

4.1 一次可不可以发送多个cookie?
  • 可以
  • 可以创建多个cookie对象,使用response调用多次addCookie方法发送cookie即可
4.2 cookie在浏览器中保存多长时间?

默认情况下,当浏览器关闭后,cookie数据被销毁

持久化存储:setMaxAge(int seconds)

  1. 正数:将cookie数据写到硬盘的文件中,持久化存储。cookie存活时间
  2. 负数:默认值
  3. 零:删除cookie数据
4.3 cookie能不能存中文?
  • 在tomcat8之前,cookie中不能直接存储中文数据
    • 需要将中文数据转码,一般采用URL编码(%E3)
  • 在tomcat8之后,cookie支持中文数据
4.4 cookie的共享问题?

假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能否共享?

  • 默认情况下cookie不能共享
  • setPath(String path):设置cookie的获取范围,默认情况下设置当前的虚拟目录
    • 如果想共享,则可以将path设置为"/"

不同的tomcat服务器间cookie共享问题?

  • setDomain(String path): 如果设置一级域名相同,那么多个服务器之间cookie可以共享
  • setDomain(".baidu.com"),那么tieba.baidu.comnews.baidu.com中cookie可以共享

扩展:

  • 如果我的项目,,没有给cookie设置path,但是我的虚拟目录也是"/",能否共享cookie
  • 能,不调用setPath("/")如果虚拟目录本身就是/,依然可以共享所有项目,但是如果调用getPath(),获取的路径为null

5. cookie的特点

  1. cookie存储数据在客户端浏览器
  2. 浏览器对于单个cookie的大小有限制(4kb)以及对于同一个域名下的总cookie数也有限制(20个)

作用:

  1. cookie一般用于存储少量的不太敏感的数据
  2. 在不登录的情况下,完成服务器对客户端的身份识别

day16 JSP

1. 入门学习

1. 概念

Java Server Pages :java服务器端页面

可以理解为:一个特殊的页面,其中既可以指定定义HTML标签,又可以定义java代码

用于简化书写

目前的传统项目中JSP仅仅作为页面展示+servlet+数据共享(request,ServletContext,page,session)

2. 原理

本质上就是一个Servlet,第一次访问该JSP的时候,翻译成一个.java .class 继承HttpJSPBase ---- HttpServlet

3. JSP的脚本

JSP定义java代码的方式

  1. <% 代码 %>: 定义的java代码,在service方法中,service方法中可以定义什么,该脚本中就可以定义什么
  2. <%! 代码 %>: 定义的java代码,在JSP转换后的java类的成员位置,一般不用
  3. <%= 代码 %>: 定义的java代码,会输出到页面上,输出语句中可以定义什么,该脚本就可以定义什么

4. 内置对象

1. 概念

在jsp页面中不需要获取和创建,可以直接使用的对象

jsp一共有9个内置对象

今天学习3个:

  • request
  • response
  • out:字符输出流对象。可以将数据输出到页面上。和response.getWrter()类似

在tomcat服务器真正给客户端做出响应之前,会先找response缓冲区数据,
response.getWriter()数据输出永远在out.write()之前,尽量不要用response.getWrter()输出!

入门案例

修改cookie案例为jsp

<%@ page import="java.net.URLDecoder" %>
<%@ page import="static cn.itcast.cookie.CookieTest.getTime" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>itcast</title>
</head>
<body>
<%
    // 设置request的编码方式
    request.setCharacterEncoding("utf-8");

    // 1. 获取cookie
    Cookie[] cookies = request.getCookies();
    boolean flag = false;
    if(cookies!=null && cookies.length>0){
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            if("lastTime".equals(name)){
                // 不是第一次,欢迎回来,您上次访问时间为:显示时间字符串
                flag = true;
                String value = cookie.getValue();
                // 取出来后要URL解码
                String value1 = URLDecoder.decode(value, "utf-8");
%>
<h1>欢迎回来,您上次访问时间为:<%= value1 %></h1>
<%
                // 修改cookie的value
                value = getTime();
                cookie.setValue(value);
                cookie.setMaxAge(60*60*24*30);
                response.addCookie(cookie);
                break;
            }
        }
    }
    if(cookies==null || cookies.length==0 || flag==false){
        Cookie cookie = new Cookie("lastTime",getTime());
        cookie.setMaxAge(60*60*24*30);
        response.addCookie(cookie);
        // 首次登陆
%>

 <h1>您好,欢迎您首次访问。</h1>
<%

    }

%>
</body>
</html>

2. session

1. 概念

服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中,HttpSession

2. 快速入门

1. 获取HttpSession对象

HttpSession session = request.getSession();

2. 使用Httpsession对象

Object getAttribute(String name)

void setAttribute(String name, Object value)

void removeAttribute(String name)

3. 原理

session的实现依赖于cookie

在这里插入图片描述

4. 细节

1. 当客户端关闭后,服务器不关闭,两次获取的session是否为同一个?
  • 默认情况下,不是
  • 如果需要相同,可以创建cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存
HttpSession session = request.getSession();
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
2. 客户端不关闭,服务器关闭后,两次获取到的session是同一个吗?
  • 不是同一个,但是要确保数据不丢失
  • session的钝化
    • 在服务器正常关闭之前,将session对象序列化到硬盘
  • session的活化
    • 在服务器启动后,将session文件转化为内存中的session对象即可
3. session什么时候被销毁?
  1. 服务器关闭

  2. session对象调用invalidate()

  3. session默认失效时间30分钟

    选择性配置修改

    <session-config>
    	<session-timeout>30</session-timeout>
    </session-config>
    

5. session 的特点

  1. session用于存储一次会话的多次请求的数据,存在服务器端
  2. session可以存储任意类型,任意大小的数据
session和cookie的区别
  1. session存储数据在服务器端,cookie在客户端
  2. session没有数据大小限制,cookie有
  3. session数据安全,cookie相对不安全

3. JSP深入

1. 指令

作用

用于配置JSP页面,导入资源文件

格式

<%@ 指令名称 属性名1=属性值1 属性名2=属性值2 ... %>

分类
  1. page:配置JSP页面的

    • ContentType:等同于response.setContentType()
      1. 设置响应体的MIME类型以及字符集
      2. 设置当前jsp页面的编码(只能是高级的IDE才能生效,低级的需要设置pageEncoding属性设置当前页面的字符集)
    • import:导包
    • errorPage :当前页面发生异常后,会自动跳转到指定的错误页面
    • isErrorPage:标识当前页面是否是错误页面
      • true:是,可以使用内置对象exception
      • false:否,默认值,不可以使用内置对象exception
  2. include:页面包含的,导入页面的资源文件

    • <%@include file="top.jsp"%>
  3. taglib:导入资源

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

2. 注释

1. HTML注释

<!-- --> 只能注释HTML代码片段

2. jsp注释:推荐使用

<%-- --%>:可以注释所有

3. 内置对象

在jsp页面中不需要创建,直接使用的对象

一共有9个

变量名 真实类型 作用
pageContext PageContext 当前页面共享数据,还可以获取其他八个内置对象
request HttpServletRequest 一次请求访问的多个资源(转发)
session HttpSession 一次会话的多个请求间
application ServletContext 所有用户间共享数据
response HttpServletResponse 响应对象
page Object 当前页面(Servlet)的对象 this
out JspWriter 输出对象,数据输出到页面上
config ServletConfig servlet的配置对象
exception Throwable 异常对象

day17 MVC

1. MVC

1. jsp演变历史

  1. 早期只有servlet,只能使用response输出标签数据,非常麻烦
  2. 再后来有jsp,简化了servlet的开发,如果过度使用jsp,在jsp中既写大量的java代码,又写HTML标签,造成难以维护,难以分工协作
  3. 再后来,java的web开发,借鉴MVC开发模式,使得程序的设计更加合理

2. MVC

1. 概念
  1. M: Model 模型。 javabean

    • 完成具体的业务操作,如:查询数据库,封装对象
  2. V:View 视图 JSP

    • 展示数据

      数据存储在哪里? 数据库,内存(四大域对象),本地

  3. C: Controller 控制器 servlet

    • 获取用户的输入
    • 调用模型
    • 将数据交给视图进行展示

在这里插入图片描述

2. 优缺点

优点:

  1. 耦合性低,方便维护,利于分工协作
  2. 重用性高

缺点:使得项目结构变得复杂,对开发人员要求高

2. EL表达式

1. 概念

Expression Language 表达式语言

2. 作用

替换和简化jsp页面中java代码的编写

3. 语法

${表达式}

4. 注意

jsp默认支持el表达式的,如果要忽略el表达式

  1. 设置jsp中page指令中:isELIgnore="true" 忽略当前jsp页面的所有的el表达式
  2. \${表达式} :忽略当前这个el表达式

5. 作用

  1. 运算
  2. 获取值
运算符
  1. 算数运算符:+ - * / (div) %(mod)
  2. 比较运算符:> < >= <= == !=
  3. 逻辑运算符: &&(and) ||(or) !(not)
  4. 空运算符: empty
    • 功能: 用于判断字符串、集合、数组对象是否为null并且长度是否为0
    • ${empty list}
获取值
  1. el表达式只能从域对象中获取值

  2. 语法:

    1. ${域名称.键名}:从指定域中获取指定键的值
      • 域名称:
        1. pageScope -->pageContext
        2. requestScope --> request
        3. sessionScope --> session
        4. applicationScope–>application(servletContext)
      • 例子:在request域中存储了name=张三
      • 获取:${requestScope.name}
    2. ${键名}:表示依次从最小的域中查找是否有该键对应的值,直到找到为止
    3. 获取对象、list集合、map集合的值
      1. 对象:${域名称.键名.属性名}
        • 本质上会去调用对象的getter方法
      2. List集合:${域名称.键名[索引]}
      3. map集合
        • ${域名称.键名.属性名}
        • ${域名称.键名["key名称"]}
    <h1>获取list集合中的map集合中的user对象中的birthday</h1>
    ${requestScope.list[0].user.birStr}
    

  3. 隐式对象

    • el表达式中有11个隐式对象,知道4个域对象和pageContext即可
    • pageContext:获取jsp其他八个内置对象
      • ${pageContext.request.contextPath}:动态获取虚拟目录

3. JSTL

1. 概念

JavaServer pages Tag Library JSP标准标签库

是由Apache组织提供的开源的免费的jsp标签 <标签>

2. 作用

用于简化和替换jsp页面上的java代码

3. 使用步骤

  1. 导入JSTL相关jar包
  2. 引入标签库: taglib指令 <%@ taglib %>
  3. 使用标签

4. 常用标签

1. if

相当于java代码的if语句

  1. 属性:
    • test:必须属性,接收boolean表达式
      • 如果表达式为true,则显示if标签体内容,如果为false,不显示标签体内容
      • 一般情况,test属性值会结合el表达式一起使用
  2. 注意:
    • c:if标签没有else情况,想要else情况,可以再定义一个c:if标签
2. choose

相当于java代码的switch语句

  1. 使用choose标签声明 相当于switch语句
  2. 使用when标签做判断 相当于case
  3. 使用otherwise标签做其他情况的声明 相当于default
3. foreach

相当于java代码的for语句

  1. 完成重复操作 for(int i=0;i<10;i++)

​ 属性:begin:开始值;end:结束值;var:临时变量;step:步长;

​ varStatus:循环状态对象;index:容器中元素的索引,从0开始;count:循环次数,从1开始

  1. 遍历容器 List<User> list; for(User user: list){}

    属性:item:容器对象;var:容器中元素的临时变量

4. 三层架构

软件设计架构

  1. 界面层(表示层):用户看得见的界面。用户可以通过界面上的组件和服务器进行交互
  2. 业务逻辑层:处理业务逻辑
  3. 数据访问层:操作数据存储文件

案例:用户信息列表展示

  1. 需求:用户信息的增删改查操作

  2. 设计

    1. 技术选型:Servlet+JSP+MySQL+JDBCTemplate+Druid+BeanUtils+tomcat

    2. 数据库设计:

      create database day17;  -- 创建数据库
      use day17;  -- 使用数据库
      create table user(  -- 创建表
      	id int primary key auto_increment,
      	name varchar(20) not null,
      	gender varchar(5),
      	age int,
      	address varchar(32),
      	qq varchar(20),
      	email varchar(50)
      );
      
    3. 开发:

      1. 环境搭建
        1. 创建数据库环境
        2. 创建项目,导入需要的jar包
      2. 编码
    4. 测试

    5. 部署运维

添加用户:

同登录类似?输入参数,获取参数,判断是否存在同名的,是,原页面返回用户名已存在,否,添加进数据库,添加成功后,进入登录页面,显示欢迎你,xxx

day18 扩展机制

1. 扩展机制之Filter

1. 概念

生活中的过滤器:净水器,空气净化器等

web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能

过滤器的作用:

一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤…

2. 快速入门

  1. 步骤:

    1. 定义一个类,实现接口Filter
    2. 复写方法
    3. 配置拦截路径
      1. web.xml
      2. 注解
  2. 代码:

    @WebFilter("/*") // 访问所有资源之前,都会执行该过滤器
    public class FilterDemo01 implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("filterDemo01被执行了。。。");
    
            // 放行
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
        @Override
        public void destroy() {
    
        }
    }
    

3. 过滤器细节:

1. web.xml配置
    <filter-name>demo1</filter-name>
    <filter-class>cn.itcast.web.filter.FilterDemo01</filter-class>
</filter>
<filter-mapping>
    <filter-name>demo1</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>-->
2. 过滤器执行流程
  1. 执行过滤器
  2. 执行放行后的资源
  3. 回来执行过滤器放行代码下的代码
3. 过滤器生命周期方法
  1. init: 在服务器启动后,会创建Filter对象,然后调用init方法,只执行一次
  2. doFilter:每一次请求被拦截资源时,会执行,执行多次
  3. destroy:在服务器关闭后,filter对象被销毁,如果服务器是正常关闭,则会执行destroy方法。用于释放资源。
4. 过滤器配置详解
  • 拦截路径配置
    1. 具体的资源路径:/index.jsp 只有访问index.jsp资源时,过滤器才会被执行
    2. 目录拦截:/user/* 访问/user下的所有资源时,过滤器都会被执行
    3. 后缀名拦截:*.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
    4. 拦截所有资源:/* 访问所有资源时,过滤器都会被执行
  • 拦截方式配置 资源被访问的方式
    • 注解配置:
      • 设置dispatcherTypes属性
        1. REQUEST 默认值 浏览器直接请求
        2. FORWORD 转发访问资源
        3. INCLUDE 包含访问资源(了解)
        4. ERROR 错误跳转资源(了解)
        5. ASYNC 异步访问资源(了解)
    • web.xml配置
      • 设置<dispatcher></dispatcher>标签即可
5. 过滤器链FilterChain(配置多个过滤器)

执行顺序:如果有两个过滤器,过滤器1 和过滤器2

  1. 过滤器1
  2. 过滤器2
  3. 资源执行
  4. 过滤器2
  5. 过滤器1

先后顺序:

  1. 注解配置:按照类名的字符串比较规则比较,值小的先执行 如A B
  2. web.xml配置:<filter-mapping>谁定义在上边,谁先执行

4. 案例

增强对象的功能

设计模式:一些通用的解决固定问题的方式

  1. 装饰模式
  2. 代理模式
    • 概念
      1. 真实对象:被代理的对象
      2. 代理对象
      3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的
    • 实现方式
      1. 静态代理:有一个类文件描述代理模式
      2. 动态代理:在内存中形成代理类
        • 实现步骤
          1. 代理对象和真实对象实现相同的接口
          2. 代理对象=Proxy.newInstance();
            1. 使用代理对象调用方法
            2. 增强方法
        • 增强方式:
          • 增强参数列表
            • 增强返回值类型
            • 增强方法体逻辑

5. 动态代理

demo:

public class ProxyTest{
    public static void main(String[] args) {
        Lenovo lenovo = new Lenovo();
        SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if(method.getName().equals("sales")){
                    // 增强参数
                    double money = (double) args[0];
                    money = money * 0.85;
                    System.out.println("专车接你");
                    // 使用真实对象调用该方法
                    String obj = (String) method.invoke(lenovo, money);
                    System.out.println("免费送货...");
                    // 增强返回值
                    return obj+"和鼠标垫";
                }else{
                    Object obj = method.invoke(lenovo, args);
                    return obj;
                }
            }
        });

        String computer = proxy_lenovo.sale(8000);
        System.out.println(computer);
    }
}

2. 扩展机制之listener

概念:web三大组件之一
当Web应用在Servlet容器中运行时,Servlet容器内部会不断的发生各种事件,如Web应用的启动和停止、用户请求到达等。 Servlet容器提供了一些默认的监听器来监听这些事件,当事件发生时,Servlet容器会负责调用监听器的方法

事件监听机制

  • 事件 :一件事情
  • 事件源 :事件发生的地方
  • 监听器 :一个对象
  • 注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码

例子

  • Spring就实现了自己的监听器,来监听ServletContext的启动事件,目的是当Servlet容器启动时,创建并初始化全局的Spring容器
  • ServletContextListener:监听ServletContext对象的创建和销毁

方法:

void contextDestroyed(ServletContextEvent sce) :ServletContext对象被销毁之前会调用该方法

void contextInitialized(ServletContextEvent sce) :ServletContext对象创建后会调用该方法

步骤:

  1. 定义一个类,实现ServletContextListener接口
  2. 复写方法
  3. 配置 @WebListener

filter和listener的区别

  • Filter是干预过程的,它是过程的一部分,是基于过程行为的。
  • Listener是基于状态的,任何行为改变同一个状态,触发的事件是一致的

扩展

springMVC容器、spring容器和servlet容器的关系

Tomcat&Jetty在启动时给每个Web应用创建一个全局的上下文环境,这个上下文就是ServletContext,其为后面的Spring容器提供宿主环境。
Tomcat&Jetty在启动过程中触发容器初始化事件,Spring的ContextLoaderListener会监听到这个事件,它的contextInitialized方法会被调用,在这个方法中,Spring会初始化全局的Spring根容器,这个就是Spring的IoC容器,IoC容器初始化完毕后,Spring将其存储到ServletContext中,便于以后来获取。
Tomcat&Jetty在启动过程中还会扫描Servlet,一个Web应用中的Servlet可以有多个,以SpringMVC中的DispatcherServlet为例,这个Servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个Servlet请求。
Servlet一般会延迟加载,当第一个请求达到时,Tomcat&Jetty发现DispatcherServlet还没有被实例化,就调用DispatcherServlet的init方法,DispatcherServlet在初始化的时候会建立自己的容器,叫做SpringMVC 容器,用来持有Spring MVC相关的Bean。同时,Spring MVC还会通过ServletContext拿到Spring根容器,并将Spring根容器设为SpringMVC容器的父容器,请注意,Spring MVC容器可以访问父容器中的Bean,但是父容器不能访问子容器的Bean, 也就是说Spring根容器不能访问SpringMVC容器里的Bean。说的通俗点就是,在Controller里可以访问Service对象,但是在Service里不可以访问Controller对象。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐