Spring源码分析-ApplicationContext

ApplicationContext

BeanFactory:面向的是Spring,属于Spring的基础设施,是Bean集合的工厂类,其中包含了各种Bean的定义,客户端请求时将对应的Bean实例化,缓存所有单实例的Bean。生成协作类之间的关键即解决类之间的依赖关系,还包含了Bean生命周期的控制,默认实现是DefalutListableBeanFactory。在首次使用Bean的时候会初始化Bean

ApplicationContext:面向的是Spring应用的,ApplicationContext扩展于BeanFactory,所以它具有BeanFactory所有的功能,除此之外ApplicationContext提供:1. 以通用的方式加载文件资源的能力。2. 将事件发布到注册监听器的功能。3. 解析消息的能力,支持国际化。4. 从父上下文继承的特性。在容器启动时,会创建所有单实例的Bean。默认实现有ClassPathXmlAC,FileSystemXmlAC,WebXmlAC

我们一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文,但有时候为了行文方便,我们也将ApplicationContext称为Spring容器。

对于BeanFactory 和 ApplicationContext的用途:

  • BeanFactory是Spring框架的基础设施,面向Spring本身
  • ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都可以直接使用Application而非底层的BeanFactory.

BeanFactory和ApplicationContext初始化区别:BeanFactory在初始化容器时并没有实例化Bean,而是在第一次访问到目标Bean时才实例化该Bean;而ApplicationContext会在初始化上下文时实例化所有的单例的Bean。

WebApplicationContext和BeanFactory、ApplicationContext初始化的区别:WebApplicationContext的初始化需要servletContext实例,即初始化需要拥有web容器,我们需要在web.xml中配置自启动的servlet或web容器监听器(servletContextListener)。

在BeanFactory和ApplicationContext中的Bean的作用域有两种:singleton和prototype,在WebApplicationContext中的Bean的作用域有三种:request,session和globalSession。

  • singleton:在IOC容器中仅存在一个Bean实例,Bean以单例方式存在,外部引用都指向这个Bean。

  • prototype:每次调用Bean都返回一个新实例。

  • request:在同一个Http请求的Bean相同,每个Http请求创建一个新的Bean。

  • session:在Http请求对应同一个session时对应同一个Bean。

  • globalSession:一般的web应用中globalSession等价于session,只有在portlet web应用中才存在globalSession概念。

Application继承了HierachicalBeanFactory和ListableBeanFactory接口,在此基础上,还通过其他接口扩展了BeanFactory的功能。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
		
	String getId();
	
	String getApplicationName();
	
	String getDisplayName();
	
	long getStartupDate();
	
	ApplicationContext getParent();
	
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}

ListableBeanFactory

细化了许多BeanFactory的功能,比如说getBeanDefinitionNames()。

HierarchicalBeanFactory

HierarchicalBeanFactory 提供父容器的访问功能.至于父容器的设置,需要找ConfigurableBeanFactory的setParentBeanFactory(接口把设置跟获取给拆开了!)
HierarchicalBeanFactory源码具体:

  1. 第一个方法返回本Bean工厂的父工厂。这个方法实现了工厂的分层。
  2. 第二个方法判断本地工厂是否包含这个Bean(忽略其他所有父工厂)。这也是分层思想的体现。

总结:这个工厂接口非常简单,实现了Bean工厂的分层。这个工厂接口也是继承自BeanFacotory,也是一个二级接口,相对于父接口,它只扩展了一个重要的功能——工厂分层。

public interface HierarchicalBeanFactory extends BeanFactory {
	//  返回本Bean工厂的父工厂
	BeanFactory getParentBeanFactory();
	//  本地工厂(容器)是否包含这个Bean
	boolean containsLocalBean(String name);

}

在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了。ApplicationContext的初始化和BeanFactory有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。因此ApplicationContext的初始化时间会比BeanFactory稍长一些,不过稍后的调用则没有"第一次惩罚"的问题。

ApplicationEventPublisher

使容器具有发布应用上下文事件的功能,包括容器的启动,关闭事件等,凡是实现了ApplicationListener事件监听接口的Bean都能够接收到容器事件并对该事件进行处理。在ApplicationContext抽象实现类AbstractApplicationContext中存在ApplicationEventMulticaster负责存储所有的监听器并在事件发生时通知监听器

public interface ApplicationEventPublisher {
 
	//通知该应用中注册的匹配的事件监听器,事件有可能是框架事件也有可能是特定的应用程序事件
	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}
	//向监听器通知事件
	void publishEvent(Object event);
 
}

MessageSource

为应用提供i18N国际化消息访问的功能

 
public interface MessageSource {
	//用来从MessageSource获取消息的基本方法。如果在指定的locale中没有找到消息,则使用默认的消息。args中的参数将使用标准类库中的MessageFormat来作消息中替换值
	String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
	//本质上和上一个方法相同,其区别在:没有指定默认值,如果没找到消息,会抛出一个NoSuchMessageException异常。
	String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
	//上面方法中所使用的属性都封装到一个MessageSourceResolvable实现中,而本方法可以指定MessageSourceResolvable实现
	String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

EnvironmentCapable

实现了此接口的类有应该有一个Environment类型的域,并且可以通过getEnvironment方法取得。 Spring中所有的应用上下文类都实现了此接口。这个接口的主要作用是用于类型检查的。例如框架中有些与用户定义的BeanFactory交互的方法,这些方法有些就需要使用用户定义的BeanFactory的环境变量。这个时候就要看其是否是EnvironmentCapable接口的子类了。 Spring中所有的应用上下文都实现了EnvironmentCapable接口。但是ConfigurableApplicationContext接口重新定义了getEnvironment方法,并将其返回值限定为ConfigurableEnvironment,这样的后果就是,使用ConfigurableApplicationContext接口会覆盖Environment接口。

//返回与此组件关联的Environment(可以为null或默认环境)
public interface EnvironmentCapable {
    Environment getEnvironment();
}

ResourcePatternResolver

加载资源,可以通过带前缀的Ant风格的资源文件路径装载Spring的配置文件

  • LifeCycle:主要提供了start() & stop()两个方法,主要用于控制异步处理过程,在具体使用时该接口需要同时被ApplicationContext和具体的Bean实现,ApplicationContext会将start/stop信息传递给该容器中所有实现了该接口的Bean,达到控制和任务调度等目的

ConfigurableApplicationContext

ConfigurableApplicationContext扩展于ApplicationContext,主要新增了两个方法 refresh()和close(),让Application具有启动、刷新、关闭应用上下文的能力。

在应用上下文关闭的情况下,refresh可以启动上下文,在启动的情况下可以清楚缓存并重新装载配置信息。 close方法这可以关闭上下文。

ClassPathXmlApplicationContext

如果配置文件放在在类路径下,则优先考虑使用ClassPathXmlApplication实现类

public class ClassPathXmlApplicationTest { 
	public static void main(String[] args) { 
	
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "classpath:com/xgj/master/ioc/beanfactory/bean-plane.xml"); 
	
	Plane plane = (Plane) applicationContext.getBean("plane"); 
	
	plane.introduce(); 
	} 
}

Spring将ApplicationContext启动的全过程,refresh函数中包含了几乎ApplicationContext中提供的全部功能,而且此函数中逻辑非常清晰明了,很容易分析对应的层次及逻辑。:

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
	// code
}

调用父类AbstractXmlApplicationContext的refesh()方法

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 准备刷新的上下文环境,例如对系统属性或者环境变量进行准备及验证。
			prepareRefresh();

			// 初始化BeanFactory,并进行XML文件读取,
            // 这一步之后,ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是可以进行Bean的提取等基础操作了。
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 对BeanFactory进行各种功能填充,@Qualifier与@Autowired这两个注解正是在这一步骤中增加的支持。
            // 设置@Autowired和 @Qualifier注解解析器QualifierAnnotationAutowireCandidateResolver
			prepareBeanFactory(beanFactory);

			try {
				// 子类覆盖方法做额外的处理,提供了一个空的函数实现postProcessBeanFactory来方便程序员在业务上做进一步扩展。
				postProcessBeanFactory(beanFactory);

				// 激活各种BeanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用是在getBean时候
				registerBeanPostProcessors(beanFactory);

				// 为上下文初始化Message源,即不同语言的消息体进行国际化处理
				initMessageSource();

				// 初始化应用消息广播器,并放入“applicationEventMulticaster”bean中
				initApplicationEventMulticaster();

				// 留给子类来初始化其它的Bean
				onRefresh();

				// 在所有注册的bean中查找Listener bean,注册到消息广播器中
				registerListeners();

				// 初始化剩下的单实例(非惰性的)
				finishBeanFactoryInitialization(beanFactory);

				// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

prepareRefresh()

protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);
        if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        }
        //留给子类覆盖
        initPropertySources();
        //验证需要的属性文件是否都已经放入环境中
        getEnvironment().validateRequiredProperties();
     //to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
    }

obtainFreshBeanFactory()

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //初始化BeanFactory,并进行XML文件读取,并将得到的BeanFacotry记录在当前实体的属性中
        refreshBeanFactory();
        //返回当前实体的beanFactory属性
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

    AbstractRefreshableApplicationContext.java
    @Override
    protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //在介绍BeanFactory的时候,声明方式为:BeanFactory bf = new XmlBeanFactory("beanFactoryTest.xml"),
       //其中的XmlBeanFactory继承自DefaultListableBeanFactory,并提供了XmlBeanDefinitionReader类型的reader属性,
       //也就是说DefaultListableBean Factory是容器的基础。必须首先要实例化,那么在这里就是实例化DefaultListableBeanFactory的步骤。
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            //为了序列化指定id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象
            beanFactory.setSerializationId(getId());
            //定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖以及
            customizeBeanFactory(beanFactory);
            //初始化DodumentReader,并进行XML文件读取及解析
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                //使用全局变量记录BeanFactory类实例。
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }


    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        //如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性,
        //此属性的含义:是否允许覆盖同名称的不同定义的对象
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        //如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性,
        //此属性的含义:是否允许bean之间存在循环依赖
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }


    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        //为指定beanFactory创建XmlBeanDefinitionReader
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        //对beanDefinitionReader进行环境变量的设置
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        //对BeanDefinitionReader进行设置,可以覆盖
        initBeanDefinitionReader(beanDefinitionReader);
        //使用XmlBeanDefinitionReader的loadBeanDefinitions方法进行配置文件的加载及注册
        loadBeanDefinitions(beanDefinitionReader);
    }

prepareBeanFactory(beanFactory)

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //设置beanFactory的classLoader为当前context的classLoader
        beanFactory.setBeanClassLoader(getClassLoader());
        //设置beanFactory的表达式语言处理器,Spring3增加了表达式语言的支持,SPEL语言。
        //默认可以使用#{bean.xxx}的形式来调用相关属性值。
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        //为beanFactory增加了一个默认的propertyEditor,这个主要是对bean的属性等设置管理的一个工具
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        //添加BeanPostProcessor
        //ApplicationContextAwareProcessor实现了BeanPostProcessor接口,在bean实例化的时候会被调用
        //postProcessBeforeInitialization方法中调用了invokeAwareInterfaces。从invokeAwareInterfaces方法中,
     //我们可以看出来,实现这些Aware接口的bean在被初始化之后,可以取得一些对应的资源。
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        //Spring将ApplicationContextAwareProcessor注册后,在invokeAwareInterfaces方法中间调用的Aware类已经不是普通的bean了,
        //如ResourceLoaderAware,ApplicationEventPublisherAware等,需要在Spring做bean的依赖注入的时候忽略它们。
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

        //设置了几个自动装配的特殊规则  
        //当注册了依赖解析后,例如当注册了对BeanFactory.class的解析后,当bean的属性注入的时候,
     //一旦检测到属性为BeanFactory类型便会将beanFactory的实例注入进去。
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        //增加对AspectJ的支持
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        //将相关环境变量及属性注册以单例模式注册,environment,systemProperties,systemEnvironment
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }

ResourceEditorRegistrar的registerCustomEditors的调用时机,就是AbstractBeanFactory类中的initBeanWrapper方法,这是在bean初始化时使用的一个方法,主要是在将BeanDefinition转换为BeanWrapper后用于对属性的填充。在bean的初始化后会调用ResourceEditorRegistrar的registerCustomEditors方法进行批量的通用属性编辑器注册。注册后,在属性填充的环节便可以直接让Spring使用这些编辑器进行属性的解析了。
Spring中用于封装bean的是BeanWrapper类型,而它又间接继承了PropertyEditorRegistry类型,也就是我们之前反复看到的方法参数PropertyEditorRegistry,其实大部分情况下都是BeanWrapper,对于BeanWrapper在Spring中的默认实现是BeanWrapperImpl,而BeanWrapperImpl除了实现BeanWrapper接口外还继承了PropertyEditorRegistrySupport,在PropertyEditorRegistrySupport中有这样一个方法:createDefaultEditors,基本的属性编辑器就在此处被注册。

postProcessBeanFactory(beanFactory);

BeanFactoryPostProcessor接口跟BeanPostProcessor类似,可以对bean的定义(配置元数据)进行处理。也就是说,Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其他的bean之前读取配置元数据,并有可能修改它。如果你愿意,你可以配置多个BeanFactoryPostProcessor。你还能通过设置“order”属性来控制BeanFactoryPostProcessor的执行次序(仅当BeanFactoryPostProcessor实现了Ordered接口时你才可以设置此属性,因此在实现BeanFactoryPostProcessor时,就应当考虑实现Ordered接口)。

如果你想改变实际的bean实例(例如从配置元数据创建的对象),那么你最好使用BeanPostProcessor。同样地,BeanFactoryPostProcessor的作用域范围是容器级的。它只和你所使用的容器有关。如果你在容器中定义一个BeanFactoryPostProcessor,它仅仅对此容器中的bean进行后置处理。BeanFactoryPostProcessor不会对定义在另一个容器中的bean进行后置处理,即使这两个容器都是在同一层次上。

在Spring中存在对于BeanFactoryPostProcessor的两种典型应用。

  1. PropertyPlaceholderConfigurer
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations">
   <list>
    <value>/WEB-INF/mail.properties</value>  
    <value>classpath: conf/sqlmap/jdbc.properties</value>//注意这两种value值的写法
   </list>
  </property>
   <property name="fileEncoding">
     <value>UTF-8</value>
   </property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName"value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}"/>
  <property name="password"value="${jdbc.password}" />
</bean>

jdbc.properties文件
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/mysqldb?useUnicode=true&amp;characterEncoding=UTF-8&amp;
jdbc.username=root
jdbc.password=123456

PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是BeanFactoryPostProcessor接口的一个实现。PropertyPlaceholderConfigurer可以将上下文(配置文件)中的属性值放在另一个单独的标准java Properties文件中去。在XML文件中用${key}替换指定的properties文件中的值。这样的话,只需要对properties文件进行修改,而不用对xml配置文件进行修改。
在Spring中,使用PropertyPlaceholderConfigurer可以在XML配置文件中加入外部属性文件,当然也可以指定外部文件的编码,PropertyPlaceholderConfigurer如果在指定的Properties文件中找不到你想使用的属性,它还会在Java的System类属性中查找。可以通过System.setProperty(key, value)或者java中通过-Dnamevalue来给Spring配置文件传递参数。

查看层级结构可以看出PropertyPlaceholderConfigurer这个类间接继承了BeanFactoryPostProcessor接口。这是一个很特别的接口,当Spring加载任何实现了这个接口的bean的配置时,都会在bean工厂载入所有bean的配置之后执行postProcessBeanFactory方法。在PropertyResourceConfigurer类中实现了postProcessBeanFactory方法,在方法中先后调用了mergeProperties、convertProperties、processProperties这3个方法,分别得到配置,将得到的配置转换为合适的类型,最后将配置内容告知BeanFactory。正是通过实现BeanFactoryPostProcessor接口,BeanFactory会在实例化任何bean之前获得配置信息,从而能够正确解析bean描述文件中的变量引用。

  • 使用自定义BeanFactoryPostProcessor

实现一个BeanFactoryPostProcessor,实现一个简单的回调处理器,它能去除潜在的"流氓"属性值,例如bean定义中留下bollocks这样的字眼。

<bean id="bfpp" class="com.spring.ch04.ObscenityRemovingBeanFactoryPostProcessor">  
    <property name="obscenties">  
        <set>  
            <value>bollocks</value>  
            <value>winky</value>  
            <value>bum</value>  
            <value>Microsoft</value>  
        </set>  
    </property>  
  
</bean>  
<bean id="simpleBean" class="com.spring.ch04.SimplePostProcessor">  
    <property name="connectionString" value="bollocks"/>  
    <property name="password" value="imaginecup"/>  
    <property name="username" value="Microsoft"/>  
</bean>
public class ObscenityRemovingBeanFactoryPostProcessor implements  
        BeanFactoryPostProcessor {  
    private Set<String> obscenties;  
    public ObscenityRemovingBeanFactoryPostProcessor(){  
        this.obscenties=new HashSet<String>();  
    }  
    public void postProcessBeanFactory(  
            ConfigurableListableBeanFactory beanFactory) throws BeansException {  
        String[] beanNames=beanFactory.getBeanDefinitionNames();  
        for(String beanName:beanNames){  
            BeanDefinition bd=beanFactory.getBeanDefinition(beanName);  
            StringValueResolver valueResover=new StringValueResolver() {  
                public String resolveStringValue(String strVal) {  
                    if(isObscene(strVal)) return "*****";  
                    return strVal;  
                }  
            };  
            BeanDefinitionVisitor visitor=new BeanDefinitionVisitor(valueResover);  
            visitor.visitBeanDefinition(bd);  
        }  
    }  
    public boolean isObscene(Object value){  
        String potentialObscenity=value.toString().toUpperCase();  
        return this.obscenties.contains(potentialObscenity);  
    }  
    public void setObscenties(Set<String> obscenties) {  
        this.obscenties.clear();  
        for(String obscenity:obscenties){  
            this.obscenties.add(obscenity.toUpperCase());  
        }  
    }  
  
}
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;  
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;  
import org.springframework.beans.factory.xml.XmlBeanFactory;  
import org.springframework.core.io.ClassPathResource;  
public class PropertyConfigurerDemo {  
    public static void main(String[] args) {  
        ConfigurableListableBeanFactory bf=new XmlBeanFactory(new ClassPathResource("/META-INF/BeanFactory.xml"));  
        BeanFactoryPostProcessor bfpp=(BeanFactoryPostProcessor)bf.getBean("bfpp");  
        bfpp.postProcessBeanFactory(bf);  
        System.out.println(bf.getBean("simpleBean"));  
          
    }  
}

SimplePostProcessor{connectionString=,username=,password=imaginecup

invokeBeanFactoryPostProcessors(beanFactory);

  1. 从invokeBeanFactoryPostProcessors的方法中我们看到,对于BeanFactoryPostProcessor的处理主要分两种情况进行,一个是对于BeanDefinitionRegistry类的特殊处理,另一种是对普通的BeanFactoryPostProcessor进行处理。而对于每种情况都需要考虑硬编码注入注册的后处理器以及通过配置注入的后处理器。

对于硬编码注册的后处理器的处理,主要是通过AbstractApplicationContext中的添加处理器方法addBeanFactoryPostProcessor进行添加。

public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {

this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);

}

添加后的后处理器会存放在beanFactoryPostProcessors中,而在处理BeanFactoryPostProcessor时候会首先检测beanFactoryPostProcessors是否有数据。当然,BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,不但有BeanFactoryPostProcessor的特性,同时还有自己定义的个性化方法,也需要在此调用。所以,这里需要从beanFactoryPostProcessors中挑出BeanDefinitionRegistryPostProcessor的后处理器,并进行其postProcessBeanDefinitionRegistry方法的激活。

  1. 记录后处理器主要使用了三个List完成。

registryPostProcessors:记录通过硬编码方式注册的BeanDefinitionRegistryPostProcessor类型的处理器。

regularPostProcessors:记录通过硬编码方式注册的BeanFactoryPostProcessor类型的处理器。

registryPostProcessorBeans:记录通过配置方式注册的BeanDefinitionRegistryPostProcessor类型的处理器。

  1. 对以上所记录的List中的后处理器进行统一调用BeanFactoryPostProcessor的postProcessBeanFactory方法。

  2. 对beanFactoryPostProcessors中非BeanDefinitionRegistryPostProcessor类型的后处理器进行统一的BeanFactoryPostProcessor的postProcessBeanFactory方法调用。

  3. 普通beanFactory处理。BeanDefinitionRegistryPostProcessor只对BeanDefinitionRegistry类型的ConfigurableListableBeanFactory有效,所以如果判断所示的beanFactory并不是BeanDefinitionRegistry,那么便可以忽略BeanDefinitionRegistryPostProcessor,而直接处理BeanFactoryPostProcessor,当然获取的方式与上面的获取类似。

对于硬编码方式手动添加的后处理器是不需要做任何排序的,但是在配置文件中读取的处理器,Spring并不保证读取的顺序。所以,为了保证用户的调用顺序的要求,Spring对于后处理器的调用支持按照PriorityOrdered或者Ordered的顺序调用。

registerBeanPostProcessors(beanFactory);

来探索下BeanPostProcessor,但是这里并不是调用,而是注册。真正的调用其实是在bean的实例化阶段进行的。这是一个很重要的步骤,也是很多功能BeanFactory不支持的重要原因。Spring中大部分功能都是通过后处理器的方式进行扩展的,这是Spring框架的一个特性,但是在BeanFactory中其实并没有实现后处理器的自动注册,所以在调用的时候如果没有进行手动注册其实是不能使用的。

对于BeanPostProcessor的处理与BeanFactoryPostProcessor的处理极为相似,但是似乎又有些不一样的地方。经过反复的对比发现,对于BeanFactoryPostProcessor的处理要区分两种情况,一种方式是通过硬编码方式的处理,另一种是通过配置文件方式的处理。那么为什么在BeanPostProcessor的处理中只考虑了配置文件的方式而不考虑硬编码的方式呢?对于BeanFactoryPostProcessor的处理,不但要实现注册功能,而且还要实现对后处理器的激活操作,所以需要载入配置中的定义,并进行激活;而对于BeanPostProcessor并不需要马上调用,再说,硬编码的方式实现的功能是将后处理器提取并调用,这里并不需要调用,当然不需要考虑硬编码的方式了,这里的功能只需要将配置文件的BeanPostProcessor提取出来并注册进入beanFactory就可以了。

对于beanFactory的注册,也不是直接注册就可以的。在Spring中支持对于BeanPostProcessor的排序,比如根据PriorityOrdered进行排序、根据Ordered进行排序或者无序,而Spring在BeanPostProcessor的激活顺序的时候也会考虑对于顺序的问题而先进行排序。

initMessageSource();

在initMessageSource中的方法主要功能是提取配置中定义的messageSource,并将其记录在Spring的容器中,也就是AbstractApplicationContext中。当然,如果用户未设置资源文件的话,Spring中也提供了默认的配置DelegatingMessageSource。

在initMessageSource中获取自定义资源文件的方式为beanFactory.getBean(MESSAGE_ SOURCE_BEAN_NAME, MessageSource.class),在这里Spring使用了硬编码的方式硬性规定了子定义资源文件必须为message,否则便会获取不到自定义资源配置。

protected void initMessageSource() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //messageSource
        if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
            //如果在配置中已经配置了messageSource,那么将messageSource提取并记录在this.messageSource中
            this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
            if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
                HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
                if (hms.getParentMessageSource() == null) {
                    hms.setParentMessageSource(getInternalParentMessageSource());
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Using MessageSource [" + this.messageSource + "]");
            }
        }
        else {
            //如果用户并没有定义配置文件,那么使用临时的DelegatingMessageSource以便于作为调用getMessage方法的返回。
            DelegatingMessageSource dms = new DelegatingMessageSource();
            dms.setParentMessageSource(getInternalParentMessageSource());
            this.messageSource = dms;
            beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
                        "': using default [" + this.messageSource + "]");
            }
        }
    }

initApplicationEventMulticaster();

initApplicationEventMulticaster的方式比较简单,无非考虑两种情况:

如果用户自定义了事件广播器,那么使用用户自定义的事件广播器。

如果用户没有自定义事件广播器,那么使用默认的ApplicationEventMulticaster。

	protected void initApplicationEventMulticaster() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isDebugEnabled()) {
				logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isDebugEnabled()) {
				logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
						APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
						"': using default [" + this.applicationEventMulticaster + "]");
			}
		}
	}

按照之前介绍的顺序及逻辑,作为广播器,一定是用于存放监听器并在合适的时候调用监听器,那么进入默认的广播器实现SimpleApplicationEventMulticaster来一探究竟。

@Override
    public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
   ErrorHandler errorHandler = getErrorHandler();
   if (errorHandler != null) {
      try {
         listener.onApplicationEvent(event);
      }
      catch (Throwable err) {
         errorHandler.handleError(err);
      }
   }
   else {
      listener.onApplicationEvent(event);
   }
}

可以推断,当产生Spring事件发生的时候会默认使用SimpleApplicationEventMulticaster的multicastEvent来广播事件,遍历所有监听器,并使用监听器中的onApplicationEvent方法来进行监听器的处理。而对于每个监听器来说其实都可以获取到产生的事件,但是是否进行处理则由事件监听器来决定。

registerListeners()

protected void registerListeners() {
        //硬编码方式注册的监听器处理
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }
        //配置文件注册的监听器处理
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }
        
     //广播早期的事件进行广播和事件处理
     // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

finishBeanFactoryInitialization(beanFactory)

finishBeanFactoryInitialization完成BeanFactory的初始化工作,其中包括ConversionService的设置、配置冻结以及非延迟加载的bean的初始化工作。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        //conversionService的bean会被注册
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }
        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }
        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);
        //冻结所有的bean定义,说明注册的bean定义将不被修改或任何进一步的处理。
        beanFactory.freezeConfiguration();
        //初始化剩下的单实例(非惰性的)
        beanFactory.preInstantiateSingletons();
    }

ConversionService的设置,之前我们提到过使用自定义类型转换器从String转换为Date的方式,使用属性编辑器,那么,在Spring中还提供了另一种转换方式:使用Converter。

ApplicationContext实现的默认行为就是在启动时将所有单例bean提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的单例bean。通常情况下这是一件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。而这个实例化的过程就是在finishBeanFactoryInitialization中完成的。其中最重要的一个方法就为:DefaultListableBeanFactory#preInstantiateSingletons
其内部核心方法为:getBean --> doGetBean方法

finishRefresh()

在Spring中还提供了Lifecycle接口,Lifecycle中包含start/stop方法,实现此接口后Spring保证在启动的时候调用其start方法开始生命周期,并在Spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,在启动后一直运行(如对MQ进行轮询等)。而ApplicationContext的初始化最后正是保证了这一功能的实现。

protected void finishRefresh() {
        // Initialize lifecycle processor for this context.
        //lifecycleProcessor的bean会被注册
     initLifecycleProcessor();
        // Propagate refresh to lifecycle processor first.
        getLifecycleProcessor().onRefresh();
        // Publish the final event.
        publishEvent(new ContextRefreshedEvent(this));
        // Participate in LiveBeansView MBean, if active.
        LiveBeansView.registerApplicationContext(this);
    }
  • initLifecycleProcessor

当ApplicationContext启动或停止时,它会通过LifecycleProcessor来与所有声明的bean周期做状态更新,而在LifecycleProcessor的使用前首先需要初始化。

  • onRefresh

启动所有实现了Lifecycle接口的bean。

  • publishEvent

当完成ApplicationContext初始化的时候,要通过Spring中的事件发布机制来发出ContextRefreshedEvent事件,以保证对应的监听器可以做进一步的逻辑处理。

FileSystemXmlApplicationContext

如果配置文件放在文件系统路径下,则优先考虑使用FileSystemXmlApplication实现类

public class FileSystemXmlApplicationTest { 
	public static void main(String[] args) { 
	
	ApplicationContext app = new FileSystemXmlApplicationContext( "file:D:/workspace/workspace-jee/HelloSpring/Spring4-char2/src/com/xgj/master/ioc/beanfactory/bean-plane.xml"); 
	
	Plane plane = (Plane) app.getBean("plane"); 
	
	plane.introduce();
	 } 
 }

WebApplicationContext

WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring应用上下文。Spring专门为此提供一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)方法,即可以从ServletContext中获取WebApplicationContext实例。

由于Web应用比一般的应用拥有更多的特性,因此WebApplicationContext扩展了ApplicationContext。WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_ CONTEXT_ATTRIBUTE,在上下文启动时,WebApplicationContext实例即以此为键放置在ServletContext的属性列表中,因此我们可以直接通过以下语句从Web容器中获取WebApplicationContext:

WebApplicationContext wac = (WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

这正是我们前面所提到的WebApplicationContextUtils工具类getWebApplicationContext (ServletContext sc)方法的内部实现方式。这样Spring的Web应用上下文和Web容器的上下文就可以实现互访,二者实现了融合。

WebApplicationContext初始化

WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实例,也就是说它必须在拥有Web容器的前提下才能完成启动的工作。有过Web开发经验的读者都知道可以在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListener),借助这两者中的任何一个,我们就可以完成启动Spring Web应用上下文的工作。

所有版本的Web容器都可以定义自启动的Servlet,但只有Servlet 2.3及以上版本的Web容器才支持Web容器监听器。有些即使支持Servlet 2.3 的Web服务器,但也不能在Servlet初始化之前启动Web监听器,如Weblogic 8.1、WebSphere 5.x、Oracle OC4J 9.0。

Spring分别提供了用于启动WebApplicationContext的Servlet和Web容器监听器:

org.springframework.web.context.ContextLoaderServlet
org.springframework.web.context.ContextLoaderListener

两者的内部都实现了启动WebApplicationContext实例的逻辑,我们只要根据Web容器的具体情况选择两者之一,并在web.xml中完成配置就可以了。

下面是使用ContextLoaderListener启动WebApplicationContext的具体配置:

<!--①指定配置文件--> 
<context-param>                                                        
     <param-name>contextConfigLocation</param-name>   
     <param-value> 
       /WEB-INF/baobaotao-dao.xml, /WEB-INF/baobaotao-service.xml  
   </param-value> 
</context-param> 
<!--②声明Web容器监听器--> 
<listener>   
     <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class> 
</listener> 

ContextLoaderListener通过Web容器上下文参数contextConfigLocation获取Spring配置文件的位置。用户可以指定多个配置文件,用逗号、空格或冒号分隔均可。对于未带资源类型前缀的配置文件路径,WebApplicationContext默认这些路径相对于Web的部署根路径。当然,我们可以采用带资源类型前缀的路径配置,如"classpath*:/baobaotao-*.xml"和上面的配置是等效的。

如果在不支持容器监听器的低版本Web容器中,我们可采用ContextLoaderServlet完成相同的工作:

<context-param> 
     <param-name>contextConfigLocation</param-name>   
     <param-value>/WEB-INF/baobaotao-dao.xml, /WEB-INF/baobaotao-service.xml </param-value> 
</context-param><!--①声明自动启动的Servlet --> 
<servlet>   
    <servlet-name>springContextLoaderServlet</servlet-name> 
    <servlet-class>org.springframework.web.context.ContextLoaderServlet </servlet-class> 
    <!--②启动顺序--> 
    <load-on-startup>1</load-on-startup> 
</servlet> 

由于WebApplicationContext需要使用日志功能,用户可以将Log4J的配置文件放置到类路径WEB-INF/classes下,这时Log4J引擎即可顺利启动。如果Log4J配置文件放置在其他位置,用户还必须在web.xml指定Log4J配置文件位置。Spring为启用Log4J引擎提供了两个类似于启动WebApplicationContext的实现类:Log4jConfigServlet和Log4jConfigListener,不管采用哪种方式都必须保证能够在装载Spring配置文件前先装载Log4J配置信息。

<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value> 
       /WEB-INF/baobaotao-dao.xml,/WEB-INF/baobaotao-service.xml  
    </param-value> 
</context-param> 
 <!--①指定Log4J配置文件位置--> 
<context-param>                                             
    <param-name>log4jConfigLocation</param-name> 
    <param-value>/WEB-INF/log4j.properties</param-value> 
</context-param> 

<!--②装载Log4J配置文件的自启动Servlet --> 
<servlet> 
    <servlet-name>log4jConfigServlet</servlet-name> 
    <servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class> 
    <load-on-startup>1</load-on-startup> 
</servlet> 
<servlet> 
        <servlet-name> springContextLoaderServlet</servlet-name> 
        <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> 
    <load-on-startup>2</load-on-startup> 
</servlet> 

注意上面我们将log4jConfigServlet的启动顺序号设置为1,而springContextLoaderServlet的顺序号设置为2。这样,前者将先启动,完成装载Log4J配置文件初始化Log4J引擎的工作,紧接着后者再启动。如果使用Web监听器,则必须将Log4jConfigListener放置在ContextLoaderListener的前面。采用以上的配置方式Spring将自动使用XmlWebApplicationContext启动Spring容器,即通过XML文件为Spring容器提供Bean的配置信息。

ApplicationContext事件机制

ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。

如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。这种事件机制都必须需要程序显示的触发。

ApplicationListener

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	void onApplicationEvent(E event);
}

在开发时有时候需要在整个应用开始运行时执行一些特定代码,比如初始化环境,准备测试数据、加载一些数据到内存等等。

在spring中可以通过ApplicationListener来实现相关的功能,加载完成后触发contextrefreshedevent事件(上下文件刷新事件)

但是这个时候,会存在一个问题,在web 项目中(spring mvc),系统会存在两个容器,一个是root application context ,另一个就是我们自己的 projectName-servlet context(作为root application context的子容器)。

这种情况下,就会造成onApplicationEvent方法被执行两次。为了避免上面提到的问题,我们可以只在root application context初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理

public class Init implements ApplicationListener<ContextRefreshedEvent>{
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(event.getApplicationContext().getParent() == null){//root application context 没有parent
            //TODO 这里写下将要初始化的内容
        }
    }

}

ApplicationEvent

public abstract class ApplicationEvent extends EventObject {

	private static final long serialVersionUID = 7099057708183571937L;
	private final long timestamp;
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
	public final long getTimestamp() {
		return this.timestamp;
	}

}

Spring内置事件

ContextRefreshedEvent

ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用

ContextStartedEvent

当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。

ContextStoppedEvent

当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。

ContextClosedEvent

当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。

RequestHandledEvent

这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。

自定义事件

可以自定义事件,然后做完业务处理后手动发出。同上集成某个监听接口,接收到事件后进行业务处理

/**
 * 自定义一个事件
 *
 */
public class DemoEvent extends ApplicationEvent{
    
    private String msg;
    
    private String email;

    public DemoEvent(Object source,String msg,String email) {
        super(source);
        this.msg=msg;
        this.email=email;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
    
}

/**
 * 定义一个事件监听类
 *
 */
@Component
public class DemoEventListener implements ApplicationListener<DemoEvent>{

    //使用注解@Async支持 这样不仅可以支持通过调用,也支持异步调用,非常的灵活,
    @Async
    @Override
    public void onApplicationEvent(DemoEvent event) {
        System.out.println("注册成功,发送确认邮件为:" + event.getEmail()+",消息摘要为:"+event.getMsg());  
    }

}

/**
 * 事件发布类
 * @author mingge
 *
 */
@Component
public class DemoEventPublisher {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void pushlish(String msg,String mail){
        applicationContext.publishEvent(new DemoEvent(this, msg,mail));
    }
    
}

/**
 * 事件测试类
 *
 */
public class EventTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(EventConfig.class);
        DemoEventPublisher demoEventPublisher=context.getBean(DemoEventPublisher.class);
        demoEventPublisher.pushlish("张三1","565792147@qq.com");
        demoEventPublisher.pushlish("张三2","565792147@qq.com");
        demoEventPublisher.pushlish("张三3","565792147@qq.com");
        demoEventPublisher.pushlish("张三4","565792147@qq.com");
        demoEventPublisher.pushlish("张三5","565792147@qq.com");
        context.close();
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值