首页 > 编程语言 >Spring源码分析(一)Spring容器及Spring Bean

Spring源码分析(一)Spring容器及Spring Bean

时间:2023-09-17 22:03:14浏览次数:43  
标签:ac 实例 Spring public Bean 源码 class

(一)Spring 容器及 Spring Bean

1.Spring 容器

1.1 什么是容器

官网中有一句话

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.

翻译下来的意思是:

  1. Spring IOC 容器就是一个 org.springframework.context.ApplicationContext 的实例化对象
  2. 容器负责实例化,配置以及装配 bean

那么我们可以这样理解:

  1. 从代码层次来看,Spring 容器就是一个实现了 ApplicationContext 接口的对象
  2. 从功能上来看,Spring 容器是 Spring 框架的核心,用来管理对象的。容器将创建的对象,把它们连接在一起,配置它们,并管理它们的整个生命周期从创建到销毁

1.2 容器如何工作

官网上一张图如下:

Spring源码分析(一)Spring容器及Spring Bean_spring

图可以理解为,Spring 容器通过我们提交的 POJO 对象(Your Business Objects(POJOs))以及配置元数据(Configuration Metadata)产生一个充分配置的可以使用的系统

这里说的配置元数据,实际上就是我们提供的 XML 配置文件,或者通过注解方式提供的一些配置信息

2.Spring bean

2.1 如何实例化一个 bean

从官网上来看,主要有三种方法:

Spring源码分析(一)Spring容器及Spring Bean_spring_02

  1. 构造方法
  2. 静态工厂方法
  3. 实例工厂方法

这三种例子官网都有具体的演示,可以通过注解查阅部分源码来验证官网的结论

从代码角度进行分析,这里我们直接定位到

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance

通过运行下面这行代码

public static void main(String[] args)  {
   // 通过配置类扫描
   AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
   System.out.println(ac.getBean(OrderService.class));
}

在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance 这个方法的入口打一个端点,如图:

Spring源码分析(一)Spring容器及Spring Bean_官网_03

接下来我们通过代码来分析

Spring源码分析(一)Spring容器及Spring Bean_spring_04

我们主要关注进行实例化的几个方法:

  1. 通过 Supplier 创建 Bean

通过 BeanDefinition 中的 instanceSupplier 直接获取一个是实例化的对象,这个 instanceSupplier 属性我不是很理解,在 XML 标签中以及注解的方式都没有找到方法配置这个属性,后来在 org.springframework.context.support.GenericApplicationContext 这个类中找到了以下两个方法

Spring源码分析(一)Spring容器及Spring Bean_官网_05

经过断点测试,发现这种情况下,在实例化对象时会进入上面的 supplier 方法。下面是测试代码

// AnnotationConfigApplicationContext 是 GenericApplicationContext 的一个子类
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        ac.registerBean("service", Service.class,Service::new);
        ac.refresh();
        System.out.println(ac.getBean("service"));

这个方法一般不常用,这是 Spring 提供的一种方便外部扩展的手段,让开发者能够更加灵活的实例化一个 bean

2. 通过注解的方式来创建 Bean

@Compent,@Service 等注解方式

测试代码:

public static void main(String[] args)  {
   // 通过配置类扫描
   AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
   System.out.println(ac.getBean(OrderService.class));
}


@Component
public class OrderService {
   
}

观察 debug:

Spring源码分析(一)Spring容器及Spring Bean_官网_06

可以发现,代码执行到最后一行,同时我们看代码上面的注释可以知道,当没有进行特殊处理的时候,默认会使用无参构造函数进行对象的实例化

3. 通过普通 XML 的方式

同@Component 注解

4. 通过@Configuration 注解的方式

public class Test {


   public static void main(String[] args)  {
      // 通过配置类扫描
      AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
      // 这里将测试对象换为config即可,同时记得将条件断点更改为beanName.equlas("appconfig")
      System.out.println(ac.getBean(Appconfig.class));
   }
}


@Configuration
public class Appconfig {

 }

Spring源码分析(一)Spring容器及Spring Bean_官网_07

同样,断点也进入了最后一行

5. 通过@Bean 方式

public class Test {


   public static void main(String[] args) {
      AnnotationConfigApplicationContext ac =
            new AnnotationConfigApplicationContext(Appconfig.class);
      System.out.println(ac.getBean("service"));
   }
}


@Configuration
@ComponentScan("com.luban.beanDefinition")
public class Appconfig {
   @Bean
   public Service service() {
      return new Service();
   }
 }


public class Service {
}

在方法入口打个断点如图:

Spring源码分析(一)Spring容器及Spring Bean_实例化_08

断点结果:

Spring源码分析(一)Spring容器及Spring Bean_官网_09

可以发现,通过@Bean方法创建对象时,Spring底层是通过factoryMethod方法进行实例化对象的,Spring会在我们需要实例化这个对象对应的BeanDefinition中记录factoryBeanName是什么(factoryBeanName就是AppConfig),同时记录这个factoryBean中创建对象的factoryMethodName是什么,最后通过factoryBeanName获取一个Bean然后反射调用factoryMethod实例化一个对象。

这里需要注意几个概念:

  1. 这里所说的通过静态工厂方式通过factoryBeanName获取一个Bean,注意这个Bean,不是一个FactoryBean,也就是说不是一个实现了org.springframework.beans.factory.FacrotyBean接口的Bean。
  2. 提到一个概念BeanDefinition,它就是Spring对自己所管理的Bean的一个抽象。之后会出一个专题专门讲解

6. 通过静态工厂方法的方式

测试代码:

public class Test {


   public static void main(String[] args) {
      ClassPathXmlApplicationContext cc =
            new ClassPathXmlApplicationContext("application.xml");
      System.out.println(cc.getBean("MyFactoryBean"));
   }
}

application.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
   <!--  <bean id="myServiceImpl" class="com.luban.service.Service"/>-->


   <!-- the factory bean, which contains a method called get() -->
   <bean id="service" class="com.luban.service.Service">
      <!-- inject any dependencies required by this locator bean -->
   </bean>


   <!-- 测试实例工厂方法创建对象-->
   <bean id="clientService"
        factory-bean="service"
        factory-method="get"/>


   <!--测试静态工厂方法创建对象-->
   <bean id="MyFactoryBean"
        class="com.luban.service.MyFactoryBean"
        factory-method="staticGet"/>
</bean

断点如下:

Spring源码分析(一)Spring容器及Spring Bean_spring_10

可以发现,这种情况也进入了insrantiateUsingFactoryMethod方法中。通过静态工厂方法的方式特殊之处在于,包含了这个静态方法的类,不需要被实例化,不需要被Spring管理,Spring的调用逻辑大概是:

  1. 通过<bean>标签中的class属性得到一个Class对象
  2. 通过Class对象获取到对应的方法名称得到Method对象
  3. 最后反射调用Method.invoke(null, args)

因为是静态方法,方法在执行时,不需要一个对象

7.通过实例工厂方法的方式

测试代码,配置文件不变:

ClassPathXmlApplicationContext cc =
        new ClassPathXmlApplicationContext("application.xml");
    System.out.println(cc.getBean("clientService"));

断点如下:

Spring源码分析(一)Spring容器及Spring Bean_实例化_11

还是执行这个方法,这个方法的执行过程和@Bean方式执行的流程是一样的

到这里,这段代码我们算是结合了官网大致过了一遍,其实还遗留了几个问题:

  1. Spring是如何推断构造函数的?我们在上面验证的都是无参的构造函数,并且只提供了一个构造函数
  2. Spring是如何推断方法的?不管是静态工厂方法还是实例化工厂方法,我们都只在类中提供了一个和配置匹配的方法名,假设我们对方法进行了重载呢?

要说清楚这两个问题需要比较深入的研究代码,同时进行测试。

总结

  1. 对象实例化,只是得到一个对象,还不是一个完全的Spring中的bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的生命周期
  2. Spring官网上说到,在Spring实例化一个对象有三种方式:

构造函数

实例化工厂方法

静态工厂方法

3.自己总结如下结论:

Spring通过解析我们的配置元数据,以及我们提供的类对象得到一个BeanDefiniton对象,通过这个对象可以实例化一个java bean对象。主要流程图如下:

Spring源码分析(一)Spring容器及Spring Bean_spring_12

标签:ac,实例,Spring,public,Bean,源码,class
From: https://blog.51cto.com/u_15668812/7504058

相关文章

  • springboot中配置类型转换,设置开启矩阵变量
    2023-09-17packagecom.hh.springboot05.config;importcom.hh.springboot05.bean.Pet;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.convert.converter.Conver......
  • 【java基础】Token令牌生成 token加密串 生成token Aes加密 Base64加密 JWT 【附
    先看效果:Token令牌-生成工具包括:头部(header)+载荷(payload)+签证(signature) 可以自定义加密盐: 源码:地址一:GitLab地址二:123盘地址三:百度盘提取码:666 ......
  • Spring Security 基于表单的认证和角色权限控制
    SpringSecurity是基于Spring框架提供的一套Web应用安全的完整解决方案,核心功能主要是认证和授权。认证主要是判断用户的合法性,主要体现在登录操作,最常用的认证方式是【基于表单的认证】和【基于OAuth2的认证】。授权主要体现在权限控制,也就是控制用户是否能够访问网站的相关......
  • Spring Security基于令牌的认证
    介绍SpringSecurity是一个功能强大的安全框架,它提供了许多不同的认证和授权选项。其中,基于令牌的认证是一种非常流行的认证方式,它允许用户在不需要提供用户名和密码的情况下进行身份验证。在本文中,我们将深入探讨SpringSecurity的基于令牌的认证机制。令牌的概念令牌是一种用......
  • 深入探讨Spring Security的OAuth2客户端模式
    介绍OAuth2是一种常见的身份验证和授权协议,它允许用户授权第三方应用程序访问他们的资源。SpringSecurity是一个强大的安全框架,它提供了OAuth2客户端模式的支持。在本文中,我们将深入探讨SpringSecurity的OAuth2客户端模式。OAuth2客户端模式OAuth2客户端模式是一种简单的身份......
  • 深入探讨Spring Boot中的Redis缓存
    介绍Redis是一种高性能的内存数据库,常用于缓存和消息队列等场景。在SpringBoot中,我们可以通过集成Redis来实现缓存功能。本文将深入探讨SpringBoot中的Redis缓存,包括如何配置、如何使用以及一些注意事项。配置在SpringBoot中,我们可以通过在application.properties或applicati......
  • 深入探讨Spring Boot的任务调度器
    介绍SpringBoot是一个流行的Java框架,它提供了许多有用的功能,其中之一是任务调度器。任务调度器可以帮助您在特定的时间间隔内执行任务,例如定期备份数据库或发送电子邮件。在本文中,我们将深入探讨SpringBoot的任务调度器,并提供一些实际的代码示例。配置任务调度器要使用Spring......
  • 选择适合你的Spring Cloud Stream Binder
    前言SpringCloudStream是一个用于构建消息驱动微服务的框架,它提供了一种简单的方式来连接消息代理和应用程序。其中最重要的组件是Binder,它负责将应用程序与消息代理连接起来。SpringCloudStream提供了多个Binder实现,包括Kafka、RabbitMQ、Kinesis等。在本文中,我们将深入探讨......
  • 深入探讨Spring Data JDBC的自定义映射
    前言SpringDataJDBC是一个轻量级的JDBC框架,它提供了一种简单的方式来访问关系型数据库。在使用SpringDataJDBC时,我们通常会使用默认的映射策略来将Java对象映射到数据库表中的行。但是,有时候我们需要自定义映射策略来满足特定的需求。本文将深入探讨SpringDataJDBC的自定义......
  • 深入探讨Spring Boot的性能调优
    前言SpringBoot是一个非常流行的Java开发框架,它提供了很多便利的功能,但是在高并发场景下,性能问题也是不可避免的。本文将深入探讨SpringBoot的性能调优,帮助开发者更好地优化应用程序。优化数据库连接池数据库连接池是一个非常重要的组件,它可以提高应用程序的性能。在SpringBo......