一、spring容器中的aware接口介紹
Spring中提供了各種Aware接口,比較常見的如BeanFactoryAware,BeanNameAware,ApplicationContextAware,BeanClassLoaderAware等,方便從上下文中獲取當前的運行環境。我們先從使用的角度來說明aware接口的使用方式,舉例如我們想得到當前的BeanFactory,我們可以讓我們的實現類繼承BeanFactoryAware接口,然后通過接口注入的方式得到當前容器中的BeanFactory:
public class Fruit implements BeanFactoryAware { private BeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
我們的Fruit類實現了aware接口,如果我們直接在應用中new一個Fruit的對象,當然是拿不到beanFactory變量的,我們必須在spring的配置文件中聲明我們的fruit對象才行,也就是說fruit對象必須交給容器進行管理,容器幫你把各種aware接口中想要注入的對象設置到bean中。具體看容器管理aware接口的代碼實現,代碼在AbstractAutowireCapableBeanFactory的initializeBean方法中:
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) { // 判斷對象實現的接口類型,處理特定的三種接口類型:BeanNameAware、BeanClassLoaderAware和BeanFactoryAware。 if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } if (bean instanceof BeanClassLoaderAware) { ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader()); } if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(this); } // 開始Bean初始化前處理、初始化、初始化后處理 Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
可以看出來,aware接口的各種處理是在屬性設置完成之后、bean初始化之前完成的。顯然的,如果我們直接new出來一個bean,這些框架性的特性是沒有使用到的。除了BeanFactoryAware、BeanNameAware、BeanClassLoaderAware之外的那些aware接口,比如ApplicationContextAware,再比如Webx中的自定義aware接口,它們又是怎么做到接口注入的呢?原來在應用中創建上下文容器時會注冊一個BeanPostProcessor------ApplicationContextAwareProcessor,在這個類里面進行了context的注入,這樣我們就能能夠拿到bean中的context對象:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 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); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } return bean; }
那么容器是在什么時候把ApplicationContextAwareProcessor的對象注冊到context的BeanPostProcessor列表中的呢,奧秘在org.springframework.context.support.AbstractApplicationContext.prepareBeanFactory(ConfigurableListableBeanFactory)方法中:
// Tell the internal bean factory to use the context's class loader. beanFactory.setBeanClassLoader(getClassLoader()); // Populate the bean factory with context-specific resource editors. beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this)); // Configure the bean factory with context callbacks. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); //在這里注冊了我們想要的BeanPostProcessor beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
二、了解webx中實現的aware接口
我們了解了aware的實現原理,我們就可以自己來實現自己的aware接口了。Webx就是這么做的(webx本身作為一個容器,本身注冊了各類aware接口的BeanPostProcessor),比如我們想要感知到當前環境是否是生產模式,我們只需要實現ProductionModeAware接口就能夠獲得生產模式的值了。我們使用aware接口感覺很神奇很簡便,原因是很多工作框架已經幫我們做了。webx中使用ProductionModeAwarePostProcessor這個BeanPostProcessor來進行生產模式的注入。在postProcessBeforeInitialization方法中:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ProductionModeAware) { ((ProductionModeAware) bean).setProductionMode(configuration.isProductionMode()); } return bean; }
所以任何實現了ProductionModeAware接口的類,在webx容器中都能夠獲得生產模式的取值。
三、自定義aware接口實現
了解了個中原理,那么自定義aware接口實現起來並不復雜。我們只需要2步操作:1.實現我們aware接口的postprocessor,並在容器中注冊;2.bean實體類集成我們自定義的aware接口並實現。代碼如下:Aware接口比較簡單,就做一件事情,把Apple對象注入。
public interface AppleAware { void setApple(Apple a); }
我們的BeanPostProcessor會檢查是否是AppleAware接口,因為注冊到容器的BeanPostProcessor會對每一個bean都做一次掃描:
public class AppleAwarePostProcessor implements BeanPostProcessor { private Apple a; public AppleAwarePostProcessor(Apple a) { this.a = a; } /* (non-Javadoc) * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String) */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof AppleAware) { ((AppleAware) bean).setApple(a); } return bean; } /* (non-Javadoc) * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String) */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub return bean; } }
實體類Market實現了AppleAware接口,能夠得到Apple對象的注入:
public class Market implements AppleAware { private Apple a; @Override public void setApple(Apple a) { this.a = a; } public String getName() { return a.getName(); } }
最后是我們的測試類,一定要把我們的BeanPostProcessor加入到當前容器中,這一點非常重要:
public class TestAware { public static void main(String args[]) { ConfigurableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); BeanPostProcessor bpp = new AppleAwarePostProcessor((Apple)beanFactory.getBean("apple")); // 工廠對象中加入我們自定義的BeanPostProcessor beanFactory.addBeanPostProcessor(bpp); Market market = (Market) beanFactory.getBean("market"); System.out.println(market.getName()); } }
四、