spring(1)-ApplicationContextAware詳解

莫小點還有救發表於2021-10-09

ApplicationContextAware詳解

一、進入正題

Aware本義就是"自動的",顧名思義spring給我們自動做了些事情。spring有很多以Aware結尾的類,有EnvironmentAware、ApplicationContextAware、MessageSourceAware等。

這裡我主要講一下ApplicationContextAware。

如下文引用,ApplicationContextAware的文件可以閱讀 Spring Core Technologies 1.4.6 Method InjectionSpring Core Technologies 1.6.2. ApplicationContextAware and BeanNameAware方法注入

概括一下,就是:

在大多數應用程式場景中,容器中的大多數bean都是單例的。當一個單例bean需要與另一個單例bean協作,或者一個非單例bean需要與另一個非單例bean協作時,通常通過將一個bean定義為另一個bean的屬性來處理依賴關係。當bean的生命週期不同時,就會出現問題。假設單例bean A需要使用非單例(原型)bean B,可能是在A的每個方法呼叫上。容器只建立單例bean A一次,因此只有一次機會設定屬性。容器不能在每次需要bean A時都向bean A提供一個新的bean B例項。

一個解決方案是放棄一些控制反轉。您可以通過實現ApplicationContextAware介面,以及在bean A每次需要bean B例項時對容器進行getBean(“B”)呼叫,從而使bean A aware(自動獲取到) 容器。

二、使用

@Service("gatewayService")
public class GatewayServiceImpl implements IGatewayService,ApplicationContextAware {

    Map<ServiceBeanEnum,IGatewayBo> chargeHandlerMap=new HashMap<ServiceBeanEnum,IGatewayBo>();

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

在我們需要使用ApplicationContext的服務中實現ApplicationContextAware介面,系統啟動時就可以自動給我們的服務注入applicationContext物件,我們就可以獲取到ApplicationContext裡的所有資訊了。

三、原理分析

我們都知道spring的入口方法就在AbstractApplicationContext的refresh()方法,我們先去看看refresh().prepareBeanFactory()方法。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        beanFactory.setBeanClassLoader(getClassLoader());
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // 新增ApplicationContextAware的處理器
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
                ...
    }

也就是說,spring在啟動的時候給我們新增了ApplicationContextAwareProcessor這樣一個processor,進去看看它的實現:

@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
    AccessControlContext acc = null;

    if (System.getSecurityManager() != null &&
        (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
         bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
         bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
        acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    }

    if (acc != null) {
        AccessController.doPrivileged((PrivilegedAction)() -> {
            //核心方法,呼叫aware介面方法
            invokeAwareInterfaces(bean);
            return null;
        }, acc);
    } else {
        invokeAwareInterfaces(bean);
    }

    return bean;
}

//實現
private void invokeAwareInterfaces(Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof EnvironmentAware) {
            ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
        }
        if (bean instanceof EmbeddedValueResolverAware) {
            ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
        }
        if (bean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
        }
        if (bean instanceof ApplicationEventPublisherAware) {
            ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
        }
        if (bean instanceof MessageSourceAware) {
            ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
        }
        //針對實現了ApplicationContextAware的介面,spring都將呼叫其setApplicationContext,將applicationContext注入到當前bean物件。
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
    }
}

那ApplicationContextAwareProcessor又是什麼時候呼叫的呢?我們接著往下看,原來refresh()方法中還有個beanFactory.preInstantiateSingletons()方法,裡面有這樣一段程式碼:

拿到所有的beanNames,然後依次判斷是否需要載入,如果是,則呼叫getBean(beanName)方法例項化出來。
// Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                boolean isEagerInit;
                if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                    isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                        @Override
                        public Boolean run() {
                            return ((SmartFactoryBean<?>) factory).isEagerInit();
                        }
                    }, getAccessControlContext());
                }
                else {
                    isEagerInit = (factory instanceof SmartFactoryBean &&
                                   ((SmartFactoryBean<?>) factory).isEagerInit());
                }
                if (isEagerInit) {
                    getBean(beanName);
                }
            }
            else {
                getBean(beanName);
            }
        }
    }

依次檢視getBean() ->doGetBean()->createBean()->doCreateBean()方法:

// Initialize the bean instance.
Object exposedObject = bean;
try {
    populateBean(beanName, mbd, instanceWrapper);
    if (exposedObject != null) {
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
} catch (Throwable ex) {
    if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
        throw (BeanCreationException) ex;
    } else {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
    }
}

檢視一下initializeBean方法:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(() -> {
            this.invokeAwareMethods(beanName, bean);
            return null;
        }, this.getAccessControlContext());
    } else {
        //呼叫setBeanName() 、setBeanClassLoaderAware、setBeanFactoryAware方法
        this.invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
    }

    try {
        //呼叫afterPropertiesSet()方法
        this.invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable var6) {
        throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
    }
    //這裡才是ApplicationContextProcessor的postProcessAfterInitialization()執行入口:
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

原來AbstractAutowireCapableBeanFactory中的inititalBean()方法就是BeanPostProcessor的呼叫處。但是像BeanNameAware、BeanFactoryAware不同,是通過initialBean()中的invokeAwareMethods直接呼叫實現的

四、樣例

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.Map;

/**
 * 通過Spring上下文獲取bean工具類
 *
 * @author moon
 */
public class SpringContextUtils implements ApplicationContextAware {

    /**
     * Spring應用上下文環境
     */
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        SpringContextUtils.initSpringContext(applicationContext);
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        return (T) applicationContext.getBean(name);
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    /**
     * 根據class物件返回IOC容器中其物件和其子類的物件。
     * 未找到則返回空MAP。
     * KEY為BEAN ID或者NAME,VALUE為BEAN例項
     *
     * @param type 需要找的bean型別的CLASS物件
     * @return bean對映
     */
    public static <T> Map<String, T> getBeansByType(Class<T> type) {
        return BeanFactoryUtils.beansOfTypeIncludingAncestors(SpringContextUtils.getApplicationContext(), type);
    }

    /**
     * 初始化ApplicationContext
     *
     * @param applicationContext 上下文
     */
    public static void initSpringContext(ApplicationContext applicationContext) {
        SpringContextUtils.applicationContext = applicationContext;
    }

    /**
     * 獲取業務線(業務線配置在配置檔案中)
     *
     * @return 業務線
     */
    public static String getProjectBusinessLine() {
        if (applicationContext == null) {
            throw new RuntimeException("spring初始化失敗");
        }
        return applicationContext.getEnvironment().getProperty("***.application.businessLine");
    }

    /**
     * 獲取專案名稱(專案名稱配置在配置檔案中)
     *
     * @return 專案名稱
     */
    public static String getProjectName() {
        if (applicationContext == null) {
            throw new RuntimeException("spring初始化失敗");
        }
        return applicationContext.getEnvironment().getProperty("***.application.name");
    }
}

相關文章