likes
comments
collection
share

5、Mini-spring 应用上下文ApplicationContext

作者站长头像
站长
· 阅读数 12

前言:

上一篇文章,我们分析实现了BeanFactoryPostProcessorBeanPostProcessor两个重要级接口,这两个接口都是spring提供的容器扩容机制,他们的区别是BeanFactoryPostProcessor接口是在bean实例化之前修改beanDefinition信息,BeanPostProcessor是在bean实例化之后对bean做修改,而且BeanPostProcessor是后面实现AOP的关键。这一节我们将引入ApplicationContextApplicationContext能自动识别BeanFactoryProcessorBeanPostProcessor,这样就可以通过直接在xml中进行配置,不用手动添加到BeanFactory了。

ApplicationContext:

ApplicationContext是Spring中较BeanFactory更为先进的IOC容器,ApplicationContext除了拥有BeanFactory的所有功能外,还支持特殊类型bean如上一节中的BeanFactoryPostProcessor和BeanPostProcessor的自动识别、资源加载、容器事件和监听器、国际化支持、单例bean自动初始化等。

BeanFactory是spring的基础设施,面向spring自身;而ApplicationContext面向spring的使用者,应用场合使用ApplicationContext。

具体实现:

我们先来看几个新增的几个应用上下文相关接口类的关系。

5、Mini-spring 应用上下文ApplicationContext

可以看到最上层的是ApplicationContext,中间多个抽象类和接口,最下层的是ClassPathXmlApplicationContext,Xml文件的应用上下文。我们就是最终通过ClassPathXmlApplicationContext来完成我们之前所有的一系列操作,加载xml文件中的内容,并且对bean实例化前后进行扩展操作。

直接从测试Demo看,我们之前所有的先创建beanFactory对象,再加载xml文件,然后手动去创建BeanFactoryPostProcessor/BeanPostProcessor实例,再调用各自的方法这些步骤,都省去了,简化成了一行代码就是ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");

测试:

public class ApplicationContextTest {

   @Test
   public void testApplicationContext() throws Exception {
      ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");

      Person person = applicationContext.getBean("person", Person.class);
      System.out.println(person);
      //name属性在CustomBeanFactoryPostProcessor中被修改为ivy
      assertThat(person.getName()).isEqualTo("ivy");

      Car car = applicationContext.getBean("car", Car.class);
      System.out.println(car);
      //brand属性在CustomerBeanPostProcessor中被修改为lamborghini
      assertThat(car.getBrand()).isEqualTo("lamborghini");
   }
}

来看下ClassPathXmlApplicationContext实例化做了什么事情,可以看到传递完xml文件参数后,接着调用了另一个构造器,配置了对应的文件,然后调用了refresh()方法

/**
 * xml文件的应用上下文
 *
 */
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {

   private String[] configLocations;

   /**
    * 从xml文件加载BeanDefinition,并且自动刷新上下文
    *
    * @param configLocation xml配置文件
    * @throws BeansException 应用上下文创建失败
    */
   public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
      this(new String[]{configLocation});
   }

   /**
    * 从xml文件加载BeanDefinition,并且自动刷新上下文
    *
    * @param configLocations xml配置文件
    * @throws BeansException 应用上下文创建失败
    */
   public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
      this.configLocations = configLocations;
      refresh();
   }

   protected String[] getConfigLocations() {
      return this.configLocations;
   }
}

refresh()方法:

我们重点分析refresh()方法。

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

   @Override
   public void refresh() throws BeansException {
      //创建BeanFactory,并加载BeanDefinition
      refreshBeanFactory();
      ConfigurableListableBeanFactory beanFactory = getBeanFactory();

      //在bean实例化之前,执行BeanFactoryPostProcessor
      invokeBeanFactoryPostProcessors(beanFactory);

      //BeanPostProcessor需要提前与其他bean实例化之前注册
      registerBeanPostProcessors(beanFactory);

      //提前实例化单例bean
      beanFactory.preInstantiateSingletons();
   }

   /**
    * 创建BeanFactory,并加载BeanDefinition
    *
    * @throws BeansException
    */
   protected abstract void refreshBeanFactory() throws BeansException;

   /**
    * 在bean实例化之前,执行BeanFactoryPostProcessor
    *
    * @param beanFactory
    */
   protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
      Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
      for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
         beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
      }
   }

   /**
    * 注册BeanPostProcessor
    *
    * @param beanFactory
    */
   protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
      Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
      for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
         beanFactory.addBeanPostProcessor(beanPostProcessor);
      }
   }

   @Override
   public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
      return getBeanFactory().getBean(name, requiredType);
   }

   @Override
   public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
      return getBeanFactory().getBeansOfType(type);
   }

   public Object getBean(String name) throws BeansException {
      return getBeanFactory().getBean(name);
   }

   public String[] getBeanDefinitionNames() {
      return getBeanFactory().getBeanDefinitionNames();
   }

   public abstract ConfigurableListableBeanFactory getBeanFactory();
}

refreshBeanFactory()

refresh()方法干的第一件事情,就是创建BeanFactory工厂,然后加载BeanDefinition,我们看下这个方法的具体内部实现。首先创建了个DefaultListableBeanFactory,接着将这个beanFactory作为参数,然后调用loadBeanDefinitions(beanFactory);

protected final void refreshBeanFactory() throws BeansException {
   DefaultListableBeanFactory beanFactory = createBeanFactory();
   loadBeanDefinitions(beanFactory);
   this.beanFactory = beanFactory;
}

接着看loadBeanDefinitions(beanFactory),这里需要着重理解下,还记得上上节,我们在实现资源加载器加载xml的时候是怎么调用的吗,可以去翻一翻第三节中的内容,当时我们是直接传入beanFactory。

5、Mini-spring 应用上下文ApplicationContext

传入beanFactory后,调用父类另一个构造器,将默认的查找资源DefaultResourceLoader传入。、

5、Mini-spring 应用上下文ApplicationContext

回到这里,现在的调用,是将this作为第二个参数,this是什么,this就是当前对象AbstractXmlApplicationContext。为什么这里可以直接将this作为资源加载器ResourceLoader呢,其实是因为抽象的应用上下文也同时继承了DefaultResourceLoader。这里的类关系比较复杂,所以在自己看的时候要仔细分析各个类的职责。

5、Mini-spring 应用上下文ApplicationContext

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory, this);
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      beanDefinitionReader.loadBeanDefinitions(configLocations);
   }
}

所有这里在最后获取ResourceLoader的时候其实就是AbstractBeanDefinitionReader具有的属性,之前放进去的AbstractXmlApplicationContext,也就是最终面向用户的ClassPathXmlApplicationContext,其本质其实还是DefaultResourceLoader

5、Mini-spring 应用上下文ApplicationContext

invokeBeanFactoryPostProcessors(beanFactory)

创建加载完beanFactory和BeanDefinition信息后,接着调用invokeBeanFactoryPostProcessors,invoke的意思是借助。这一步是在bean实例化之前执行BeanFactoryPostProcessor。

/**
 * 在bean实例化之前,执行BeanFactoryPostProcessor
 *
 * @param beanFactory
 */
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
   for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
      beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
   }
}

可以看到方法内部就是通过getbeansOfType()方法先获取BeanFactoryPostProcessor,然后和我们前面章节一样的调用postProcessBeanFactory。其实后面的BeanPostProcessor也是一样的操作,所以我们重点看getbeansOfType()方法是如何自动识别这两个扩展类的。

这里可以debug进去,来到方法的具体实现,可以看到方法内部根据我们传入的参数type的Class类型以及已经存在beanFactory中的beanDefinition信息去匹配判断。

这里说下isAssignabkeFrom()方法,用来判断一个类Class1是否与另一个类Class2相同,或者Class1是否是Class2的超类或接口;他的格式是Class1.isAssignableFrom(Class2);Class1是父,Class2是子,父-子。同时,我们常用的还有一个instanceof关键字,instanceof 用来判断一个对象实例obj是否是另一个类或接口的实例,格式是obj instanceof ClassName第一个参数是实例对象,第二个参数是类名;

接着说回代码,isAssignableFrom()方法找到BeanDefinition中的所有的BeanFactoryPostProcessor,调用getBean,获取到真正的Bean,然后调用postProcessBeanFactory方法,在实例化其他bean之前,修改beanDefinition的信息。

@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
   Map<String, T> result = new HashMap<>();
   beanDefinitionMap.forEach((beanName, beanDefinition) -> {
      Class beanClass = beanDefinition.getBeanClass();
      if (type.isAssignableFrom(beanClass)) {
         T bean = (T) getBean(beanName);
         result.put(beanName, bean);
      }
   });
   return result;
}

registerBeanPostProcessors(beanFactory)

执行完BeanFactoryPostProcessor后,接着就是注册BeanPostProcessor,这一步要在其他bean实例化之前注册,这样在其他bean调用getBean方法实例化bean之后,才能执行对应的前置处理和后置处理。

还是getBeansOfType()方法,不同的是,传递的参数变成了BeanPostProcessor.class。这里不再赘述。

/**
 * 注册BeanPostProcessor
 *
 * @param beanFactory
 */
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
   for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
      beanFactory.addBeanPostProcessor(beanPostProcessor);
   }
}

beanFactory.preInstantiateSingletons()

最后一步是对剩下的bean进行获取。因为BeanFactoryPostProcessor和BeanPostProcessor已经注册,在获取的时候不会重复获取,后续的内容就与上一节的一致了。

@Override
public void preInstantiateSingletons() throws BeansException {
   beanDefinitionMap.keySet().forEach(this::getBean);
}

@Override
public Object getBean(String name) throws BeansException {
   Object bean = getSingleton(name);
   if (bean != null) {
      return bean;
   }

   BeanDefinition beanDefinition = getBeanDefinition(name);
   return createBean(name, beanDefinition);
}

从bean的角度看,目前的生命周期如下:

5、Mini-spring 应用上下文ApplicationContext

总结:

这一节我们完成了ApplicationContext,Spring中面向使用者的IOC容器,他除了具有BeanFactory的所有功能,同时具有更多面向应用层面的功能。通过这几节的学习,我们知道了BeanFactory和ApplicationContext都支持BeanFactoryPostProcessor和BeanPostProcessor,但是BeanFactory需要我们手动注册,而ApplicationContext是自动注册。

《精通Spring4.x 企业应用开发实战》中的解释。

5、Mini-spring 应用上下文ApplicationContext