Spring中ApplicationEvent、ApplicationListener、InitializingBean、FactoryBean

一、ApplicationEvent&ApplicationListener

   Spring 3.0中提供了很多类似*Aware的类,其中ApplicationContextAware接口可以实现我们在初始化bean的时候给bean注入ApplicationConxt(Spring上下文对象)对象。ApplicationContextAware接口提供了publishEvent方法,实现了Observe(观察者)设计模式的传播机制,实现了对bean的传播。通过ApplicationContextAware我们可以把系统中所有ApplicationEvent传播给系统中所有的ApplicationListener。因此,我们只需要构造好我们自己的ApplicationEvent和ApplicationListener,就可以在系统中实现相应的监听器。

   下面以增加学生的示例来演示如何构造Spring的监听器,StudentAddEvent是监听的事件对象,StudentAddListener是事件的监听器(负责处理接收到的监听事件),StudentAddBean负责触发StudentAddEvent事件。具体步骤如下:

1、定义StudentAddEvent监听事件

package com.trs.spring.event;  
  
import org.springframework.context.ApplicationEvent;  
  
/** 
 * 增加学生的监听事件 
 */  
public class StudentAddEvent extends ApplicationEvent {   
    private static final long serialVersionUID = 20L;   
    private String m_sStudentName;    
    public StudentAddEvent(Object source, String _sStudentName) {  
        super(source);  
        this.m_sStudentName = _sStudentName;  
    }  
    public String getStudentName() {  
        return m_sStudentName;  
    }  
  
}  
2、定义StudentAddListener监听器

package com.trs.spring.event;  
  
import org.springframework.context.ApplicationEvent;  
import org.springframework.context.ApplicationListener;  
  
public class StudentAddListener implements ApplicationListener {  
  
    
    public void onApplicationEvent(ApplicationEvent _event) {    
        if (!(_event instanceof StudentAddEvent)) {  
            return;  
        }    
        StudentAddEvent studentAddEvent = (StudentAddEvent) _event;  
        System.out.println("增加了学生:::" + studentAddEvent.getStudentName());  
    }  
  
}  
3、定义StudentAddBean触发StudentAddEvent事件

<span style="font-size: 18px;">package com.trs.spring.event;  
  
import org.springframework.beans.BeansException;  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.ApplicationContextAware;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  
public class StudentAddBean implements ApplicationContextAware {  
    private ApplicationContext m_applicationContext = null;  
   </span><span style="font-size:18px;"> public void setApplicationContext(ApplicationContext _applicationContext)  
            throws BeansException {  
        this.m_applicationContext = _applicationContext;  
    }  
    public void addStudent(String _sStudentName) {  
        StudentAddEvent aStudentEvent = new StudentAddEvent(  
                m_applicationContext, _sStudentName);  
        m_applicationContext.publishEvent(aStudentEvent);  
    }  
    public static void main(String[] args) {  
        String[] xmlCo</span><span style="font-size: 18px;">nfig = new String[] { "applicationContext.xml" };  
        ApplicationContext context = new ClassPathXmlApplicationContext(  
                xmlConfig);  
        StudentAddBean studentBean = (StudentAddBean) context  
                .getBean("StudentAddBean");  
        studentBean.addStudent("我是第一个学生");  
        studentBean.addStudent("第二个学生已经添加");  
    }  
}  </span>
4、applicationContext.xml配置文件
<bean id="StudentAddBean" class="com.trs.spring.event.StudentAddBean"></bean>
<bean id="StudentAddListener" class="com.trs.spring.event.StudentAddListener"></bean>

5、说明

ApplicationContext在运行期会自动检测到所有实现了ApplicationListener的bean对象,并将其作为事件接收对象。当ApplicationContext的publishEvent方法被触发时,每个实现了ApplicationListener接口的bean都会收到ApplicationEvent对象,每个ApplicationListener可根据事件类型只接收处理自己感兴趣的事件,比如上面的StudentAddListener只接收StudentAddEvent事件。

6、执行StudentAddBean的main函数,结果如下:
增加了学生:::我是第一个学生
增加了学生:::第二个学生已经添加
二、InitializingBean

InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。
测试程序如下:

import org.springframework.beans.factory.InitializingBean;
public class TestInitializingBean implements InitializingBean{

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("ceshi InitializingBean");        
    }
    public void testInit(){
        System.out.println("ceshi init-method");        
    }
}
配置文件:

<bean id="testInitializingBean" class="com.TestInitializingBean" ></bean>
main函数如下:

public class Main {
    public static void main(String[] args){
        ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/java/com/beans.xml");
    }
}
运行main函数,打印如下结果:
ceshi InitializingBean  
这说明在spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。

思考一、实现InitializingBean接口与在配置文件中指定init-method有什么不同?

修改配置文件:

<bean id="testInitializingBean" class="com.TestInitializingBean" init-method="testInit"></bean>
运行main函数,打印如下结果:
ceshi InitializingBean
ceshi init-method
由结果可看出,在spring初始化bean的时候,如果该bean是实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertiesSet方法,然后在调用init-method中指定的方法。

思考二、这方式在spring中是怎么实现的?

通过查看spring的加载bean的源码类(AbstractAutowireCapableBeanFactory)可看出其中奥妙
AbstractAutowireCapableBeanFactory类中的invokeInitMethods讲解的非常清楚,源码如下:

<span style="font-size:18px;">protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
            throws Throwable {
		//判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        public Object run() throws Exception {
                            //直接调用afterPropertiesSet
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    },getAccessControlContext());
                } catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }                
            else {
                //直接调用afterPropertiesSet
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
        if (mbd != null) {
            String initMethodName = mbd.getInitMethodName();
            //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
            if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                //进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }</span>
总结:
1:spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用
2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖
3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

三、FactoryBean

Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean,这两种Bean都被容器管理,但工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该FactoryBean的getObject方法所返回的对象。在Spring框架内部,有很多地方有FactoryBean的实现类,它们在很多应用如(Spring的AOP、ORM、事务管理)及与其它第三框架(ehCache)集成时都有体现,下面简单分析FactoryBean的用法。 

以下SimpleFactoryBean类实现了FactoryBean接口中的三个方法。 并将该类配置在XML中。

<span style="font-size:18px;">public class SimpleFactoryBean implements FactoryBean {  
        private boolean flag;  
        public Object getObject() throws Exception {  
            if (flag) {  
                return new Date();  
            }  
            return new String("false");  
        }  
        @SuppressWarnings("unchecked")  
        public Class getObjectType() {  
            return flag ? Date.class : String.class;  
        } 
        public boolean isSingleton() {  
            return false;  
        }  
        public void setFlag(boolean flag) {  
            this.flag = flag;  
        }  
    }  </span>
<span style="font-size:18px;">     <bean id="factoryBeanOne" class="com.study.demo.factorybean.SimpleFactoryBean" >  
        <property name="flag">  
            <value>true</value>  
        </property>  
    </bean>  
    <bean id="factoryBeanTwo" class="com.study.demo.factorybean.SimpleFactoryBean" >  
        <property name="flag">  
            <value>false</value>  
        </property>  
   </bean></span>
<span style="font-size:18px;">public class MainTest {  
        public static void main(String[] args) {  
            Resource res = new ClassPathResource("bean.xml");  
            BeanFactory factory = new XmlBeanFactory(res);  
            System.out.println(factory.getBean("factoryBeanOne").getClass());  
            System.out.println(factory.getBean("factoryBeanTwo").getClass());  
        }  
    }  </span>
通过简单的测试可知,该类输出如下: 
class java.util.Date 
class java.lang.String 
也就是说,容器通过getBean方法返回的不是FactoryBean本身,而是FactoryBean实现类中getObject()方法所返回的对象。







  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值