網頁

2019/12/17

Spring InitializingBean

當Bean在所有的屬性被BeanFactory設置後需要進行一些客制化的調整,則Bean可實作InitializingBean介面來客制Bean的初始化。


InitializingBean介面需要實作方法為afterPropertiesSet()。此方法在BeanFactory把Bean所有的屬性值設置好後會被呼叫。

Spring的Bean實例是透過BeanFactory的實作類AbstractBeanFactory產生,
經由AbstractBeanFactory.getBean()
AbstractBeanFactory.doGetBean()
AbstractAutowireCapableBeanFactory.createBean()
AbstractAutowireCapableBeanFactory.doCreateBean()
AbstractAutowireCapableBeanFactory.initializeBean()
AbstractAutowireCapableBeanFactory.invokeInitMethods()等方法。

InitializingBean.afterPropertiesSet()是在AbstractAutowireCapableBeanFactory.invokeInitMethods()中被調用的。

以下節錄自AbstractAutowireCapableBeanFactory.invokeInitMethods()原始碼。

AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
...

    /**
     * Give a bean a chance to react now all its properties are set,
     * and a chance to know about its owning bean factory (this object).
     * This means checking whether the bean implements InitializingBean or defines
     * a custom init method, and invoking the necessary callback(s) if it does.
     * @param beanName the bean name in the factory (for debugging purposes)
     * @param bean the new bean instance we may need to initialize
     * @param mbd the merged bean definition that the bean was created with
     * (can also be {@code null}, if given an existing bean instance)
     * @throws Throwable if thrown by init methods or by the invocation process
     * @see #invokeCustomInitMethod
     */
    protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {

        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isTraceEnabled()) {
                logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction>Object<) () -> {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                ((InitializingBean) bean).afterPropertiesSet(); // <--
            }
        }

        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            if (StringUtils.hasLength(initMethodName) &&
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }
    ...
}

InitializingBean用法如下,只要把該Bean類實作InitializingBean並覆寫afterPropertiesSet()方法即可。

DemoBean

@Component
public class DemoBean implements InitializingBean {
    
    @Override
    public void afterPropertiesSet() {
        // 寫一些自訂的Bean屬性設置後的初始邏輯
    }

}

應用場景包括Spring Bean初始化後一些額外設定值的載入,屬性值的驗證及調整修改等。

不過Spring官方不推薦實作InitializingBean來自訂Bean初始化的邏輯,因為那會與Spring耦合,建議改用JavaEE的@PostConstruct annotation來自訂Bean初始化的邏輯。

DemoBean

@Component
public class DemoBean {
    
    @PostConstruct
    public void init() {
        // 寫一些自訂的Bean屬性設置後的初始邏輯
    }

}

參考:

沒有留言:

張貼留言