sayi.github.com icon indicating copy to clipboard operation
sayi.github.com copied to clipboard

依赖注入(二)Spring Dependency injection

Open Sayi opened this issue 7 years ago • 0 comments

Spring核心功能之一是IOC容器,Spring凭借优雅的扩展性和强大的配置功能,几乎已经让它成为服务端IOC的标杆。也正因为Spring,才有了Guice等后起之秀。

IOC 容器

我们先从一个简单的例子开始,了解一下Spring IOC容器的用法,更多的用法可以参见Spring官方文档。

  1. 写一个服务接口
package com.deepoove.diexample.service;

public interface AccountService {
  String get();
}
  1. 实现这个接口
package com.deepoove.diexample.service;

public class AccountServiceImpl implements AccountService {

  @Override
  public String get() {
    return "Sayi";
  }
}

  1. 使用XML形式配置服务的实例化
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

  <bean id="accountService" class="com.deepoove.diexample.service.AccountServiceImpl">
  </bean>

</beans>
  1. 容器加载XML配置,负责Bean的创建,装配和销毁。
@Test
public void testXMLConifg() {
  ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

  AccountService accountService = context.getBean("accountService", AccountService.class);
  Assert.assertEquals(accountService.get(), "Sayi");

  context.close();

}

通过这个简单的例子,我们看到ClassPathXmlApplicationContext起了重要的作用,它负责整个Bean的生命周期,我们不妨先看看这个类关系图: image

暂时我们无需关注细节,只需要知道BeanFactory和ApplicationContext都是接口,它们是整个容器的核心。BeanFactory类似于上一篇提到的门面Injector,提供了统一的DI接口,ApplicationContext是它的子接口,包含了它所有功能之余,还提供了更多的特性,比如AOP。

BeanFactory or ApplicationContext? Use an ApplicationContext unless you have a good reason for not doing so.

Spring 依赖注入 与 JSR330

Spring提供了自己的注解外,也支持JSR330的注解。下表列出了它们类似的功能,当然Spring提供了等多的注解,比如@Service标识一个服务,@Component标识一个组件。

Spring JSR330
@Autowired @Inject
@Scope("singleton") @Singleton
@Qualifier @Qualifier / @Named
ObjectFactory<T> Provider<T>

源码分析

我们跟着ClassPathXmlApplicationContext来一步步探究下Spring DI的源码。

BeanFactory默认实现是DefaultListableBeanFactory,包含了存储所有Bean的数据结构。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

核心初始化代码在AbstractApplicationContext.refresh()方法,每行代码具体作用可以阅读英文注释来了解。ApplicationContext通过持有DefaultListableBeanFactory对象获得所有BeanFactory的功能。

public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    prepareRefresh();

    // Tell the subclass to refresh the internal bean factory.
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);

    try {
      // Allows post-processing of the bean factory in context subclasses.
      postProcessBeanFactory(beanFactory);

      // Invoke factory processors registered as beans in the context.
      invokeBeanFactoryPostProcessors(beanFactory);

      // Register bean processors that intercept bean creation.
      registerBeanPostProcessors(beanFactory);

      // Initialize message source for this context.
      initMessageSource();

      // Initialize event multicaster for this context.
      initApplicationEventMulticaster();

      // Initialize other special beans in specific context subclasses.
      onRefresh();

      // Check for listener beans and register them.
      registerListeners();

      // Instantiate all remaining (non-lazy-init) singletons.
      finishBeanFactoryInitialization(beanFactory);

      // Last step: publish corresponding event.
      finishRefresh();
    }

    catch (BeansException ex) {
      if (logger.isWarnEnabled()) {
        logger.warn("Exception encountered during context initialization - " +
            "cancelling refresh attempt: " + ex);
      }

      // Destroy already created singletons to avoid dangling resources.
      destroyBeans();

      // Reset 'active' flag.
      cancelRefresh(ex);

      // Propagate exception to caller.
      throw ex;
    }

    finally {
      // Reset common introspection caches in Spring's core, since we
      // might not ever need metadata for singleton beans anymore...
      resetCommonCaches();
    }
  }
}

接下来会对几个核心步骤作一个说明。

obtainFreshBeanFactory()创建DefaultListableBeanFactory,加载BeanDefinition

obtainFreshBeanFactory()获得了具体BeanFactory的实例,是通过如下代码实现的。

  DefaultListableBeanFactory beanFactory = createBeanFactory();
  beanFactory.setSerializationId(getId()); // 设置唯一ID
  customizeBeanFactory(beanFactory); 
  loadBeanDefinitions(beanFactory); // 读取Bean的定义,可以是XML形式,也可以是其它形式
  synchronized (this.beanFactoryMonitor) {
    this.beanFactory = beanFactory;
  }

接下来我们说明下loadBeanDefinitions方法,它的主要作用是将Beans的定义加载到BeanFactory,初始化beanDefinitionMap。

/**
 * Load bean definitions into the given bean factory, typically through
 * delegating to one or more bean definition readers.
 * @param beanFactory the bean factory to load bean definitions into
 * @throws BeansException if parsing of the bean definitions failed
 * @throws IOException if loading of bean definition files failed
 * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 */
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
        throws BeansException, IOException;

Spring提供了统一的接口BeanDefinitionReader来加载Bean,比如实现类XmlBeanDefinitionReader从XML中读取Bean,Spring也实现了其它的读取器,AnnotatedBeanDefinitionReader从注解读取Bean,JdbcBeanDefinitionReader提供了利用SQL从数据库读取Bean的功能。

XmlBeanDefinitionReader主要通过委托类BeanDefinitionParserDelegate实现,具体如何加载的源码本文不作过多展开。

prepareBeanFactory(beanFactory) 对容器进行标准化配置

比如设置类加载器

beanFactory.setBeanClassLoader(getClassLoader());

设置ApplicationContextAwareProcessor支持对某些特殊组件(Aware)的注入(关于BeanPostProcessor参见下文);

beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

注册一些默认的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());
}

invokeBeanFactoryPostProcessors(beanFactory) 调用BeanFactoryPostProcessors

Spring容器的扩展点BeanFactoryPostProcessors提供了在BeanDefinition已加载,还没有任何Bean被实例化的时候操作BeanFactory的能力。

registerBeanPostProcessors(beanFactory); 注册BeanPostProcessor

Spring容器的扩展点BeanPostProcessor提供了Bean在调用初始化方法(InitializingBean's {@code afterPropertiesSet或者init-method)前后操作Bean的能力。

public interface BeanPostProcessor {

  @Nullable
  default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    return bean;
  }

  @Nullable
  default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    return bean;
  }

}

BeanPostProcessor为Spring提供了优秀的扩展能力,AOP(AspectJAwareAdvisorAutoProxyCreator)和@Autowired(AutowiredAnnotationBeanPostProcessor)的特性都是基于此扩展的插件。

image

在业务开发中,经常需要在关键流程记录日志,代码可能会是这样:

Log logger = LogFactory.getLog(getClass());

我们可以实现BeanPostProcessor接口,在实例化Bean后通过反射获取到logger对象,进而实例化logger来避免频繁的写这串日志变量声明代码。

initApplicationEventMulticaster()初始化事件分发 和 registerListeners()注册监听器

默认广播实现是SimpleApplicationEventMulticaster类,Spring监听事件采用观察者模式,我们可以注册自己的Listener监听器,也可以自定义事件Event。

finishBeanFactoryInitialization(beanFactory) 实例化非Lazy的单例Bean

我们直接看核心代码AbstractAutowireCapableBeanFactory.doCreateBean方法,作用是创建Bean。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
    throws BeanCreationException {

  // Instantiate the bean.
  BeanWrapper instanceWrapper = null;
  if (mbd.isSingleton()) {
    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  }
  if (instanceWrapper == null) {
    instanceWrapper = createBeanInstance(beanName, mbd, args);
  }
  final Object bean = instanceWrapper.getWrappedInstance();
  Class<?> beanType = instanceWrapper.getWrappedClass();
  if (beanType != NullBean.class) {
    mbd.resolvedTargetType = beanType;
  }

  // Allow post-processors to modify the merged bean definition.
  synchronized (mbd.postProcessingLock) {
    if (!mbd.postProcessed) {
      try {
        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
      }
      catch (Throwable ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Post-processing of merged bean definition failed", ex);
      }
      mbd.postProcessed = true;
    }
  }

  // Eagerly cache singletons to be able to resolve circular references
  // even when triggered by lifecycle interfaces like BeanFactoryAware.
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
    if (logger.isDebugEnabled()) {
      logger.debug("Eagerly caching bean '" + beanName +
          "' to allow for resolving potential circular references");
    }
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  }

  // Initialize the bean instance.
  Object exposedObject = bean;
  try {
    populateBean(beanName, mbd, instanceWrapper);
    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);
    }
  }

  if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
      if (exposedObject == bean) {
        exposedObject = earlySingletonReference;
      }
      else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
        String[] dependentBeans = getDependentBeans(beanName);
        Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
        for (String dependentBean : dependentBeans) {
          if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
            actualDependentBeans.add(dependentBean);
          }
        }
        if (!actualDependentBeans.isEmpty()) {
          throw new BeanCurrentlyInCreationException(beanName,
              "Bean with name '" + beanName + "' has been injected into other beans [" +
              StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
              "] in its raw version as part of a circular reference, but has eventually been " +
              "wrapped. This means that said other beans do not use the final version of the " +
              "bean. This is often the result of over-eager type matching - consider using " +
              "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
        }
      }
    }
  }

  // Register bean as disposable.
  try {
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
  }
  catch (BeanDefinitionValidationException ex) {
    throw new BeanCreationException(
        mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
  }

  return exposedObject;
}

其中earlySingletonExposure是为了解决循环依赖的问题,我们先把重点放到以下三行代码:

  1. instanceWrapper = createBeanInstance(beanName, mbd, args);用来创建具体对象实例
  2. populateBean(beanName, mbd, instanceWrapper);用来填充对象的属性
  3. initializeBean(beanName, exposedObject, mbd);调用初始化方法

createBeanInstance最核心的代码如下:

// Need to determine the constructor...
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
  return autowireConstructor(beanName, mbd, ctors, args);
}

// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);

实现原理是通过反射获得构造器,然后创建实例。此处采用了策略模式,实例化对象的接口为InstantiationStrategy,两个实现类SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy(支持Method Injection)。

反射的代码就很简单了,在BeanUtils中提供了静态方法instantiateClass:

try {
  ReflectionUtils.makeAccessible(ctor);
  return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
      KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}

populateBean注入的核心代码在AbstractAutowireCapableBeanFactory.autowireByType,通过DefaultListableBeanFactory.resolveDependency解析依赖对象,PropertyDescriptor注入属性值。

PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// Don't try autowiring by type for type Object: never makes sense,
// even if it technically is a unsatisfied, non-simple property.
if (Object.class != pd.getPropertyType()) {
  MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
  // Do not allow eager init for type matching in case of a prioritized post-processor.
  boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
  DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
  Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
  if (autowiredArgument != null) {
    pvs.add(propertyName, autowiredArgument);
  }
  for (String autowiredBeanName : autowiredBeanNames) {
    registerDependentBean(autowiredBeanName, beanName);
    if (logger.isDebugEnabled()) {
      logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
          propertyName + "' to bean named '" + autowiredBeanName + "'");
    }
  }
  autowiredBeanNames.clear();
}

关于Aware注入

在源码分析中,提到过ApplicationContextAwareProcessor类,它实现了BeanPostProcessor接口,在Bean调用初始化方法前,支持对一些实现了Aware接口的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);
  }
}

image

更多:扩展点FactoryBean

org.springframework.beans.factory.FactoryBean和BeanFactory只是两个单词的顺序不同,但它们绝对不是一样的东西。

factory-bean提供了通过工厂和工厂方法实例化Bean的xml配置方式。

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

org.springframework.beans.factory.FactoryBean为我们提供了生成Bean的工厂方式的接口。

public interface FactoryBean<T> {

  T getObject() throws Exception;

  Class<?> getObjectType();

  boolean isSingleton();

}

getObject是具体实例化的Bean,getObjectType是Bean的类型,我们实现这个方法来实例化我们所需要的所有对象。 Spring很多地方使用了FactoryBean,比如ListFactoryBean、MapFactoryBean和spring-remoting模块:HttpInvokerProxyFactoryBean、HessianProxyFactoryBean,这里就不作过度延伸了。

循环依赖问题和@Lazy

Spring解决了单例下属性注入的循环依赖问题。对于构造器依赖,或者非单例情况下,可以使用@Lazy注解延迟实例化。

关于Spring循环依赖,这里有篇英文文章,写的很易懂。http://www.baeldung.com/circular-dependencies-in-spring

我们先来分析下原理,单例下属性注入在上文提到的核心三行代码已经看得很清楚,采取提前暴露对象+延迟注入属性的方式,先实例化所有单例实例,再对属性进行注入,对于prototype类型的实例,或者构造器循环依赖就会抛出BeanCurrentlyInCreationException异常。

@Lazy的核心原理是采取了动态代理的方式,当构造器依赖一个@Lazy修饰的Bean时,会优先返回这个Bean的动态代理,而将这个Bean的实际初始化工作延迟,Spring的代理的核心类是ProxyFactory,会在我的另一篇文章《面向切面编程AOP》中详细阐述。

解析@Lazy注解的入口在DefaultListableBeanFactory.resolveDependency,源码如下:

  Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
        descriptor, requestingBeanName);

生成代理的方法源码如下:

protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
  Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
      "BeanFactory needs to be a DefaultListableBeanFactory");
  final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
  TargetSource ts = new TargetSource() {
    @Override
    public Class<?> getTargetClass() {
      return descriptor.getDependencyType();
    }
    @Override
    public boolean isStatic() {
      return false;
    }
    @Override
    public Object getTarget() {
      Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
      if (target == null) {
        Class<?> type = getTargetClass();
        if (Map.class == type) {
          return Collections.emptyMap();
        }
        else if (List.class == type) {
          return Collections.emptyList();
        }
        else if (Set.class == type || Collection.class == type) {
          return Collections.emptySet();
        }
        throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
            "Optional dependency not present for lazy injection point");
      }
      return target;
    }
    @Override
    public void releaseTarget(Object target) {
    }
  };
  ProxyFactory pf = new ProxyFactory();
  pf.setTargetSource(ts);
  Class<?> dependencyType = descriptor.getDependencyType();
  if (dependencyType.isInterface()) {
    pf.addInterface(dependencyType);
  }
  return pf.getProxy(beanFactory.getBeanClassLoader());
}

总结

至此,我们对Spring的容器和Bean的创建过程进行了源码分析,正因为Spring优秀的扩展机制,当我们了解这些机制后,可以为技术赋能,实现更高级的功能。

下一篇我们将重点解读google guice框架,它同样优秀,还轻量级,待续。

Sayi avatar Jul 27 '18 02:07 Sayi