Spring中@Component和@Bean的底层逻辑

186 阅读2分钟

在上一篇文章《Spring创建BeanDefinition之路径扫描》中,我们了解了对@ComponentScan注解的处理逻辑。今天,一起来看看对@Component、@Bean的处理逻辑。

本文中源码来自Spring 5.3.x分支,github源码地址:github.com/spring-proj…

一 路径扫描@Component

当从@ComponentScan中获得包路径后,将在ClassPathBeanDefinitionScanner#doScan处理,该方法将在指定的基包中执行扫描,返回已注册的BeanDefinition。

1.1 获取候选Bean

findCandidateComponents方法,支持两种扫描方式:

  1. 索引文件: 当在类路径下创建spring.components文件,在其中指定组件全类名,直接告诉Spring你要加载的Bean类名。好处在于加速扫描过程。
  2. 路径扫描: 读取路径下所有.class文件,逐个判断其是否是Component。

来看路径扫描:

  1. 首先,处理包路径,使变成“classpath*:basePackage/**/*.class”,将class文件封装为Resource对象;
  2. 逐个处理resource,使用ASM技术从字节码中获取Metadata,并不会加载类到JVM中;
  3. 根据类的Metadata,进行excludeFilters和includeFilters判断,以及@Conditional筛选;通过后才会继续;
  4. 当创建ClassPathBeanDefinitionScanner对象时,默认会添加@Component到includeFilters中,类上有@Component时才能成为候选Bean;
  5. 为通过筛选的类,创建ScannedGenericBeanDefinition,再次判断对应的类是不是嵌套类、接口或抽象类;如果是则不能成为Bean。

1.2 注册BeanDefinition

  • 对BeanDefinition,进一步设置属性如scope、lazyInit、primary等,生成beanName;
  • 判断容器中是否已存在同名的beanName,存在时则判断两个BeanDefinition是否兼容,如果兼容则本次放弃注册;
  • 所谓注册,就是将beanName与BeanDefinition的映射,添加到DefaultListableBeanFactory#beanDefinitionMap中。

二 @Bean底层逻辑

在ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry中,

当带有@Component(包括@Service等)的类中有@Bean注解的方法时,会将这些方法封装为BeanMethod,放到Map缓存中。

BeanMethod是ConfigurationMethod的子类,有两个属性:

  • MethodMetadata metadata:方法的元数据,如MethodName、ReturnTypeName等;
  • ConfigurationClass configurationClass:该方法所在的类信息。

在ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod中,根据BeanMethod生成BeanDefinition,向容器中注册

具体流程为:

  1. 执行@Conditional判断,如果shouldSkip则跳过;
  2. 斟酌beanName,先从@Bean的name属性(String[]类型)获取第一个元素,其次用methodName;将@Bean的name属性的其他剩余值作为别名;
  3. 创建ConfigurationClassBeanDefinition对象;
  4. 当方法被static修饰时,类似于静态工厂模式,与下面xml声明相似;
<bean id="user" class="com.xiakexing.service.UserService" factorymethod="createUser" />
  1. 当方法未被static修饰时,类似于工厂方法模式,与下面xml声明相似;
<bean id="userService" class="com.xiakexing.service.UserService"/>
<bean id="user" factorybean="userService" factorymethod="createUser"/>
  1. 设置beanClassName、factoryBeanName、factoryMethodName、initMethod、destroyMethod等属性;
  2. 注册BeanDefinition。

来看一个示例

import com.xiakexing.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserService {

    public void test() {
        System.out.println("hello spring");
    }

    @Bean(name = {"user"})
    public User createUser() {
        return new User("Tom", 25);
    }
}

将createUser()封装为BeanMethod对象如下: 为createUser()创建的BeanDefinition,部分信息如下: