likes
comments
collection
share

第8节 Spring源码之 prepareBeanFactory 方法

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

第8节 Spring源码之 prepareBeanFactory 方法

prepareBeanFactory(beanFactory)方法是refresh方法中第三个核心流程,主要是给容器对象 BeanFactory 的属性值赋值操作,通过一系列的addsetignoreregister开头的方法完成赋值操作。

一、prepareBeanFactory方法代码

protected void  prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {

	beanFactory.setBeanClassLoader(getClassLoader());
	// 设置 beanFactory 的表达式解析器
	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
	// 为 beanFactory 增加一个默认的PropertyEditor,这个主要是针对bean的属性值编辑管理的工具类;是Spring预留的一个扩展点
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
	
	// 添加 BeanPostProcessor
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

	// 设置需要忽略的自动装配的接口,为什么要对这些接口进行忽略?因为这些接口的实现是由容器通过set方法进行注入的,
	// 所以在使用autowire进行注入的时候需要将这些接口忽略掉,不是在 invokerMethod 中设置的
	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
   beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
   beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
   beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

   // 设置几个自动装配的特殊规则,当在进行IOC初始化时如果有多个实现,那么就使用指定的对象进行注入,
   // 比如注入多个DataSource数据源时,会取@Primary注解标识对象进行注入
   beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
   beanFactory.registerResolvableDependency(ResourceLoader.class, this);
   beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
   beanFactory.registerResolvableDependency(ApplicationContext.class, this);
   
   // 注册 BeanPostProcessor
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

   // 增加对AspectJ的支持,在java中织入分为三种方式,分为编译器织入,类加载器织入,运行期织入;
   // 编译器织入是指在java编译器,采用特殊的编译器,将切面织入到java类中;
   // 而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;
   // 运行期织入则是采用cglib和jdk进行切面的织入;
   // aspectj提供了两种织入方式,第一种是通过特殊编译器,在编译器,将aspectj语言编写的切面类织入到java类中;
   // 第二种是类加载期织入,就是下面的load time weaving,此处逻辑后面到aop源码再讲
   if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		// Set a temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }

   // 注册默认的系统环境bean到一级缓存中
   if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
   }
   if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
   }
   if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
   }
}

看了该方法的全貌之后,下面对方法中的重点逻辑点进行逐一解释

1. SpEL表达式语言解析器

beanFactory.setBeanExpressionResolver()中设置了SpEL表达式解析器;SpEL使用#{...}作为占位符,所有在大括号内的字符都将被认为是SpELSpEL表达式使用非常丰富,如果感兴趣参考这篇博文:Spring 表达式语言 (SpEL)

<!-- 从系统变量中取出操作系统名称设置给 username 属性 -->
<bean id="user3" class="com.sff.demo.bean.User">
   <property name="username" value="#{ systemProperties['os.name'] }"></property>
</bean>

BeanFactorysetBeanExpressionResolver 方法中注册了表达式语言解析器,那么它在哪里解析的表达式呢?

  • 在Bean的生命周期:Bean属性填充 阶段对表达式进行解析的,也就是 AbstractAutowireCapableBeanFactorydoCreateBean 中的 populateBean 方法中执行的,方法调用过程如下:

第8节 Spring源码之 prepareBeanFactory 方法

evaluateBeanDefinitionString方法中就是调用setBeanExpressionResolver方法中设置的表达式解析器的解析方法进行解析

第8节 Spring源码之 prepareBeanFactory 方法

2. 属性编辑器

beanFactory.addPropertyEditorRegistrar :给 BeanFactory 设置一个默认的PropertyEditor,主要对Bean的属性值编辑管理的工具类,是Spring预留的一个扩展点

  • 先看这样一个业务场景:如何在xml中给一个实体bean注入一个日期类型的属性?
public class User {
   private String username;
   private Date birthday;
}   
<bean id="user4" class="com.sff.demo.bean.User">
   <property name="username" value="Kate"/>
   <property name="birthday" value="2023-7-11"/>
</bean>

Spring注入时是无法将String类型解析成Date类型的。要解决该问题就得自定义一个属性编辑器来转换日期类型

(1)自定义日期属性编辑器

public class DatePropertyEditor extends PropertyEditorSupport {

   private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.CHINA);

   @Override
   public void setAsText(String text) throws IllegalArgumentException {

      if (!StringUtils.hasText(text)) {
         return;
      }

      try {
         setValue(this.dateFormat.parse(text));
      } catch (ParseException ex) {
         throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex);
      }
   }
}

(2)日期属性编辑器注册到Spring容器中

public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
   @Override
   public void registerCustomEditors(PropertyEditorRegistry registry) {
      registry.registerCustomEditor(Date.class, new DatePropertyEditor());
   }
}

(3)xml文件配置

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="user4" class="com.sff.demo.bean.User">
      <property name="username" value="Kate"/>
      <property name="birthday" value="2023-7-11"/>
   </bean>

   <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
      <property name="propertyEditorRegistrars">
         <list>
            <bean class="com.sff.demo.editor.DatePropertyEditorRegistrar"></bean>
         </list>
      </property>
   </bean>

</beans>

(4)运行结果

public class TestBean {

   public static void main(String[] args) {
      testPropertyEditor();
   }

   public static void testPropertyEditor() {
      ApplicationContext cx = new ClassPathXmlApplicationContext("application-property-editor.xml");
      User user = (User) cx.getBean("user4");
      System.out.println(user.getUsername() + "; " + user.getBirthday());
   }
}   

第8节 Spring源码之 prepareBeanFactory 方法

  • registerCustomEditors方法的调用时机?

第8节 Spring源码之 prepareBeanFactory 方法 通过方法调用链路可以看出registerCustomEditors方法是在Bean生命周期的Bean对象实例化之后、对象属性填充之前 调用的

  • setValue方法的调用时机?

第8节 Spring源码之 prepareBeanFactory 方法 通过方法调用链路可以看出registerCustomEditors方法是在Bean生命周期的Bean对象属性填充 时调用的

3. ApplicationContextAwareProcessor处理器

beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))

BeanFactoty 添加 BeanPostProcessor 处理器,只不过这个处理器是 ApplicationContextAwareProcessor,它到底干了什么事?

先简单回顾一下Bean的生命周期流程,如下图所示: 第8节 Spring源码之 prepareBeanFactory 方法

在调populateBean完成bean属性填充之后,会调用 initializeBean方法,该方法依次调用Aware接口、BeanPostProcessor前置处理器、init-method、调用BeanPostProcessor后置处理器。

但是我们发现通过invokeAwareMethods调用 Aware接口时只处理了BeanNameAware、 BeanClassLoaderAware、BeanFactoryAware 三个接口那么其他的Aware接口是在哪里处理的呢???

  • ApplicationContextAwareProcessor 就是处理其他Aware接口的。因为它是BeanPostProcessor,所以在postProcessBeforeInitialization中做了 Aware 接口处理逻辑
class ApplicationContextAwareProcessor implements BeanPostProcessor {

   private final ConfigurableApplicationContext applicationContext;
   private final StringValueResolver embeddedValueResolver;

   public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
      this.applicationContext = applicationContext;
      this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
   }

   @Override
   @Nullable
   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<Object>) () -> {
            invokeAwareInterfaces(bean);
            return null;
         }, acc);
      }
      else {
         invokeAwareInterfaces(bean);
      }
      return bean;
   }

   // 处理其他 Aware 接口
   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);
         }
         if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
         }
      }
   }

   @Override
   public Object postProcessAfterInitialization(Object bean, String beanName) {
      return bean;
   }

}

所以 EnvironmentAware EmbeddedValueResolverAware ResourceLoaderAware ApplicationEventPublisherAware MessageSourceAware ApplicationContextAware 这些接口的调用时机是在BeanPostProcessor前置处理器中处理的