主要内容: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个

变量名真实类型作用
pageContextPageContext当前页面共享数据,还可以获取其他八个内置对象
requestHttpServletRequest一次请求访问的多个资源(转发)
sessionHttpSession一次会话的多个请求间
applicationServletContext所有用户间共享数据
responseHttpServletResponse响应对象
pageObject当前页面(Servlet)的对象 this
outJspWriter输出对象,数据输出到页面上
configServletConfigservlet的配置对象
exceptionThrowable异常对象

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联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐