1. ServletConfig和ServletContext
1.1 ServletConfig和ServletContext配置和使用
一些和业务逻辑无关的信息不应该放在硬编码到程序中,一般将其放在ServletConfig或ServletContext。
在web.xml文件中配置ServletConfig和ServletContext参数:
<!-- 配置ServletContext参数 --> <context-param> <param-name>email</param-name> <param-value>yaoyinglong@gmail.com</param-value> </context-param>
<servlet> <servlet-name>Test</servlet-name> <servlet-class>com.yyl.Test</servlet-class> <!-- 配置ServletConfig参数 --> <init-param> <param-name>email</param-name> <param-value>yaoyinglong@gmail.com</param-value> </init-param> </servlet>
我们可以清楚的看到:ServletConfig参数配置在<servlet> 节点内部;而ServletContext参数配置在<servlet> 节点外部。
使用ServletConfig和ServletContext参数:
String servletConfigEmail=getServletConfig().getInitParameter("email"); String servletContextEmail=getServletContext().getInitParameter("email");
不用说大家值也只那行去那个参数了。
ServletConfig和ServletContext参数的使用范围:
ServletConfig参数:
①只可以在配置了它的servlet中可用;
②在Java对象变为Servlet之前不可用(init函数有两个版本,一般情况下不要覆盖接受ServletConfig对象的那个版本吗,即是要覆盖也应该加上super.init(ServletConfig))。
③ServletConfig参数只在Servlet初始化是读取一次,中途不能修改。
ServletContext对Web应用中的所有servlet和JSP都可用。
1.2 ServletConfig和ServletContext举例
这两个对象非常重要,当我们程序需要读取配置文件的时候便可以将在这里配置配置文件的路径,下面以Spring配置来说明:
我们如果通过MyEclipse使用Spring框架时,它会帮我们将Spring配置好,无需劳您大驾亲自动手,但是如果你使用的是Eclipse的时候,抱歉,它可没那么贴心了,只能我们自己手动来配了,无论是IDE帮我们配还是我们自己手动来陪,很多时候都是Ctrl+C和Ctrl+V,可是你知道Spring是怎么做的吗?知道的请略过,或者您可以看看我说的是不是正确,指出错误之处(感激不尽!!!),不知道的就请跟着我一起看看吧:
首先来看为什么要在web.xml中配置Spring:
不在web.xml中配置Spring时,我们要获取配置在Spring的bean时我们是这样做的:
import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { BeanFactory beanFactory=new ClassPathXmlApplicationContext("appclicationContext.xml"); beanFactory.getBean("beanId"); } }
C/S端可以这么做,那么B/S端呢?我们可以用一个Servlet来实现:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { BeanFactory beanFactory=new ClassPathXmlApplicationContext("applicationContext.xml"); getServletContext().setAttribute("beanFactory", beanFactory); }
这样配置了之后,在Web应用的所有JSP和Servlet中都可以使用BeanFactory了。
还有一种配置方式就是我们下面要讲到的监听器(可以先看一下下面的内容在回来):
@Override public void contextDestroyed(ServletContextEvent sce) { BeanFactory beanFactory=new ClassPathXmlApplicationContext("applicationContext.xml"); sce.getServletContext().setAttribute("beanFactory", beanFactory); }
大家有没有发现无论用什么方法配,代码都是一样的。Spring框架开发者,也发现了这件事情,于是他们就把这件事情给做了(他们提供了后两种的实现),我们都知道,Servlet和监听器都是要在web.xml中配置的。因此我们工作就是把Spring框架开发者帮我们实现好的Servlet和监听器配置到web.xml中。
注:我这里只是简单的说明最核心的部分,强大的Spring要做的工作远比我这里做的多的多,并且我这里将application.xml配置文件的路径硬编码到了程序里,这是不好的,应该使用我们现在讲的ServletConfig和ServletContext参数配置到xml文件中。
我们看看Spring到底是怎么做的:
哈哈,我没有骗大家吧,Spring为我们实现了Servlet和Listener的配置方式。不过大多数情况下我们都是用Listener来配置的。那我们进到这个监听器中看看:
public class ContextLoaderListener implements ServletContextListener { private ContextLoader contextLoader; public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); this.contextLoader.initWebApplicationContext(event.getServletContext()); } protected ContextLoader createContextLoader() { return new ContextLoader(); } public ContextLoader getContextLoader() { return this.contextLoader; } public void contextDestroyed(ServletContextEvent event) { if (this.contextLoader != null) { this.contextLoader.closeWebApplicationContext(event.getServletContext()); } } }
我们发现他在Web应用启动时,做了两件事情:创建ContextLoader类,然后调用它的initWebApplicationContext方法,进入该方法:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) throws IllegalStateException, BeansException { // 考虑到篇幅的太大故将一些我们暂时不关心的部分删掉了…… try { // Determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. this.context = createWebApplicationContext(servletContext, parent); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context); // 考虑到篇幅的太大故将一些我们暂时不关心的部分删掉了……
}
从黄底加粗的代码中我们可以清楚的看到:先创建了WebApplicationContext对象,然后将其保存到了servletContext中(即Application范围内)。以后Struts2和Spring继承插件就可以从Application中获取BeanFactory,通过BeanFactory取得Ioc容器中的Actoin对象,这样Action的依赖对象都会被注入。
那么我再跟踪createWebApplicationContext函数:
protected WebApplicationContext createWebApplicationContext( ServletContext servletContext, ApplicationContext parent) throws BeansException { Class contextClass = determineContextClass(servletContext); // 考虑到篇幅的太大故将一些我们暂时不关心的部分删掉了……
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setParent(parent); wac.setServletContext(servletContext); //这里读取web.xml文件中的 <context-param>节点中的值 wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM)); customizeContext(servletContext, wac); wac.refresh(); return wac; }
从黄底加粗的代码指示我们必须在web.xml文件中配置<context-param>参数,至于配什么呢,<param-value>参数的值我们知道,肯定是要applicationContext.xml文件的路径,参数的名字是什么呢?往后看,它从那个参数读,我们就用那个参数组为的<param-name>值。
查看CONFIG_LOCATION_PARAM常量:
所以我们的<param-name>的值为contextConfigLocation。
诶!前面我们不是说创建BeanFactory吗?怎么这里创建的是WebApplicationContext呢?经跟踪发现它继承自BeanFactory,主要使用BeanFactory的getBean方法来获取Bean。
这下明白我在web.xml中使用监听器配置Spring的含义了吧。
那么如果我们使用Servlet怎么配置呢?(不懂的可以留言,这里就不浪费篇幅了)。
1.3 ServletContext不是线程安全的
应用中的每一部分都能访问到ServletContext,因此,必要时应该对其同步:
synchronized (getServletContext()) { //业务逻辑代码 }
2. 监听器(Listener)
所谓监听者就是,当你做某一件事情的时候,另一个类关注着你,并且采取相应的行动。
2.1 ServletContextListener
查看API可知,它监听Web应用初始化和Web应用销毁动作,那我们就可以指示该监听者,在Web应用初始化和Web应用销毁时帮我们干我们想干的事情:
public class TestServletContextListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("Oh,my god! I was killed"); } @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("houhou, God created me"); String email=sce.getServletContext().getInitParameter("email"); System.out.println("My Email is "+email); } }
这样它就能听我们的话了吗?还不行,因为Tomcat不会看你实现了ServletContextListener接口就会让它去监视Web应用的初始化和销毁。Tomcat只会处理你在web.xml文件中配置了的事情。因此还需在web.xml中配置监听:
在节点下添加:
<listener> <listener-class>com.yyl.TestServletContextListener</listener-class> </listener>
现在配置就完成了,ServletContextListener的实现者已经为我所用了,不行你看看:
当我启动Tomcat时:
当我们关闭Tomcat时:
到这里您是不是有个疑问:Tomcat怎么知道你配的这个com.yyl.TestServletContextListener是用来监听Web应用的创建于销毁动作呢,即这个监听者是ServletContext监听者?因为Servlet还有很多其他监听者(后面会一一登场亮相)。这是因为Web容器会会检查类,检查它继承了那个监听接口,或者多个监听接口,据此明确它来监听什么类型的事件。
3. 监听器有8种(8种接口)
ServletContextAttributeListener:监听在ServletContext对象中添加、删除、替换属性的动作。
HttpSessionListener:监听Web应用Session的创建于销毁。据此可以知道有多少并发用户。
ServletRequestListener:监听浏览器请求Servlet动作。
ServletRequestAttrubuteListener:监听在ServletRequest对象中请求中添加、删除、替换属性的动作。
HttpSessionBindingListener:属性本身在被添加到session中或从session中删除时得到通知。
HttpSesionAttributeListener:监听在HttpSession中增加、删除、替换属性的动作。
servletContextListener:监听Web应用创建或撤销动作。
HttpSessionActivationListener:当一个Session被迁移到另一个JVM中通知实现了该接口的属性做好准备。
今天实在没有精力一一举例了,下篇吧!