spring详解-IOC

打印 上一主题 下一主题

主题 1686|帖子 1686|积分 5058

Spring--IOC容器

Spring 是一个基于 Java 平台的开源全栈应用步伐框架,也是一个控制反转(IoC)容器实现。
Spring IoC 容器即 Spring 框架中负责管理“Bean”(由容器实例化、配置并组装的普通 POJO 对象)的焦点组件。容器读取配置元数据(XML、注解或 Java 配置类),解析 Bean 界说,然后在启动或按需时实例化 Bean,完成依赖注入,并负责 Bean 的整个生命周期管理。
本文章示例代码见该堆栈:【spring】中的“spring”模块。
堆栈地址:https://gitee.com/quercus-sp204/sourcecode-and-demos
1.相干概念

IOC: Inversion of control 【控制反转】
控制反转是一种设计原则,其焦点思想是:将步伐中对象的创建、配置和生命周期管理的控制权,从应用代码“反转”交给外部框架或容器来负责。在传统的步伐设计中,业务逻辑代码主动调用库或框架完成通用功能;而在 IoC 中,则是框架或容器主动调用开发者编写的代码,实现“控制权”反转,别的呢,IoC 不是一种技术,而是一种思想。
低落组件间的耦合度,提拔体系的可维护性和扩展性。例如,传统开发中Service层直接依赖Dao层的具体实现,而IoC通过容器动态注入依赖,使两者解耦。
DI:Dependency Injection 【依赖注入】
依赖注入是 IoC 最常见的一种实现方式。在 DI 模式下,对象只需声明它所依赖的其他对象(通过构造器、属性或工厂方法),由 IoC 容器在实例化时自动“注入”这些依赖,从而实现组件间的松耦合和可测试性提拔。
在Spring中,我们可以通过构造器注入、Setter方法注入或字段注入(如@Autowired)。
  1. // 构造器注入
  2. @Component
  3. public class UserService {
  4.     private final UserDao userDao;
  5.     @Autowired
  6.     public UserService(UserDao userDao) {
  7.         this.userDao = userDao;
  8.     }
  9. }
复制代码
2.焦点类介绍

我们都知道,spring可以从xml配置文件中解析bean的界说,同时也可以结合相干注解(例如@Configuration, @Bean等)来界说相干bean。最经典的就是这两种了,下面我们就基于这两种类型,来介绍一下相干的焦点类。
①根本的


  • Bean工厂相干接口以及context

上面的类间关系图我们必要了解的是赤色框框中的三个,BeanFactory、ApplicationEventPublisher、ApplicationContext。
BeanFactory : IOC容器的焦点接口,界说了基础功能(如getBean()、containsBean()),可以看到,BeanFactory是一个顶级接口,界说了比力常用的getBean方法。
  1. public interface BeanFactory {
  2.     String FACTORY_BEAN_PREFIX = "&";
  3.     Object getBean(String name) throws BeansException;
  4.     <T> T getBean(String name, Class<T> requiredType) throws BeansException;
  5.     <T> T getBean(Class<T> requiredType) throws BeansException;
  6.     boolean containsBean(String name);
  7.     .....
  8. }
复制代码
ApplicationContext:BeanFactory的扩展接口,提供高级功能(如国际化、事件发布、AOP支持)
用于为应用步伐提供配置的中央接口。这在应用步伐运行时是只读的,但如果实现支持,则可以重新加载。ApplicationContext提供:
继承自 ListableBeanFactory用于访问应用步伐组件的 Bean 工厂方法。
继承自ResourceLoader 接口用以通用方式加载文件资源的能力。
继承自 ApplicationEventPublisher 接口能够将事件发布到已注册的侦听器。
继承自 MessageSource 接口 能够解析消息的能力,支持国际化。
  1. public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
  2.                 MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
  3.     .....
  4. }
复制代码
ApplicationEventPublisher:封装事件发布功能的接口。
  1. @FunctionalInterface
  2. public interface ApplicationEventPublisher {
  3.     /*
  4.     向在此应用程序注册过应用程序事件的匹配侦听器 发送通知。
  5.     事件可以是框架事件(例如 ContextRefreshedEvent)或特定于应用程序的事件。
  6.     这样的事件发布步骤实际上是移交给 multicaster,并不意味着同步异步执行,甚至根本不意味着立即执行。
  7.     建议事件侦听器尽可能高效,单独使用异步执行来执行运行时间较长且可能阻塞的作。
  8.     */
  9.         default void publishEvent(ApplicationEvent event) {
  10.                 publishEvent((Object) event);
  11.         }
  12.     void publishEvent(Object event); // 接口
  13. }
复制代码
   
   
spring各种类非常多,所以在上图中,我这里只做出了各自类之间的大致关系。
DefaultListableBeanFactory: Spring IoC 容器的 基础实现类,直接管理 Bean 的全生命周期(实例化、依赖注入、销毁);通过BeanDefinitionRegistry 接口注册/移除 Bean 界说(如 XML 或注解配置);preInstantiateSingletons() 方法来实例化bean。-----这个方法是实现了接口ConfigurableListableBeanFactory
AbstractApplicationContext:是 容器启动的模板,扩展了企业级功能并依赖 BeanFactory 实现业务逻辑,通过 refresh() 方法界说容器启动流程(如加载配置、注册 BeanFactoryPostProcessor)。在其子类实现中,重写了obtainFreshBeanFactory()方法,可以获取到beanFactory

  • Bean界说
BeanDefinition 接口用于描述一个 Bean 实例的元信息,包罗类名、构造函数参数、属性值、作用域、初始化/销毁方法等。
  1. public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
  2.     .....
  3. }
复制代码
BeanDefinitionHolder 封装一个 BeanDefinition 以及它对应的 Bean 名称和别名,可用于内部 Bean 的占位或编程式注册时携带额外信息。
  1. public class BeanDefinitionHolder implements BeanMetadataElement {
  2.         private final BeanDefinition beanDefinition;
  3.         private final String beanName;
  4.         @Nullable
  5.         private final String[] aliases;
  6.     ....
  7. }
复制代码
BeanDefinitionBuilder 用于以流式 API 构造 GenericBeanDefinition、RootBeanDefinition、ChildBeanDefinition 等,并设置各种属性(如 scope、lazyInit、autowireMode、initMethod 等)。
②基于注解的context


AnnotationConfigApplicationContext :是 Spring 注解驱动配置的焦点实现。其中的关键组件:

  • AnnotatedBeanDefinitionReader
    负责解析配置类(@Configuration)并注册 Bean 界说。
    焦点逻辑

    • 通过 registerBean() 方法将配置类转换为 AnnotatedGenericBeanDefinition。
    • 处理 @Bean 方法,生成对应的 Bean 界说。
    • 注册 BeanPostProcessor(如 ConfigurationClassPostProcessor)。

  • ClassPathBeanDefinitionScanner
    扫描包路径下的类,注册组件(@Component、@Service)。

    • 过滤条件:默认扫描带有 @Component 及其派生注解的类。
    • 扩展点:可通过 includeFilters 和 excludeFilters 自界说过滤规则。

该context初始化流程

  • 构造阶段

    • 创建 DefaultListableBeanFactory(底层 BeanFactory)。
    • 初始化 AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner。

  • 注册阶段

    • 解析配置类,生成 BeanDefinition(如 AnnotatedGenericBeanDefinition)。
    • 将 Bean 界说注册到 BeanDefinitionRegistry(即 DefaultListableBeanFactory)。

  • 刷新阶段(refresh()):

    • 预备环境:验证配置属性。
    • 获取 BeanFactory:确保 DefaultListableBeanFactory 已初始化。
    • 注册 BeanPostProcessor:如 AutowiredAnnotationBeanPostProcessor。
    • 完成 Bean 实例化:通过 preInstantiateSingletons() 预加载单例 Bean。

③基于配置文件的context


ClassPathXmlApplicationContext :

  • 基于 XML 配置的 Spring 容器:通过加载类路径(ClassPath)下的 XML 文件初始化 Spring 容器,管理 Bean 的生命周期和依赖注入。
  • ApplicationContext 的实现类:继承自 AbstractApplicationContext,提供完整的容器功能(如国际化、事件机制)。
  • 传统 Spring 应用的焦点入口:实用于 XML 配置驱动的项目(如早期 Spring 项目)。
示例:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5.     <bean scope="singleton"  name="policeman">
  6.         <constructor-arg name="name" value="国窖001"/>
  7.     </bean>
  8. </beans>
复制代码
  1. public class XmlContextMain {
  2.     public static void main(String[] args) {
  3.         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("all-spring.xml");
  4.         Policeman policeman = context.getBean(Policeman.class);
  5.         policeman.say();
  6.     }
  7. }
  8. // 实体类
  9. public class Policeman {
  10.     private String name;
  11.     public Policeman(){
  12.         System.out.println("Policeman 构造函数");
  13.     }
  14.     public Policeman(String name){
  15.         this.name = name;
  16.     }
  17.     public void say() {
  18.         System.out.println(this.name + "报道!");
  19.     }
  20. }
复制代码
该示例见spring模块中的带有xml前缀的类和包。
现在ClassPathXmlApplicationContext用的很少了,现代 Spring 开发更倾向注解配置。
3. 容器创建

在了解到了根本的类,以及他们之间的大致关系之后,这本节我们来探索一下IOC容器是如何创建的。那我们就以注解配置的context为例子。
  1. ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
  2. Husband h = context.getBean(Husband.class);
  3. h.say();
复制代码
从构造函数一步一步向下走
  1. // AnnotationConfigApplicationContext.java
  2. public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
  3.     this(); // 1.无参构造
  4.     register(componentClasses); // 2.注册配置类并生成BeanDefinition
  5.     refresh(); // 3.容器刷新
  6. }
复制代码
①构造函数
  1. // 1.无参构造
  2. public AnnotationConfigApplicationContext() {
  3.     StartupStep createAnnotatedBeanDefReader = getApplicationStartup().start("spring.context.annotated-bean-reader.create");
  4.     // 初始化注解 Bean 定义读取器,通过读取器预注册这些处理器,
  5.     // 以确保后续注解解析和依赖注入的基础能力。
  6.     /*
  7.     创建 AnnotatedBeanDefinitionReader 实例,负责将
  8.     注解配置类(如 @Configuration、@Component)转换为 BeanDefinition
  9.     自动注册关键组件:
  10.     ConfigurationClassPostProcessor(解析 @Configuration 类)。
  11.     AutowiredAnnotationBeanPostProcessor(处理 @Autowired)。
  12.     CommonAnnotationBeanPostProcessor(处理 @Resource、@PostConstruct)。
  13.     EventListenerMethodProcessor(处理 @EventListener)。
  14.     */
  15.     this.reader = new AnnotatedBeanDefinitionReader(this);
  16.     createAnnotatedBeanDefReader.end();
  17.     /*
  18.     创建 ClassPathBeanDefinitionScanner 实例,负责扫描类路径下的组件类(如 @Component、@Service)
  19.     */
  20.     this.scanner = new ClassPathBeanDefinitionScanner(this);
  21. }
复制代码
在创建AnnotatedBeanDefinitionReader的时候,默认会创建几个BeanDefinition放到beanDefinitionMap里面。
②注册配置类

[code]// 2.注册配置类并生成BeanDefinition@Overridepublic void register(Class... componentClasses) {    Assert.notEmpty(componentClasses, "At least one component class must be specified");    StartupStep registerComponentClass = getApplicationStartup().start("spring.context.component-classes.register")            .tag("classes", () -> Arrays.toString(componentClasses));    this.reader.register(componentClasses);    registerComponentClass.end();}// AnnotatedBeanDefinitionReader.javapublic void register(Class... componentClasses) {    for (Class componentClass : componentClasses) {        registerBean(componentClass);    }}// 往下末了是调用下面这个private  void doRegisterBean(Class beanClass, @Nullable String name,        @Nullable Class

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万万哇

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表