Spring Ioc容器如何进行依赖查找和依赖注入

264 阅读6分钟

重新认识IOC一文中详细介绍了Ioc的概念,作用,以及传统Java的相关实现。本文开始学习Spring Ioc的实现方式

Spring Ioc的实现方式

Spring Ioc的实现方式也是基于依赖查找和依赖注入。

Spring 依赖查找,可以根据Bean名称或者Bean类型进行依赖查找,也可以混合查找(Bean名称+Bean类型)。spring3.0开始,支持根据注解依赖查找。

Spring 依赖注入,可以根据Bean名称或者Bean类型进行依赖注入,注入Spring内建的Bean对象(比如Environment,),也可以实现Aware接口注入非Bean对象。

同时Spring可以根据应用场景,灵活使用延迟/实时依赖查找/依赖注入

Spring Ioc依赖查找

Java Bean代码如下:

public class User {
  private String id;
  private String name;
  private int age;
  private String address;
  private Company company;
  private Properties context;
  private String contextAsText;

  public User() {}

  public User(String id, String name, int age, String address) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.address = address;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public String getAddress() {
    return address;
  }

  public void setAddress(String address) {
    this.address = address;
  }

  public Company getCompany() {
    return company;
  }

  public void setCompany(Company company) {
    this.company = company;
  }

  public Properties getContext() {
    return context;
  }

  public void setContext(Properties context) {
    this.context = context;
  }

  public String getContextAsText() {
    return contextAsText;
  }

  public void setContextAsText(String contextAsText) {
    this.contextAsText = contextAsText;
  }

  @Override
  public String toString() {
    return "User{"
        + "id='"
        + id
        + '''
        + ", name='"
        + name
        + '''
        + ", age="
        + age
        + ", address='"
        + address
        + '''
        + ", company="
        + company
        + ", context="
        + context
        + ", contextAsText='"
        + contextAsText
        + '''
        + '}';
  }

  @PostConstruct
  public void init() {
    System.out.println("用户 id:" + id + " 正在初始化中...");
  }

  @PreDestroy
  public void destroy() {
    System.out.println("用户 id: " + id + " 正在销毁中...");
  }

  public static User getInstance() {
    User user = new User();
    user.setId("1");
    user.setName("Shawn");
    user.setAge(26);
    user.setAddress("SHANGHAI");
    return user;
  }
}

根据Bean名称依赖查找

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"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="user" class="com.xxx.User">
    <property name="id" value="1"/>
    <property name="name" value="shawn"/>
    <property name="age" value="25"/>
    <property name="address" value="shanghai"/>
  </bean>

</beans>

java代码如下:

private final ApplicationContext applicationContext;

public DependencyLookUpDemo() {
    applicationContext = new ClassPathXmlApplicationContext("classpath:/dependency-lookup.xml");
}

/** 根据BeanName:单一类型依赖查找 */
public User lookupByBeanName() {
    return (User) applicationContext.getBean("user");
}

根据Bean类型依赖查找

/** 根据BeanType:单一类型依赖查找 */
  public User lookupByBeanType() {
    return applicationContext.getBean(User.class);
  }

根据Bean类型+Bean名称依赖查找

  /** 根据BeanName & BeanType:单一类型依赖查找 */
  public User lookupByBeanNameAndType() {
    return applicationContext.getBean("user", User.class);
  }

返回集合类型

  /** 集合类型依赖查找 */
  public Map<String, User> lookupCollectionType() {
    return applicationContext.getBeansOfType(User.class);
  }

根据java注解依赖查找

注解如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Super {}

注解使用情况:

@Super
public class SuperUser extends User {
  private String idCard;

  public String getIdCard() {
    return idCard;
  }

  public void setIdCard(String idCard) {
    this.idCard = idCard;
  }

  @Override
  public String toString() {
    return "SuperUser{"
        + "id='"
        + getId()
        + '''
        + ", name='"
        + getName()
        + '''
        + ", age="
        + getAge()
        + ", address='"
        + getAddress()
        + '''
        + ", idCard='"
        + getIdCard()
        + '}';
  }
}

xml配置如下:

  <!--  primary="true"  用于解决org.springframework.beans.factory.NoUniqueBeanDefinitionException expected single matching bean but found 2-->
  <bean id="superUser" class="com.xxx.SuperUser" parent="user"
    primary="true">
    <property name="idCard" value="1234567890"/>
  </bean>

根据java annotation依赖查找 代码如下:

/** 根据java annotation依赖查找 */
public Map<String, Object> lookupBeansByAnnotation() {
    return applicationContext.getBeansWithAnnotation(Super.class);
}

延迟依赖查找

需要用到ObjectFactory类进行延迟依赖查找

xml配置如下:

<bean id="objectFactory"
  class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
  <property name="targetBeanName" value="user"/>
</bean>

延迟依赖查找代码如下:

  public User lookupInLazy() {
    ObjectFactory<User> objectFactory =
        (ObjectFactory<User>) applicationContext.getBean("objectFactory");
    return objectFactory.getObject();
  }

Spring Ioc 依赖注入

前置Bean定义如下:

  <bean id="user" class="com.shawn.study.deep.in.spring.core.ioc.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="shawn"/>
    <property name="age" value="25"/>
    <property name="address" value="shanghai"/>
  </bean>

  <!--  primary="true"  用于解决org.springframework.beans.factory.NoUniqueBeanDefinitionException expected single matching bean but found 2-->
  <bean id="superUser" class="com.shawn.study.deep.in.spring.core.ioc.domain.SuperUser" parent="user"
    primary="true">
    <property name="idCard" value="1234567890"/>
  </bean>

  <bean id="objectFactory"
    class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
    <property name="targetBeanName" value="user"/>
  </bean>

根据Bean名称依赖注入

<bean id="userRepository"
  class="com.xxx.UserRepository">
  <property name="users">
    <util:list>
      <ref bean="user"/>
      <ref bean="superUser"/>
    </util:list>
  </property>
</bean>

根据Bean类型依赖注入

<bean id="auto-user-repository"
    class="com.xxx.UserRepository" autowire="byType"/>

依赖注入内建依赖和非bean对象

  1. 手动依赖注入,需要手动引入内建的依赖,如果不手动引入,则不会被注入进来
  2. 自动依赖注入,可以自动注入内建依赖
  @Test
  public void testInjectionWithCode() {
    UserRepository userRepository = injection.injectWithCode();
    assertNotNull(userRepository);
    User[] users = userRepository.getUsers();
    assertNotNull(users);
    for (User user : users) {
      assertUser(user);
    }
    BeanFactory beanFactory = userRepository.getBeanFactory();
    assertNull(beanFactory);

    Environment environment = userRepository.getEnvironment();
    assertNull(environment);

    ObjectFactory<ApplicationContext> applicationContextObjectFactory =
        userRepository.getObjectFactory();
    assertNull(applicationContextObjectFactory);

    ObjectFactory<User> userObjectFactory = userRepository.getUserObjectFactory();
    assertNull(userObjectFactory);
  }

  @Test
  public void testInjectionWithAutoWiring() {
    UserRepository userRepository = injection.injectWithAutoWiring();
    assertNotNull(userRepository);
    User[] users = userRepository.getUsers();
    assertNotNull(users);
    for (User user : users) {
      assertUser(user);
    }
    BeanFactory beanFactory = userRepository.getBeanFactory();
    assertNotNull(beanFactory);

    Environment environment = userRepository.getEnvironment();
    assertNotNull(environment);

    ObjectFactory<ApplicationContext> applicationContextObjectFactory =
        userRepository.getObjectFactory();
    assertNotNull(applicationContextObjectFactory);

    ObjectFactory<User> userObjectFactory = userRepository.getUserObjectFactory();
    assertNotNull(userObjectFactory);
  }

依赖注入内建Bean

比如Environment

    UserRepository beanAutoWiring = source.getBeanAutoWiring();
    BeanFactory beanFactory = beanAutoWiring.getBeanFactory();
    if (beanFactory instanceof DefaultListableBeanFactory) {
      DefaultListableBeanFactory defaultListableBeanFactory =
          (DefaultListableBeanFactory) beanFactory;
      String[] beanDefinitionNames = defaultListableBeanFactory.getBeanDefinitionNames();
      Arrays.stream(beanDefinitionNames).forEach(System.out::println);
    }
    Environment env1 = beanFactory.getBean("environment", Environment.class);
    Environment env2 = beanAutoWiring.getEnvironment();
    assertEquals(env1, env2);

    UserRepository userRepository = source.getBeanFromXMl();
    assertNull(userRepository.getEnvironment());

Spring Ioc容器

BeanFactory

BeanFactory是Spring Ioc的基本容器,是一个容器接口,BeanFactory负责配置、创建、管理Bean。

  • BeanFactory是Spring Ioc最底层的容器接口,提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean别名的方法。它最主要的方法就是getBean(String beanName)。
  • BeanFactory最直接的三个子接口
    • HierarchicalBeanFactory:提供父容器的访问功能。
    • ListableBeanFactory:提供了根据Bean Type获取集合Bean的方法
    • AutowireCapableBeanFactory:
      • 自动注入Bean。可根据Bean Name,Type,Constructor等方式自动装配Bean,详情看autowire, autowireBeanProperties两个方法
      • 创建Bean。通过CreateBean方法,创建Bean实例,并通过initializeBean进行初始化Bean,通过applyBeanPostProcessorsBeforeInitialization 和 applyBeanPostProcessorsAfterInitialization方法进行初始化回调处理,最后通过destroyBean销毁Bean,实现Bean的生命周期管理以及更灵活的Bean管理和配置。
      • 解析依赖,resolveDependency可以解析指定的依赖关系,支持字段、方法、构造函数等各种依赖注入方式。
  • ConfigurableBeanFactory继承了HierarchicalBeanFactory和SingletonBeanRegistry
    • 注册单例Bean
    • 定制化BeanFactory,可以设置父级BeanFactory、类加载器、表达式解析器、类型转换服务等,以满足特定应用程序的需求。
  • ConfigurableListableBeanFactory扩展了ConfigurableBeanFactory,ListableBeanFactory, AutowireCapableBeanFactory三个基本接口。并扩展了注册内建依赖,获取BeanDefinition,冻结配置等方法。
  • DefaultListableBeanFactory是Ioc容器底层实现类。

ApplicationContext

ApplicationContext是BeanFactory的超集,为Spring提供了企业级的扩展功能

  • 面向切面(AOP)
  • 配置元信息(Configuration Metadata)
  • 资源管理(Resources)
  • 事件(Events)
  • 国际化(i18n)
  • 注解(Annotations)
  • Environment 抽象(Environment Abstraction)

  1. 从UML中可以看到ApplicationContext也是一个接口,继承了HierarchicalBeanFactory和ListableBeanFactory,所以ApplicationContext也是一个BeanFactory。
  2. ApplicationContext同时还继承了EnvironmentCapable,MessageSource,ResourcePatternResolver,所以ApplicationContext除了是Ioc容器外,也提供了Environment 抽象,国际化,资源管理相关能力。
  3. ConfigurationApplicationContext继承了ApplicationContext和LifeCycle两个接口,LifeCycle管理着ApplicationContext的生命周期,ConfigurationApplication还扩展了事件监听机制和配置元信息(除了Bean相关配置之外,还有Environment加载多个配置来源)。
  4. ConfigurationApplicationContext,addBeanFactoryPostProcessor(BeanFactoryPostProcessor)方法为ApplicationContext提供了可扩展性,例如Spring AOP面向切面编程就是通过BeanFactoryPostProcessor来实现的,还有Spring Cache等。
  5. BeanDefinitionRegistry负责注册Bean相关元信息。
  6. GenericApplicationContext内部BeanFactory就是DefaultListableBeanFactory。

ApplicationContext常用的实现类

类名作用
AnnotationConfigApplicationContext从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式。
ClassPathXmlApplicationContext从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式。
FileSystemXmlApplicationContext从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。
AnnotationConfigWebApplicationContext专门为web应用准备的,适用于注解方式。
XmlWebApplicationContext从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。

BeanFactory和ApplicationContext区别

  1. BeanFactory是Spring Ioc最底层的容器,而ApplicationContext也是Ioc容器,继承自BeanFactory,在其基础上添加很多特性,例如事件机制,国际化,资源管理,AOP,Environment抽象等。
  2. BeanFactory在初始化容器时,并未实例化Bean,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至首次调用getBean方法才会抛出异常;而ApplicationContext则在初始化应用上下文时初始化所有Singleton Bean,这样有利于检查所依赖属性是否已经注入。且由于ApplicationContext会预先初始化所有的Singleton Bean,于是在系统创建前期会有较大的系统开销,后续程序再获取Singleton Bean的时候将会有就好的性能。
  3. BeanFactory负责处理BeanDefinition,管理Bean的生命周期,维护Bean之间的依赖关系