深入分析Spring BeanDefinition的构造元信息
Spring BeanDefinition元信息定义方式
Bean Definition
是一个包含Bean
元数据的对象。它描述了如何创建Bean
实例、Bean
属性的值以及Bean
之间的依赖关系。可以使用多种方式来定义 Bean Definition 元信息,包括:
- XML 配置文件:使用
<bean>
标签定义 Bean 元数据,可以指定Bean
类型、属性值和依赖项等信息。 - 注解:使用
@Component
、@Service
、@Repository
等注解标记Bean
类,并使用@Autowired
注解注入依赖项。 - Java 配置类:使用
@Configuration
和@Bean
注解定义Bean
元数据,可以指定Bean
类型、属性值和依赖项等信息。
除此之外,还可以通过实现 BeanDefinitionRegistryPostProcessor
接口来自定义 Bean Definition
的生成过程。这个接口有一个方法 postProcessBeanDefinitionRegistry()
,允许开发人员动态地添加、修改或删除Bean Definition
元信息。
XML配置文件定义Bean的元数据
<bean id="user" class="org.thinging.in.spring.ioc.overview.domain.User"> <property name="id" value="1"/> <property name="name" value="Liutx"/> </bean>
注解定义Bean的元数据
@Service(value = "HelloService") public class HelloService { private final Logger logger = LoggerFactory.getLogger(HelloService.class); private final HelloAsyncService helloAsyncService; public HelloService(HelloAsyncService helloAsyncService) { this.helloAsyncService = helloAsyncService; } }
value = "HelloService" 即为Bean:HelloService的元数据,在构造方法中的依赖关系同样属于元数据。
Java 配置类定义Bean的元数据
@Component(value = "balanceRedisProcessor") public class BalanceRedisProcessorService implements EntryHandler<Balance>, Runnable { @Autowired(required = true) public BalanceRedisProcessorService(RedisUtils redisUtils, CanalConfig canalConfig, @Qualifier("ownThreadPoolExecutor") Executor executor, RocketMQProducer rocketMqProducer) { this.redisUtils = redisUtils; this.canalConfig = canalConfig; this.executor = executor; this.rocketMQProducer = rocketMqProducer; } }
@Component(value = "balanceRedisProcessor") 是Bean:BalanceRedisProcessorService的元数据,在构造方法中的依赖关系同样属于元数据。
BeanDefinition的元数据解析
在Spring中,无论是通过XML、注解、Java配置类定义Bean元数据,最终都是需要转换成BeanDefinition
对象,然后被注册到Spring容器中。
而BeanDefinition
的创建过程,确实是通过AbstractBeanDefinition
及其派生类、``等一系列工具类实现的。
- 当我们使用
XML
配置时,Spring会解析XML
文件,将其中的Bean元数据信息转换成对应的BeanDefinition
对象,然后注册到Spring容器中。在这个过程中,Spring内部会使用XmlBeanDefinitionReader
等相关工具类,将XML文件中定义的Bean元数据转换成BeanDefinition
对象。 - 当我们使用注解方式或Java配置类方式定义Bean元数据时,
Spring
会扫描相应的注解或Java配置类,然后根据其定义生成对应的BeanDefinition
对象,并注册到Spring
容器中。在这个过程中,Spring内部会使用AnnotationConfigApplicationContext
等相关工具类,将注解或Java配置类中定义的Bean元数据转换成BeanDefinition
对象。
源码分析XML是如何转化为Spring BeanDefinition的
将xml文件中的配置转为为BeanDefinition
需要依赖自XmlBeanDefinitionReader
类中的loadBeanDefinitions
方法。
选自:Spring Framework 5.2.20 RELEASE版本的XmlBeanDefinitionReader
。
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded"){ @Override protected Set<EncodedResource> initialValue() { return new HashSet<>(4); } }; /** * Load bean definitions from the specified XML file. * @param encodedResource the resource descriptor for the XML file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try (InputStream inputStream = encodedResource.getResource().getInputStream()) { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 实际上从指定的 XML 文件加载 Bean 定义 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } } //实际上从指定的 XML 文件加载 Bean 定义 /** * Actually load bean definitions from the specified XML file. * @param inputSource the SAX InputSource to read from * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #doLoadDocument * @see #registerBeanDefinitions */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } }
- 使用
ThreadLocal
线程级别的变量存储带有编码资源的集合,保证每个线程都可以访问到XmlBeanDefinitionReader
在加载XML配置文件时当前正在加载的资源,以确保加载过程中的完整性和正确性。 - 在
ThreadLocal
中获取到当前正在加载的xml资源,转换为输入流 - 开始执行
doLoadBeanDefinitions
,实际上从指定的 XML 文件加载 Bean 定义,该方法会返回加载的Bean定义数量。 doLoadBeanDefinitions
方法中,首先调用doLoadDocument
方法加载XML文件并生成一个Document对象。- 然后,调用
registerBeanDefinitions
方法来注册Bean
定义,将其放入Spring容器中。该方法会返回注册的Bean定义数量。 - 最后,根据需要记录日志信息,并返回加载的Bean定义数量。
源码分析配置类、注解是如何转化为Spring BeanDefinition的
在Spring中,配置类和注解都可以被转换为Bean
定义(BeanDefinition
)。下面是关于如何将配置类和注解转换为Bean
定义的简要源码分析:
- 配置类转换为Bean定义:
- 当使用Java配置类时,Spring会通过解析配置类中的注解来生成相应的Bean定义。主要实现是通过
ConfigurationClassParser
类完成的。 ConfigurationClassParser
会解析配置类上的注解,包括@Configuration
、@ComponentScan
、@Bean
等,然后将其转换为对应的Bean
定义。- 在解析过程中,Spring会创建一个
ConfigurationClass
对象表示配置类,并根据不同的注解类型生成相应的Bean
定义,包括RootBeanDefinition
和MethodMetadata
。 RootBeanDefinition
代表配置类本身,而MethodMetadata
代表配置类中的方法上的注解,例如@Bean
注解。- 最终,这些生成的Bean定义会被注册到
DefaultListableBeanFactory
中,以供后续的Bean实例化和依赖注入。
- 当使用Java配置类时,Spring会通过解析配置类中的注解来生成相应的Bean定义。主要实现是通过
- 注解转换为
Bean
定义:
- 当使用注解方式配置
Bean
时,Spring会扫描指定的包或类,并解析其中的注解来生成Bean定义。 - Spring提供了
AnnotationBeanDefinitionReader
类用于处理注解,它会扫描指定的包路径或类,并根据注解生成相应的Bean定义。 - 在扫描过程中,
AnnotationBeanDefinitionReader
会解析常见的注解,比如@Component
、@Controller
、@Service
、@Repository
等,然后生成相应的Bean定义。 - 注解生成的Bean定义同样会被注册到DefaultListableBeanFactory中,以供后续的Bean实例化和依赖注入。
- 当使用注解方式配置
总而言之,无论是配置类还是注解,Spring都会通过解析注解并生成对应的Bean定义,最终将这些Bean定义注册到DefaultListableBeanFactory
中。这样,在容器启动时,Spring就能够根据这些Bean
定义来实例化Bean
并进行依赖注入。
配置类、注解转换为Spring BeanDefition源码后续博客中展示,敬请期待。
如何手动构造BeanDefinition
Bean定义
public class User { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + ''' + '}'; } }
通过BeanDefinitionBuilder构建
//通过BeanDefinitionBuilder构建 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class); //通过属性设置 beanDefinitionBuilder.addPropertyValue("id", 1L) .addPropertyValue("name","公众号:种棵代码技术树"); //获取BeanDefinition实例 BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition(); // BeanDefinition 并非 Bean 终态,可以自定义修改 System.out.println(beanDefinition);
通过AbstractBeanDefinition以及派生类
// 2. 通过 AbstractBeanDefinition 以及派生类 GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition(); //设置Bean类型 genericBeanDefinition.setBeanClass(User.class); //通过 MutablePropertyValues 批量操作属性 MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.add("id",1L) .add("name","公众号:种棵代码技术树"); // 通过 set MutablePropertyValues 批量操作属性 genericBeanDefinition.setPropertyValues(propertyValues);
最后
以上就是深入分析Spring BeanDefinition的构造元信息的详细内容,更多关于Spring BeanDefinition构造元信息的资料请关注脚本之家其它相关文章!
最新评论