HeadFirst Jsp 05 (属性和监听)

活用DD, 比如, 我想设置一个email地址, 但是不像在servlet中硬编码, 如果能再web.xml中设置一个参数, 直接拿到这个参数就更好一点.

容器建立一个servlet时, 它会读DD(web.xml), 并为ServletConfig创建名/值对, 容器不会再读初始化参数了, 一旦参数放在ServletConfig中,就不会再读了. 除非你重新部署 servlet.

可见web.xml只读取一次, 所以如果你想修改点web.xml内容时, 就需要重新部署整个内容, 但是这比将内容写在servlet或Jsp中强一点,后者不仅要重新部署, 还要重新编译.

例如我想把email地址作为servlet 来响应的内容, 把它写在 web.xml 比在servlet中强, 因为不需要编译.

web.xml 中的初始化参数如下:

<servlet>

  <init-param>

    <param-name>abc</param-name>

    <param-value>abc@123.com</param-value>

  </init-param>

</servlet>

以上内容, 会作为一个参数名和值传给ServletConfig, 然后init()方法执行时, 会将这些内容读取, 只读取这一次

getServletConfig().getInitParameter("abc")  // 可以获得参数 abc的值 abc@123.com

另外, init 初始化参数只能针对某个特定的servlet, 即 <init-param> 要在 <servlet>内部

以上的 init 初始化参数, 只能 servlet 使用, jsp 不能使用, jsp如果想使用, 可以通过增加jsp属性的方法, 上一章有提过来增加, 但是这样做也不好, 解决办法是上下文初始化参数.

上下文初始化参数, 对整个Web应用而不只是对servlet可用. 并且, 上下文参数不在 <servlet>内部, 不是针对特定的 servlet.

<context-param>

  <param-name>aaa</param-name>

  <param-value>aaa@123</param-value>

</context-param>

在servlet中使用代码, getServletContext().getInitParameter(“aaa”);

上下文参数, 与普通 parameter 参数区别

image

容器在创建servlet时, 会读取DD, 并为ServletConfig创建 名/值对, 容器不会再读初始化参数了. 除非重新部署

成熟的 容器可以提供动态部署, 即不用关闭, 重启就可以部署.

每个 servlet 一个 servletConfig, 每个 Web应用一个 ServletContext

如果你希望应用初始化参数是一个数据库 datasource ? 不可能还利用xml来初始化数据库, 另外也不能把对象放到XML文件中来进行初始化啊?

如果 xml 能够激活一个对象或一个函数, 而这个对象或函数能够初始化对象和数据库(比如数据库连接, 关闭等), 所以就由了xml监听类.

xml中设置监听类

我们需要一个单独的对象, 它能做到:

上下文初始化时得到通知( 应用得到部署 )

  • 从ServletContext得到上下文的初始化参数
  • 使用初始化参数查找名建立一个数据库连接
  • 把数据库连接存储为一个属性, 使得web应用的各个部分都能访问

上下文撤销时得到通知 ( 应用取消部署或结束 )

  • 关闭数据库连接

容器通过web.xml 发现和使用监听者

用来初始化的对象

import javax.servlet.*;

public class MyServletContextListener implements ServletContextListener {
    
    public void contextInitialized(ServletContextEvent event) {
        // 初始化数据库连接代码
        // 并且保存这段连接代码为一个 context attribute
    }

    public void contextDestoryed(ServletContextEvent event) {
        // 关闭数据库连接的代码
    }
}
View Code

<listener></listener>

process

image

image

image

image

例子:我们把String初始化参数转换为一个真正的对象, 一个 Dog

0. 首先创建一个监听者类 , 把它放在 WEB-INF/classes目录中, 并在部署描述文件中放一个<listener>元素来告诉容器.

package com.example;

import javax.servlet.*;

public class MyServletContextListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent event) {
        // 由事件得到 ServletContext
        servletContext sc = event.getServletContext();
        // 使用上下文的到初始化参数
        String dogBreed = sc.getInitParameter("breed");
        // 根据参数建立对象
        Dog d = new Dog(dogBreed);
        // 使用上下文, 增加了属性
        sc.setAttribute("dog", d);    
    }

    public void contextDestroyed(ServletContextEvent event) {
    }
}

1. web.xml


2. Dog class 只是一个比方, 连接数据库的内容类似


3. Listener 主要用来实例化 Dog

4. Servlet, testlistener

5. 测试时, 只需要执行 http://localhost:8080/listenerTest/ListenTest.do

总结:首先, 容器在启动的时候, 会去读取 web.xml, 并实例化其中的<Listener> 类和servlet类,  并且将所有的上下文参数<context-param> 做成了 (key-value 对的形式交给servletContext), 注意:实例化<Listener>类时会调用contextInitialized()这个方法, 这个方法的实际意义是用来连接数据库的, 这个方法具体做什么事情, 你可以自己编写, 只不过这个方法可以访问到ServletContext所有的key和value对, 通过这些可以访问到得key和value, 来初始化数据库或者创建对象等等, 并将结果以 key, value的形式添加到 ServletContext的atribute中, 此时这个方法可以通过 getServletContext()获得<context-param>中的参数来进行一些处理, 例如连接数据库, 然后再将数据库连接的内容设置成servletContext的一个属性, 供所有人调用, 这个时候如果有别人需要请求时, 其他的servlet就可以调用这个属性来确认连接数据库. 由此可见, <Listener>会在容器启动时先运行并实例化完毕, 在哪里等待, 等待别的servlet调用 (本例中就是 ListenerTester).

这样也就实现了, 属性可以通过编程设置. 另外可以用很多类型的监听者, 不单单是监听上下文参数的.

另外要注意, 上下文属性是大家都能访问的, 换句话说, 它不是线程安全的.

其他的监听者

image

image

属性和参数的区别


image

上下文, 请求, 会话

image

image

image

多线程访问时, 属性也并非是安全的, 比如 A线程修改了一个属性的值, 而后B线程也修改了属性的值, 但是A不知道, A还以为属性是刚刚他修改的内容. 类似之前说的实例变量不安全, 一样.

RequestDispatcher 只有两个方法, forward() 和 include()

image

原文地址:https://www.cnblogs.com/moveofgod/p/3389773.html