一、InitializingBean接口说明
InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。如下源码,从方法名afterPropertiesSet也可以清楚的理解该方法是在属性设置后才调用的。
package org.springframework.beans.factory; /** * Interface to be implemented by beans that need to react once all their * properties have been set by a BeanFactory: for example, to perform custom * initialization, or merely to check that all mandatory properties have been set. * * <p>An alternative to implementing InitializingBean is specifying a custom * init-method, for example in an XML bean definition. * For a list of all bean lifecycle methods, see the BeanFactory javadocs. * * @author Rod Johnson * @see BeanNameAware * @see BeanFactoryAware * @see BeanFactory * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName * @see org.springframework.context.ApplicationContextAware */ public interface InitializingBean { /** * Invoked by a BeanFactory after it has set all bean properties supplied * (and satisfied BeanFactoryAware and ApplicationContextAware). * <p>This method allows the bean instance to perform initialization only * possible when all bean properties have been set and to throw an * exception in the event of misconfiguration. * @throws Exception in the event of misconfiguration (such * as failure to set an essential property) or if initialization fails. */ void afterPropertiesSet() throws Exception; }
二、源码分析接口应用
通过查看spring的加载bean的源码类(AbstractAutowireCapableBeanFactory)可以看到:
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) { //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method String initMethodName = mbd.getInitMethodName(); if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { //反射调用init-method方法 invokeCustomInitMethod(beanName, bean, mbd); } } }
分析代码可以了解:
1. spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用
2. 实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖
3. 如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。
三、接口应用
InitializingBean接口在spring框架中本身就很多应用,这就不多说了。我们在实际应用中如何使用该接口呢?
应用一:
1、使用InitializingBean接口处理一个配置文件:
import java.io.File; import java.io.FileInputStream; import java.util.Properties; import org.springframework.beans.factory.InitializingBean; public class ConfigBean implements InitializingBean{ //微信公众号配置文件 private String configFile; private String appid; private String appsecret; public String getConfigFile() { return configFile; } public void setConfigFile(String configFile) { this.configFile = configFile; } public void afterPropertiesSet() throws Exception { if(configFile!=null){ File cf = new File(configFile); if(cf.exists()){ Properties pro = new Properties(); pro.load(new FileInputStream(cf)); appid = pro.getProperty("wechat.appid"); appsecret = pro.getProperty("wechat.appsecret"); } } System.out.println(appid); System.out.println(appsecret); } }
2、配置
spring配置文件:
<bean id="configBean" class="com.ConfigBean">
<property name="configFile" value="d:/wechat.properties"></property>
</bean>
wechat.properties配置文件
wechat.appid=wxappid
wechat.appsecret=wxappsecret
3、测试
public static void main(String[] args) throws Exception { String config = Test.class.getPackage().getName().replace('.', '/') + "/bean.xml"; ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config); context.start(); }
应用二:
在我们将一个Bean交给Spring管理的时候,有时候我们的Bean中有某个属性需要注入,但是又不能通过一般的方式注入,什么意思呢?举个栗子:首先我们有个Service,在该Service中有一个属性
,但是该属性不支持Spring注入,只能通过Build或者new的方式创建(比如StringBuffer之类的),但是我们想在Spring配置Bean的时候一起将该属性注入进来,这时候该怎么办呢?这时候可以通
过实现InitializingBean接口来解决!
@Service public class DemoService implements InitializingBean{ private StringBuffer stringBuffer; @Override public void afterPropertiesSet() throws Exception { stringBuffer = new StringBuffer(); } }
上面的列子实现了InitializingBean接口并实现其afterPropertiesSet方法,通过这种方式就可以实现一些比较特殊的注入,当然也可以在afterPropertiesSet方法中添加一些其他逻辑来控制创建的对象。当然除了InitializingBean接口,还有一个类似的接口:DisposableBean ,该接口的作用是在对象销毁时调用。
原理:
首先说说spring的IOC容器初始化过程,首先Spring会定位BeanDefinition资源文件,然后会一个一个的去加载所有BeanDefinition,这里的BeanDefinition就是指的Bean的资源文件,即:在XML中配置的Bean和通过注解装配的Bean,在加载完所有BeanDefinition之后,会将这些BeanDefinition注册到一个HashMap中。到此spring的IOC初始化完成,那么依赖注入发生在哪里呢?在用户第一次向IOC容器索要Bean时才开始依赖注入过程(也可以通过配置lazy-init属性让容器初始化的时候就对Bean预实例化)那究竟afterPropertiesSet()方法的调用是在哪个时间点呢?通过查看该方法上的注释:
Invoked by a BeanFactory after it has set all bean properties supplied (and satisfied BeanFactoryAware and ApplicationContextAware).
转载自: