参考: https://blog.csdn.net/ivan820819/article/details/79744797

IOC概念

  • IOC(Inversion of Control), 控制反转, 指获得依赖对象的过程被反转了, 由原来的自身管理(主动)变为由IOC容器注入(被动)
  • DI(Dependency Injection), 依赖注入, 由IOC容器在运行期间, 动态的将依赖关系注入到对象中
  • 依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

核心: 借助于”第三方”实现具有依赖关系的对象之间的解耦.
IOC容器概述 - 图1

补充:

  • IOC的实现策略有依赖查找和依赖注入
  • IOC职责

    1. 依赖处理
    2. 依赖查找
    3. 依赖注入
    4. 生命周期管理
    5. 容器
    6. 托管的资源(Java Beans或其他资源)
    7. 配置
    8. 容器
    9. 外部化配置
    10. 托管的资源(Java Beans或其他资源)
  • 依赖查找与依赖注入的对比

image.png

  • 构造器注入与setter注入对比

构造器注入能够在构造期创建一个完整合法的对象, 更符合对象创建即不变的设计理念, 增强系统的稳定以及便利性.

Spring IOC 依赖查找

  • 根据Bean名称查找
    • 实时查找
    • 延迟查找
  • 根据Bean类型查找
    • 单个Bean对象
    • 集合Bean对象
  • 根据Bean名称+类型查找
  • 根据Java注解查找
    • 单个Bean对象
    • 集合Bean对象
  1. public static void main(String[] args) {
  2. // 配置XML配置文件, 启动Spring应用上下文
  3. BeanFactory beanFactory = new ClassPathXmlApplicationContext(
  4. "classpath:META-INF/dependency-lookup-context.xml");
  5. // 通过名称查找
  6. // lookUpInRealTime(beanFactory);
  7. // lookUpInLazy(beanFactory);
  8. // 通过类型查找
  9. lookUpByType(beanFactory);
  10. lookUpByCollectionType(beanFactory);
  11. // 通过注解查找
  12. lookUpByAnnotation(beanFactory);
  13. }
  14. private static void lookUpInRealTime(BeanFactory beanFactory) {
  15. User user = (User) beanFactory.getBean("user");
  16. System.out.println("根据名称实时查找: " + user);
  17. }
  18. private static void lookUpInLazy(BeanFactory beanFactory) {
  19. ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
  20. User user = objectFactory.getObject();
  21. System.out.println("根据名称延迟查找: " + user);
  22. }
  23. private static void lookUpByType(BeanFactory beanFactory) {
  24. User user = beanFactory.getBean(User.class);
  25. System.out.println("根据类型实时查找: " + user);
  26. }
  27. private static void lookUpByCollectionType(BeanFactory beanFactory) {
  28. if (beanFactory instanceof ListableBeanFactory) {
  29. ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
  30. Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
  31. System.out.println("根据类型查找集合对象: " + users);
  32. }
  33. }
  34. private static void lookUpByAnnotation(BeanFactory beanFactory) {
  35. if (beanFactory instanceof ListableBeanFactory) {
  36. ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
  37. Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
  38. System.out.println("根据类型查找集合对象: " + users);
  39. }
  40. }
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans
  3. xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. https://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. https://www.springframework.org/schema/context/spring-context.xsd">
  10. <bean id="user" class="top.xinzhang0618.ioc.overview.domain.User" primary="true">
  11. <property name="id" value="1"/>
  12. <property name="name" value="xinzhang"/>
  13. </bean>
  14. <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
  15. <property name="targetBeanName" value="user"/>
  16. </bean>
  17. <bean id="superUser" class="top.xinzhang0618.ioc.overview.domain.SuperUser" parent="user">
  18. <property name="address" value="shenzhen"/>
  19. </bean>
  20. </beans>

实体如下

  1. public class User {
  2. private Long id;
  3. private String name;
  4. ...
  5. }
  6. @Super
  7. public class SuperUser extends User {
  8. private String address;
  9. ...
  10. }
  11. /**
  12. * 标记超级
  13. */
  14. @Target({ElementType.TYPE})
  15. @Retention(RetentionPolicy.RUNTIME)
  16. public @interface Super {
  17. }

ObjectFactoryBean, FactoryBean, BeanFactory三者有什么区别?
ObjectFactory 通常是针对单类 Bean 做延迟获取的,BeanFactory 则是全局 Bean 管理的容器。

Spring IOC依赖注入

  • 根据Bean名称注入
  • 根据Bean类型注入
    • 单个Bean对象
    • 集合Bean对象
  • 注入容器内建Bean对象
  • 注入非Bean对象
  • 注入类型
    • 实时注入
    • 延迟注入

如下示例, 可以手动配置, 也可以通过Autowire进行注入

  1. <bean id="userRepository" class="org.geekbang.thinking.in.spring.ioc.overview.repository.UserRepository"
  2. autowire="byType"> <!-- Auto-Wiring -->
  3. <!-- 手动配置 -->
  4. <!-- <property name="users">-->
  5. <!-- <util:list>-->
  6. <!-- <ref bean="superUser" />-->
  7. <!-- <ref bean="user" />-->
  8. <!-- </util:list>-->
  9. <!-- </property>-->
  10. </bean>

Spring IOC依赖来源

  1. 自定义Bean(自己用xml配置或注解配置的bean)
  2. 内部容器依赖的Bean(非自己定义的Bean,spring容器初始化的Bean), 如Environment对象
  3. 内部容器所构建的依赖(非Bean,不可通过获取依赖查找Bean的方法来获取(getBean(XXX)))

    1. public class UserRepository {
    2. private Collection<User> users; // 自定义 Bean
    3. private BeanFactory beanFactory; // 內建非 Bean 对象(依赖)
    4. ...
    5. }

    内建Bean和内建依赖如何区别?

    实际上,内建的 Bean 是普通的 Spring Bean,包括 BeanDefinitions 和 Singleton Objects,而内建依赖则是通过 AutowireCapableBeanFactory 中的 resolveDependency 方法来注册,这并非是一个 Spring Bean,无法通过依赖查找获取~

内部容器构建的依赖指的是什么?

Spring IoC 底层容器就是指的 BeanFactory 的实现类,大多数情况是 DefaultListableBeanFactory 这个类,它来管理 Spring Beans,而 ApplicationContext 通常为开发人员接触到的 IoC 容器,它是一个 Facade,Wrap 了 BeanFactory 的实现。

Java Bean 是一种功能组件,它是 Java对象的方式封装, 所谓依赖就是被组合的对象。

Spring IOC配置元信息

  1. Bean定义配置
  • 基于XML文件
  • 基于Properties文件
  • 基于JAVA注解
  • 基于JAVA API
  1. IOC容器配置
  • 基于XML文件
  • 基于JAVA注解
  • 基于JAVA API
  1. 外部化属性配置
  • 基于JAVA注解, 比如@Value

Spring IOC容器

BeanFacrtory和ApplicationContext谁才是IOC容器?

官方文档如下图
image.png
简要翻译: BeanFactory提供了管理对象的一些高级配置特性, ApplicationContext是BeanFactory的子接口, 它添加了:

  • 更容易与Spring AOP的整合
  • 消息资源的处理(用于国际化)
  • 事件发布
  • 应用级别的上下文, 比如应用在WEB应用的WebApplicationContext

总结: BeanFactory是个基本的IOC容器, ApplicationContext是在其基础上提供了更多企业级特性的超集.

分析下实现:

  • ApplicationContext继承了BeanFactory
  • ConfigurableApplicationContext中, 有个getBeanFactory()方法, 在AbstractRefreshableApplicationContext中有实现, 返回的是AbstractRefreshableApplicationContext持有的DefaultListableBeanFactory
  • 即ApplicationContext是委托DefaultListableBeanFactory来操作getBean等方法的

image.png
image.png

BeanFactory和ApplicationContext的关系,从设计模式上讲, 使用了代理模式,也使用了面门模式。

Spring应用上下文

image.png

使用Spring IOC

  • BeanFactory是Spring底层的IOC容器
  • ApplicationContext是具备应用特性的BeanFactory超集

两者的使用场景区别, 简单来说就是如果只用xml进行Bean的简单配置, 用BeanFactory即可, 如果要用到一些高级特性比如注解方式的Bean配置, 那么需要用ApplicationContext, 注解方式配置的demo如下

  1. public class AnnotationApplicationContextAsIocContainerDemo {
  2. public static void main(String[] args) {
  3. // 创建BeanFactory容器
  4. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  5. // 将当前类作为配置类
  6. context.register(AnnotationApplicationContextAsIocContainerDemo.class);
  7. context.refresh();
  8. // 依赖查找集合对象
  9. lookUpByCollectionType(context);
  10. }
  11. @Bean
  12. public User user() {
  13. User user = new User();
  14. user.setId(1L);
  15. user.setName("hhh");
  16. return user;
  17. }
  18. private static void lookUpByCollectionType(BeanFactory beanFactory) {
  19. if (beanFactory instanceof ListableBeanFactory) {
  20. ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
  21. Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
  22. System.out.println("根据类型查找集合对象: " + users);
  23. }
  24. }
  25. }

Spring IOC容器的生命周期

一般分三阶段: 启动, 运行, 停止

启动看applicationContext.refresh()方法
以下为AbstractApplicationContext源码, 可以看到启动有几个操作:

  • 初始化BeanFactory
  • 触发BeanFactory的后续处理
  • 注册Bean的后续处理器
  • 初始化国际化相关
  • 初始化事件广播
  • 注册监听器

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. synchronized (this.startupShutdownMonitor) {
  4. // Prepare this context for refreshing.
  5. prepareRefresh();
  6. // Tell the subclass to refresh the internal bean factory.
  7. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  8. // Prepare the bean factory for use in this context.
  9. // 这里加入了Bean以及内建的非Bean对象(如BeanFactory)等依赖
  10. prepareBeanFactory(beanFactory);
  11. try {
  12. // Allows post-processing of the bean factory in context subclasses.
  13. postProcessBeanFactory(beanFactory);
  14. // Invoke factory processors registered as beans in the context.
  15. // 这里可以自定义BeanFactory的拓展
  16. invokeBeanFactoryPostProcessors(beanFactory);
  17. // Register bean processors that intercept bean creation.
  18. // 这里可以自定义Bean的拓展
  19. registerBeanPostProcessors(beanFactory);
  20. // Initialize message source for this context.
  21. initMessageSource();
  22. // Initialize event multicaster for this context.
  23. initApplicationEventMulticaster();
  24. // Initialize other special beans in specific context subclasses.
  25. onRefresh();
  26. // Check for listener beans and register them.
  27. registerListeners();
  28. // Instantiate all remaining (non-lazy-init) singletons.
  29. finishBeanFactoryInitialization(beanFactory);
  30. // Last step: publish corresponding event.
  31. finishRefresh();
  32. }
  33. catch (BeansException ex) {
  34. if (logger.isWarnEnabled()) {
  35. logger.warn("Exception encountered during context initialization - " +
  36. "cancelling refresh attempt: " + ex);
  37. }
  38. // Destroy already created singletons to avoid dangling resources.
  39. destroyBeans();
  40. // Reset 'active' flag.
  41. cancelRefresh(ex);
  42. // Propagate exception to caller.
  43. throw ex;
  44. }
  45. finally {
  46. // Reset common introspection caches in Spring's core, since we
  47. // might not ever need metadata for singleton beans anymore...
  48. resetCommonCaches();
  49. }
  50. }
  51. }

停止看applicationContext.close()方法
以下为AbstractApplicationContext源码, 可以看到停止主要有几个操作:

  • 注销Bean
  • 关闭BeanFactory
  • 还有个onClose()空实现可以拓展

  1. protected void doClose() {
  2. // Check whether an actual close attempt is necessary...
  3. if (this.active.get() && this.closed.compareAndSet(false, true)) {
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Closing " + this);
  6. }
  7. LiveBeansView.unregisterApplicationContext(this);
  8. try {
  9. // Publish shutdown event.
  10. publishEvent(new ContextClosedEvent(this));
  11. }
  12. catch (Throwable ex) {
  13. logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
  14. }
  15. // Stop all Lifecycle beans, to avoid delays during individual destruction.
  16. if (this.lifecycleProcessor != null) {
  17. try {
  18. this.lifecycleProcessor.onClose();
  19. }
  20. catch (Throwable ex) {
  21. logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
  22. }
  23. }
  24. // Destroy all cached singletons in the context's BeanFactory.
  25. destroyBeans();
  26. // Close the state of this context itself.
  27. closeBeanFactory();
  28. // Let subclasses do some final clean-up if they wish...
  29. onClose();
  30. // Reset local application listeners to pre-refresh state.
  31. if (this.earlyApplicationListeners != null) {
  32. this.applicationListeners.clear();
  33. this.applicationListeners.addAll(this.earlyApplicationListeners);
  34. }
  35. // Switch to inactive.
  36. this.active.set(false);
  37. }
  38. }

面试题

什么是Spring IOC容器?
Spring IOC容器是Spring使用依赖注入和依赖查找对IOC原则的一种实现, 同时Spring IOC还提供了一些其他的特性, 比如容器生命周期的管理, 容器的配置, 外部化配置等等.

BeanFactory与FactoryBean的区别?
BeanFactory是IOC底层容器, FactoryBean是创建Bean的一种方式, 帮助实现复杂的初始化逻辑

Spring Bean有两种实现,普通Bean,和工厂Bean(FactoryBean) 实现工厂Bean的方法就是pojo继承FactoryBean,并实现他的方法,当容器通过getBean()获取bean时,返回的是实现的getObject()方法所返回的对象