之前一篇文件简单的讲了一些spring容器的启动顺序以及父子容器,现在说的是一些有关spring容器启动时涉及的设计模式的思想-观察者模式
首先在web.xml文件中我们通过配置监听器来监听web容器的启动从而加载spring容器,并且spring中的一些监听事件和监听器也会同时被创建出来:
<!-- 加载spring容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
//ServletContextEvent event web程序应用上下文启动事件
初始化容器是首先调用的是ContextLoaderListener类中contextInitialized((ServletContextEvent event))
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
//event.getServletContext() 获得web应用程序上下文 调用ContextLoader类中的initWebApplicationContext()方法
//目的是通过servletContext容器获得spring web application(容器)
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
//如果为空就创建一个spring web application
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
/
if (this.context instanceof ConfigurableWebApplicationContext) {
//重启容器, 其中会调用 refresh()方法来完成观察者模式的监听与实现
configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);
}
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
customizeContext(sc, wac);
wac.refresh();
}
spring在容器中使用了观察者模式:
spring事件:ApplicationEvent, 事件,该抽象类继承了EventObject,jdk建议所有的事件都应该继承自EventObject
spring事件监听器:ApplicationLisener
Java代码
- public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
- /**
- * Handle an application event.
- * @param event the event to respond to
- */
- void onApplicationEvent(E event);
- }
该接口继承了EventListener接口,jdk建议所有的事件监听器都应该继承EventListener
spring事件发布:ApplicationEventPublisher , ApplicationContext继承了该接口,在ApplicationContext的抽象类AbstractApplicationContext中做了实现
Java代码
- public interface ApplicationEventPublisher {
- /**
- * Notify all listeners registered with this application of an application
- * event. Events may be framework events (such as RequestHandledEvent)
- * or application-specific events.
- * @param event the event to publish
- * @see org.springframework.web.context.support.RequestHandledEvent
- */
- void publishEvent(ApplicationEvent event);
- }
AbstractApplicationContext类中publishEvent方法实现:
Java代码
- public void publishEvent(ApplicationEvent event) {
- Assert.notNull(event, "Event must not be null");
- if (logger.isTraceEnabled()) {
- logger.trace("Publishing event in " + getDisplayName() + ": " + event);
- }
- //事件广播委托给ApplicationEventMulticaster来进行
- getApplicationEventMulticaster().multicastEvent(event);
- if (this.parent != null) {
- this.parent.publishEvent(event);
- }
- }
由上代码可知,AbstractApplicationContext类并没有具体的做事件广播,而是委托给ApplicationEventMulticaster来进行,ApplicationEventMulticaster的multicastEvent()方法实现如下:
Java代码
- public void multicastEvent(final ApplicationEvent event) {
- for (final ApplicationListener listener : getApplicationListeners(event)) {
- Executor executor = getTaskExecutor();
- if (executor != null) {
- executor.execute(new Runnable() {
- public void run() {
- listener.onApplicationEvent(event);
- }
- });
- }
- else {
- listener.onApplicationEvent(event);
- }
- }
- }
获得listener集合,遍历listener触发事件Executor接口有多个实现类,可以支持同步或异步广播事件
现在大家可能有个疑问,spring容器是怎么根据事件去找到事件对应的事件监听器呢? 接着上面的refresh() 讲
在spring容器初始化的时候,
- public void refresh() throws BeansException, IllegalStateException {
- synchronized (this.startupShutdownMonitor) {
- // Prepare this context for refreshing.
- prepareRefresh();
- // Tell the subclass to refresh the internal bean factory.
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- // Prepare the bean factory for use in this context.
- prepareBeanFactory(beanFactory);
- try {
- // Allows post-processing of the bean factory in context subclasses.
- postProcessBeanFactory(beanFactory);
- // Invoke factory processors registered as beans in the context.
- invokeBeanFactoryPostProcessors(beanFactory);
- // Register bean processors that intercept bean creation.
- registerBeanPostProcessors(beanFactory);
- // Initialize message source for this context.
- initMessageSource();
- //初始化一个事件注册表
- // Initialize event multicaster for this context.
- initApplicationEventMulticaster();
- // Initialize other special beans in specific context subclasses.
- onRefresh();
- //注册事件监听器
- // Check for listener beans and register them.
- registerListeners();
- // Instantiate all remaining (non-lazy-init) singletons.
- finishBeanFactoryInitialization(beanFactory);
- // Last step: publish corresponding event.
- finishRefresh();
- }
- catch (BeansException ex) {
- // Destroy already created singletons to avoid dangling resources.
- destroyBeans();
- // Reset 'active' flag.
- cancelRefresh(ex);
- // Propagate exception to caller.
- throw ex;
- }
- }
- }
以下是自己写一个spring的监听器和事件
// 所有的事件都必须继承EventObject
public abstract class ApplicationEvent extends EventObject {}
public class MyTestEvent extends ApplicationEvent {
private String name;
private String id;
public MyTestEvent(Object source, String name, String id) {
super(source);
this.name = name;
this.id = id;
System.out.println("tetetetet");
}
public MyTestEvent(Object source) {
super(source);
}
}
//监听器
public class MyTestListener implements ApplicationListener<ApplicationEvent>{
@Override
public void onApplicationEvent(ApplicationEvent arg0) {
if(arg0 instanceof MyTestEvent){
MyTestEvent myTestEvent = (MyTestEvent)arg0;
System.out.println(myTestEvent.getId());
}else{
System.out.println("nonono");
}
}
}
spring-bean.xml
<bean id="myTestEvent" class="com.springinaction.springidol.bean.MyTestEvent">
<constructor-arg value ="heee"/>
<constructor-arg value ="name"/>
<constructor-arg value ="34"/>
</bean>
<bean id="myTestListener" class="com.springinaction.springidol.bean.MyTestListener"/>
测试方法
public class testBean {
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/spring-bean.xml");
//测试监听事件
MyTestEvent myTestEvent = (MyTestEvent)ctx.getBean("myTestEvent");
//MyTestEvent myTestEvent = new MyTestEvent("hello", "test", "2");
//将事件放进applicationContext中中 只要容器启动就会触发监听
ctx.publishEvent(myTestEvent);
}
}