spring 容器启动-观察者模式
2017-06-25 00:53
330 查看
之前一篇文件简单的讲了一些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类中的[b]initWebApplicationContext()方法[/b]
//目的是通过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的监听器和事件
// 所有的事件都必须继承[b]EventObject
[/b]
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);
}
}
首先在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类中的[b]initWebApplicationContext()方法[/b]
//目的是通过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的监听器和事件
// 所有的事件都必须继承[b]EventObject
[/b]
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);
}
}
相关文章推荐
- 用 spring 实现观察者设计模式
- 深入列表遍历问题,并分析spring和tomcat中观察者模式的实现
- web项目启动Spring容器
- spring—容器启动载入bean过程
- Web容器启动时加载Spring的配置
- 模式实践:观察者模式与Spring
- Spring Ioc(1)——web中spring容器的启动初步
- Spring IoC容器在Web环境中的启动源码阅读
- Spring容器启动过程
- Spring源码解析之:Spring Security启动细节和工作模式
- Spring源代码分析之(二):IOC容器在web容器中的启动
- spring+hibernate整合,不能启动spring容器进行单元测试
- Spring的Bean生命周期内的callback方法和Spring容器启动关闭时的callback方法
- Struts1和Spring整合三种启动Spring容器的方式
- Web容器启动时加载Spring
- 开发阶段eclipse下面的spring容器的启动优化
- Spring容器启动后自动执行Servlet进行预处理
- Spring容器启动配置
- 【spring】容器启动后马上调用java 类的方法
- Spring源代码解析(二):IoC容器在Web容器中的启动