diff --git a/README.md b/README.md index 8142f7e..653ba1a 100644 --- a/README.md +++ b/README.md @@ -39,52 +39,31 @@ + IOC容器 + [关于IOC容器源码分析](spring-core-ioc/README.md) + 后置处理器与初始化 - + [关于BeanFactoryPostProcessor源码分析](spring-interface-beanFactoryPostProcessor/README.md) - + [关于BeanDefinitionRegistryPostProcessor源码分析](spring-interface-beanDefinitionRegistryPostProcessor/README.md) - + [关于BeanPostProcessor源码分析](spring-interface-beanPostProcessor/README.md) - + [关于InstantiationAwareBeanPostProcessor源码分析](spring-interface-instantiationAwareBeanPostProcessor/README.md) - + [关于DestructionAwareBeanPostProcessor源码分析](spring-interface-destructionAwareBeanPostProcessor/README.md) - + [关于MergedBeanDefinitionPostProcessor源码分析](spring-interface-mergedBeanDefinitionPostProcessor/README.md) - + [关于SmartInstantiationAwareBeanPostProcessor源码分析](spring-interface-smartInstantiationAwareBeanPostProcessor/README.md) - + [关于InitializingBean源码分析](spring-interface-initializingBean/README.md) - + [关于DisposableBean源码分析](spring-interface-disposableBean/README.md) - + [关于SmartInitializingSingleton源码分析](spring-interface-smartInitializingSingleton/README.md) + + [关于BeanFactoryPostProcessor源码分析](spring-interface/spring-interface-beanFactoryPostProcessor/README.md) + + [关于BeanDefinitionRegistryPostProcessor源码分析](spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/README.md) + + [关于BeanPostProcessor源码分析](spring-interface/spring-interface-beanPostProcessor/README.md) + + [关于InstantiationAwareBeanPostProcessor源码分析](spring-interface/spring-interface-instantiationAwareBeanPostProcessor/README.md) + + [关于DestructionAwareBeanPostProcessor源码分析](spring-interface/spring-interface-destructionAwareBeanPostProcessor/README.md) + + [关于MergedBeanDefinitionPostProcessor源码分析](spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/README.md) + + [关于SmartInstantiationAwareBeanPostProcessor源码分析](spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/README.md) + + [关于InitializingBean源码分析](spring-interface/spring-interface-initializingBean/README.md) + + [关于DisposableBean源码分析](spring-interface/spring-interface-disposableBean/README.md) + + [关于SmartInitializingSingleton源码分析](spring-interface/spring-interface-smartInitializingSingleton/README.md) + Aware接口 - + [关于BeanNameAware源码分析](spring-aware-beanNameAware/README.md) - + [关于BeanClassLoaderAware源码分析](spring-aware-beanClassLoaderAware/README.md) - + [关于BeanFactoryAware源码分析](spring-aware-beanFactoryAware/README.md) - + [关于EnvironmentAware源码分析](spring-aware-environmentAware/README.md) - + [关于EmbeddedValueResolverAware源码分析](spring-aware-embeddedValueResolverAware/README.md) - + [关于ResourceLoaderAware源码分析](spring-aware-resourceLoaderAware/README.md) - + [关于ApplicationEventPublisherAware源码分析](spring-aware-applicationEventPublisherAware/README.md) - + [关于MessageSourceAware源码分析](spring-aware-messageSourceAware/README.md) - + [关于ApplicationStartupAware源码分析](spring-aware-applicationStartupAware/README.md) - + [关于ApplicationContextAware源码分析](spring-aware-applicationContextAware/README.md) - + [关于ImportAware源码分析](spring-aware-importAware/README.md) + + [关于BeanNameAware源码分析](spring-aware/spring-aware-beanNameAware/README.md) + + [关于BeanClassLoaderAware源码分析](spring-aware/spring-aware-beanClassLoaderAware/README.md) + + [关于BeanFactoryAware源码分析](spring-aware/spring-aware-beanFactoryAware/README.md) + + [关于EnvironmentAware源码分析](spring-aware/spring-aware-environmentAware/README.md) + + [关于EmbeddedValueResolverAware源码分析](spring-aware/spring-aware-embeddedValueResolverAware/README.md) + + [关于ResourceLoaderAware源码分析](spring-aware/spring-aware-resourceLoaderAware/README.md) + + [关于ApplicationEventPublisherAware源码分析](spring-aware/spring-aware-applicationEventPublisherAware/README.md) + + [关于MessageSourceAware源码分析](spring-aware/spring-aware-messageSourceAware/README.md) + + [关于ApplicationStartupAware源码分析](spring-aware/spring-aware-applicationStartupAware/README.md) + + [关于ApplicationContextAware源码分析](spring-aware/spring-aware-applicationContextAware/README.md) + + [关于ImportAware源码分析](spring-aware/spring-aware-importAware/README.md) + 核心注解 - + [关于@Configuration源码分析](spring-annotation-configuration/README.md) - + [关于@ComponentScan源码分析](spring-annotation-componentScan/README.md) - + [关于@Bean源码分析](spring-annotation-bean/README.md) - + [关于@Import源码分析](spring-annotation-import/README.md) - + [关于@PropertySource源码分析](spring-annotation-propertySource/README.md) -+ Bean工厂 - + 关于BeanFactory源码分析 - + 关于HierarchicalBeanFactory源码分析 - + 关于ListableBeanFactory源码分析 - + 关于AutowireCapableBeanFactory源码分析 - + 关于ConfigurableBeanFactory源码分析 - + 关于SingletonBeanRegistry源码分析 - + 关于BeanDefinition源码分析 - + 关于BeanDefinitionRegistry源码分析 - + 关于FactoryBean源码分析 -+ 应用上下文 - - 关于ApplicationContext源码分析 - - 关于ConfigurableApplicationContext源码分析 - - 关于WebApplicationContext源码分析 - - 关于ApplicationEventPublisher源码分析 - - 关于ApplicationListener源码分析 -+ 环境变量 - - 关于Environment源码分析 - - 关于PropertyResolver源码分析 - - 关于PropertySources源码分析 - - 关于Profile源码分析 \ No newline at end of file + + [关于@Configuration源码分析](spring-annotation/spring-annotation-configuration/README.md) + + [关于@ComponentScan源码分析](spring-annotation/spring-annotation-componentScan/README.md) + + [关于@Bean源码分析](spring-annotation/spring-annotation-bean/README.md) + + [关于@Import源码分析](spring-annotation/spring-annotation-import/README.md) + + [关于@PropertySource源码分析](spring-annotation/spring-annotation-propertySource/README.md) \ No newline at end of file diff --git a/pom.xml b/pom.xml index 17868ab..f41a7e0 100644 --- a/pom.xml +++ b/pom.xml @@ -18,40 +18,10 @@ - spring-annotation-configuration - spring-annotation-bean - spring-annotation-import - spring-annotation-propertySource - spring-annotation-componentScan - spring-interface-beanFactoryPostProcessor - spring-interface-beanPostProcessor - spring-interface-environment - spring-interface-applicationListener - spring-interface-importSelector - spring-interface-importBeanDefinitionRegistrar - spring-interface-resource - spring-interface-embeddedValueResolver - spring-interface-factoryBean - spring-interface-beanDefinitionRegistryPostProcessor - spring-interface-instantiationAwareBeanPostProcessor - spring-interface-destructionAwareBeanPostProcessor - spring-interface-mergedBeanDefinitionPostProcessor - spring-interface-smartInstantiationAwareBeanPostProcessor - spring-interface-initializingBean - spring-interface-disposableBean spring-core-ioc - spring-interface-smartInitializingSingleton - spring-aware-beanNameAware - spring-aware-beanFactoryAware - spring-aware-beanClassLoaderAware - spring-aware-applicationContextAware - spring-aware-applicationEventPublisherAware - spring-aware-environmentAware - spring-aware-resourceLoaderAware - spring-aware-embeddedValueResolverAware - spring-aware-messageSourceAware - spring-aware-applicationStartupAware - spring-aware-importAware + spring-annotation + spring-aware + spring-interface diff --git a/spring-aware-beanClassLoaderAware/pom.xml b/spring-annotation/pom.xml similarity index 54% rename from spring-aware-beanClassLoaderAware/pom.xml rename to spring-annotation/pom.xml index 8f4f6d6..95510b1 100644 --- a/spring-aware-beanClassLoaderAware/pom.xml +++ b/spring-annotation/pom.xml @@ -7,13 +7,17 @@ com.xcs.spring 0.0.1-SNAPSHOT + 4.0.0 + spring-annotation + pom - spring-aware-beanClassLoaderAware - - - 11 - 11 - + + spring-annotation-configuration + spring-annotation-bean + spring-annotation-import + spring-annotation-propertySource + spring-annotation-componentScan + \ No newline at end of file diff --git a/spring-annotation-bean/README.md b/spring-annotation/spring-annotation-bean/README.md similarity index 99% rename from spring-annotation-bean/README.md rename to spring-annotation/spring-annotation-bean/README.md index 782b896..3afd212 100644 --- a/spring-annotation-bean/README.md +++ b/spring-annotation/spring-annotation-bean/README.md @@ -25,7 +25,7 @@ ### 一、前置条件 -+ [关于BeanDefinitionRegistryPostProcessor源码分析](../spring-interface-beanDefinitionRegistryPostProcessor/README.md) ++ [关于BeanDefinitionRegistryPostProcessor源码分析](../../spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/README.md) ### 二、注解描述 diff --git a/spring-annotation-bean/pom.xml b/spring-annotation/spring-annotation-bean/pom.xml similarity index 88% rename from spring-annotation-bean/pom.xml rename to spring-annotation/spring-annotation-bean/pom.xml index d6eec43..7fff1d5 100644 --- a/spring-annotation-bean/pom.xml +++ b/spring-annotation/spring-annotation-bean/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-annotation com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-annotation-bean/src/main/java/com/xcs/spring/BeanApplication.java b/spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/BeanApplication.java similarity index 100% rename from spring-annotation-bean/src/main/java/com/xcs/spring/BeanApplication.java rename to spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/BeanApplication.java diff --git a/spring-annotation-bean/src/main/java/com/xcs/spring/bean/MyBean.java b/spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/bean/MyBean.java similarity index 100% rename from spring-annotation-bean/src/main/java/com/xcs/spring/bean/MyBean.java rename to spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/bean/MyBean.java diff --git a/spring-annotation-bean/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 86% rename from spring-annotation-bean/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/config/MyConfiguration.java index cafeff0..6e1b89c 100644 --- a/spring-annotation-bean/src/main/java/com/xcs/spring/config/MyConfiguration.java +++ b/spring-annotation/spring-annotation-bean/src/main/java/com/xcs/spring/config/MyConfiguration.java @@ -2,7 +2,6 @@ package com.xcs.spring.config; import com.xcs.spring.bean.MyBean; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** diff --git a/spring-annotation-componentScan/README.md b/spring-annotation/spring-annotation-componentScan/README.md similarity index 100% rename from spring-annotation-componentScan/README.md rename to spring-annotation/spring-annotation-componentScan/README.md diff --git a/spring-annotation-componentScan/pom.xml b/spring-annotation/spring-annotation-componentScan/pom.xml similarity index 88% rename from spring-annotation-componentScan/pom.xml rename to spring-annotation/spring-annotation-componentScan/pom.xml index 5009752..febe7a8 100644 --- a/spring-annotation-componentScan/pom.xml +++ b/spring-annotation/spring-annotation-componentScan/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-annotation com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-annotation-componentScan/src/main/java/com/xcs/spring/ComponentScanApplication.java b/spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/ComponentScanApplication.java similarity index 100% rename from spring-annotation-componentScan/src/main/java/com/xcs/spring/ComponentScanApplication.java rename to spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/ComponentScanApplication.java diff --git a/spring-annotation-componentScan/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-annotation-componentScan/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-annotation-componentScan/src/main/java/com/xcs/spring/repository/UserRepository.java b/spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/repository/UserRepository.java similarity index 100% rename from spring-annotation-componentScan/src/main/java/com/xcs/spring/repository/UserRepository.java rename to spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/repository/UserRepository.java diff --git a/spring-annotation-componentScan/src/main/java/com/xcs/spring/service/AdminService.java b/spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/service/AdminService.java similarity index 100% rename from spring-annotation-componentScan/src/main/java/com/xcs/spring/service/AdminService.java rename to spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/service/AdminService.java diff --git a/spring-annotation-componentScan/src/main/java/com/xcs/spring/service/UserService.java b/spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/service/UserService.java similarity index 100% rename from spring-annotation-componentScan/src/main/java/com/xcs/spring/service/UserService.java rename to spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/service/UserService.java diff --git a/spring-annotation-componentScan/src/main/java/com/xcs/spring/special/SpecialComponent.java b/spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/special/SpecialComponent.java similarity index 100% rename from spring-annotation-componentScan/src/main/java/com/xcs/spring/special/SpecialComponent.java rename to spring-annotation/spring-annotation-componentScan/src/main/java/com/xcs/spring/special/SpecialComponent.java diff --git a/spring-annotation-configuration/README.md b/spring-annotation/spring-annotation-configuration/README.md similarity index 100% rename from spring-annotation-configuration/README.md rename to spring-annotation/spring-annotation-configuration/README.md diff --git a/spring-annotation-configuration/pom.xml b/spring-annotation/spring-annotation-configuration/pom.xml similarity index 88% rename from spring-annotation-configuration/pom.xml rename to spring-annotation/spring-annotation-configuration/pom.xml index 0d09b70..a44f946 100644 --- a/spring-annotation-configuration/pom.xml +++ b/spring-annotation/spring-annotation-configuration/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-annotation com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-annotation-configuration/src/main/java/com/xcs/spring/ConfigurationApplication.java b/spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/ConfigurationApplication.java similarity index 100% rename from spring-annotation-configuration/src/main/java/com/xcs/spring/ConfigurationApplication.java rename to spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/ConfigurationApplication.java diff --git a/spring-annotation-configuration/src/main/java/com/xcs/spring/bean/MyBean.java b/spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/bean/MyBean.java similarity index 100% rename from spring-annotation-configuration/src/main/java/com/xcs/spring/bean/MyBean.java rename to spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/bean/MyBean.java diff --git a/spring-annotation-configuration/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-annotation-configuration/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-annotation/spring-annotation-configuration/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-annotation-import/README.md b/spring-annotation/spring-annotation-import/README.md similarity index 100% rename from spring-annotation-import/README.md rename to spring-annotation/spring-annotation-import/README.md diff --git a/spring-annotation-import/pom.xml b/spring-annotation/spring-annotation-import/pom.xml similarity index 88% rename from spring-annotation-import/pom.xml rename to spring-annotation/spring-annotation-import/pom.xml index ee9173f..5f77530 100644 --- a/spring-annotation-import/pom.xml +++ b/spring-annotation/spring-annotation-import/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-annotation com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/ImportApplication.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/ImportApplication.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/ImportApplication.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/ImportApplication.java diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBean.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBean.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBean.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBean.java diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanA.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanA.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanA.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanA.java diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanB.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanB.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanB.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanB.java diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanC.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanC.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanC.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/bean/MyBeanC.java diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/config/MyDeferredImportSelector.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyDeferredImportSelector.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/config/MyDeferredImportSelector.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyDeferredImportSelector.java diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportBeanDefinitionRegistrar.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportBeanDefinitionRegistrar.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportBeanDefinitionRegistrar.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportBeanDefinitionRegistrar.java diff --git a/spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportSelector.java b/spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportSelector.java similarity index 100% rename from spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportSelector.java rename to spring-annotation/spring-annotation-import/src/main/java/com/xcs/spring/config/MyImportSelector.java diff --git a/spring-annotation-propertySource/README.md b/spring-annotation/spring-annotation-propertySource/README.md similarity index 100% rename from spring-annotation-propertySource/README.md rename to spring-annotation/spring-annotation-propertySource/README.md diff --git a/spring-annotation-propertySource/pom.xml b/spring-annotation/spring-annotation-propertySource/pom.xml similarity index 88% rename from spring-annotation-propertySource/pom.xml rename to spring-annotation/spring-annotation-propertySource/pom.xml index 950c809..4f832f7 100644 --- a/spring-annotation-propertySource/pom.xml +++ b/spring-annotation/spring-annotation-propertySource/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-annotation com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-annotation-propertySource/src/main/java/com/xcs/spring/PropertySourceApplication.java b/spring-annotation/spring-annotation-propertySource/src/main/java/com/xcs/spring/PropertySourceApplication.java similarity index 100% rename from spring-annotation-propertySource/src/main/java/com/xcs/spring/PropertySourceApplication.java rename to spring-annotation/spring-annotation-propertySource/src/main/java/com/xcs/spring/PropertySourceApplication.java diff --git a/spring-annotation-propertySource/src/main/java/com/xcs/spring/config/MyBeanConfig.java b/spring-annotation/spring-annotation-propertySource/src/main/java/com/xcs/spring/config/MyBeanConfig.java similarity index 100% rename from spring-annotation-propertySource/src/main/java/com/xcs/spring/config/MyBeanConfig.java rename to spring-annotation/spring-annotation-propertySource/src/main/java/com/xcs/spring/config/MyBeanConfig.java diff --git a/spring-annotation-propertySource/src/main/resources/my-application.yml b/spring-annotation/spring-annotation-propertySource/src/main/resources/my-application.yml similarity index 100% rename from spring-annotation-propertySource/src/main/resources/my-application.yml rename to spring-annotation/spring-annotation-propertySource/src/main/resources/my-application.yml diff --git a/spring-aware-beanFactoryAware/pom.xml b/spring-aware-beanFactoryAware/pom.xml deleted file mode 100644 index 252d73f..0000000 --- a/spring-aware-beanFactoryAware/pom.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - spring-reading - com.xcs.spring - 0.0.1-SNAPSHOT - - 4.0.0 - - spring-aware-beanFactoryAware - - - 11 - 11 - - - \ No newline at end of file diff --git a/spring-aware-beanNameAware/pom.xml b/spring-aware-beanNameAware/pom.xml deleted file mode 100644 index ef9e248..0000000 --- a/spring-aware-beanNameAware/pom.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - spring-reading - com.xcs.spring - 0.0.1-SNAPSHOT - - 4.0.0 - - spring-aware-beanNameAware - - - 11 - 11 - - - \ No newline at end of file diff --git a/spring-aware-resourceLoaderAware/pom.xml b/spring-aware-resourceLoaderAware/pom.xml deleted file mode 100644 index 621efd5..0000000 --- a/spring-aware-resourceLoaderAware/pom.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - spring-reading - com.xcs.spring - 0.0.1-SNAPSHOT - - 4.0.0 - - spring-aware-resourceLoaderAware - - - 11 - 11 - - - \ No newline at end of file diff --git a/spring-aware/pom.xml b/spring-aware/pom.xml new file mode 100644 index 0000000..556a1d4 --- /dev/null +++ b/spring-aware/pom.xml @@ -0,0 +1,29 @@ + + + + spring-reading + com.xcs.spring + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aware + pom + + + spring-aware-beanNameAware + spring-aware-beanFactoryAware + spring-aware-beanClassLoaderAware + spring-aware-applicationContextAware + spring-aware-applicationEventPublisherAware + spring-aware-environmentAware + spring-aware-resourceLoaderAware + spring-aware-embeddedValueResolverAware + spring-aware-messageSourceAware + spring-aware-applicationStartupAware + spring-aware-importAware + + + \ No newline at end of file diff --git a/spring-aware-applicationContextAware/README.md b/spring-aware/spring-aware-applicationContextAware/README.md similarity index 97% rename from spring-aware-applicationContextAware/README.md rename to spring-aware/spring-aware-applicationContextAware/README.md index 2126dfe..5166411 100644 --- a/spring-aware-applicationContextAware/README.md +++ b/spring-aware/spring-aware-applicationContextAware/README.md @@ -1,538 +1,538 @@ -## ApplicationContextAware - -- [ApplicationContextAware](#applicationcontextaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`ApplicationContextAware` 接口,允许我们访问当前的应用上下文 (`ApplicationContext`)。这通常在某些Spring bean需要访问应用上下文本身或其内部其他bean时会有用。 - -### 二、接口源码 - -`ApplicationContextAware` 是 Spring 框架中的一个接口,主要用于那些需要感知或交互其所在的 ApplicationContext 的 Spring beans。例如,可能需要发布事件或查询 ApplicationContext 中的其他 beans。 - -```java -/** - * 该接口应由希望接收其运行的 ApplicationContext 通知的任何对象实现。 - * - * 例如,当对象需要访问一组合作的bean时,实现此接口是有意义的。 - * 注意,通过bean引用进行配置优于仅为查找bean的目的而实现此接口。 - * - * 如果对象需要访问文件资源(即希望调用getResource), - * 想要发布一个应用事件,或需要访问MessageSource,也可以实现此接口。 - * 但在这种特定情况下,最好实现更为特定的 ResourceLoaderAware, - * ApplicationEventPublisherAware 或 MessageSourceAware 接口。 - * - * 注意,文件资源依赖也可以作为类型为 org.springframework.core.io.Resource 的bean属性暴露, - * 通过字符串填充,由bean工厂自动进行类型转换。这样就不需要为访问特定文件资源而实现任何回调接口了。 - * - * org.springframework.context.support.ApplicationObjectSupport 是应用对象的便利基类, - * 它实现了此接口。 - * - * 关于所有bean生命周期方法的列表,请参阅 - * org.springframework.beans.factory.BeanFactory BeanFactory的javadocs。 - * - * @author Rod Johnson - * @author Juergen Hoeller - * @author Chris Beams - * @see ResourceLoaderAware - * @see ApplicationEventPublisherAware - * @see MessageSourceAware - * @see org.springframework.context.support.ApplicationObjectSupport - * @see org.springframework.beans.factory.BeanFactoryAware - */ -public interface ApplicationContextAware extends Aware { - - /** - * 设置此对象运行的 ApplicationContext。 - * 此调用通常用于初始化对象。 - * 此方法在普通bean属性被填充之后调用,但在诸如 org.springframework.beans.factory.InitializingBean#afterPropertiesSet() - * 这样的初始化回调或自定义初始化方法之前调用。 - * 在 ResourceLoaderAware#setResourceLoader, - * ApplicationEventPublisherAware#setApplicationEventPublisher 和 - * MessageSourceAware 之后调用(如果适用)。 - * - * @param applicationContext 要由此对象使用的 ApplicationContext 对象 - * @throws ApplicationContextException 如果上下文初始化出错 - * @throws BeansException 如果应用上下文方法抛出异常 - * @see org.springframework.beans.factory.BeanInitializationException - */ - void setApplicationContext(ApplicationContext applicationContext) throws BeansException; - -} -``` - -### 三、主要功能 - -**动态查找其他Beans**:尽管我们通常使用依赖注入来获取其他beans的引用,但在某些动态或复杂情况下,bean可能需要在运行时查找其他beans。 - -**发布事件**:通过 `ApplicationContext`,bean可以发布应用级事件,这些事件可以被其他beans捕获和处理,这是实现松耦合交互的一种方法。 - -**资源加载**:`ApplicationContext` 扩展了 `ResourceLoader`,因此bean可以使用它来加载外部资源,如文件或URL。 - -**访问消息源**:对于支持国际化的应用程序,bean可以通过 `ApplicationContext` 访问消息源,从而解析特定的消息。 - -**访问其他应用上下文服务**:除了上述功能,`ApplicationContext` 还提供了其他一些服务,例如与JNDI交互、访问应用的环境属性等。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationContextAware`类型的bean,最后调用`publish`方法用于发布一个事件。 - -```java -public class ApplicationContextAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyApplicationContextAware contextAware = context.getBean(MyApplicationContextAware.class); - contextAware.publish("hello world"); - } -} -``` - -这里使用`@Bean`注解,定义了两个Bean,是为了确保`MyEventListener`, `MyApplicationContextAware` 被 Spring 容器执行 - -```java -@Configuration -public class MyConfiguration { - - @Bean - public MyApplicationContextAware myApplicationContextAware(){ - return new MyApplicationContextAware(); - } - - @Bean - public MyEventListener MyEventListener(){ - return new MyEventListener(); - } -} -``` - - `MyApplicationContextAware` 类使用 `ApplicationContextAware` 来获取 `ApplicationContext` 的引用,并使用这个引用来发布自定义事件。 - -```java -public class MyApplicationContextAware implements ApplicationContextAware { - - private ApplicationContext context; - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; - } - - public void publish(String message) { - context.publishEvent(new MyEvent(this, message)); - } -} -``` - -`MyEvent` 是我们自定义的 Spring 应用事件,用于传递一个字符串消息。 - -```java -public class MyEvent extends ApplicationEvent { - - private final String message; - - public MyEvent(Object source, String message) { - super(source); - this.message = message; - } - - public String getMessage() { - return message; - } -} -``` - -`MyEventListener` 是一个监听器。当 `MyEvent` 事件被发布时,此监听器会自动被触发,执行 `onApplicationEvent` 方法的逻辑。 - -```java -public class MyEventListener implements ApplicationListener { - - @Override - public void onApplicationEvent(MyEvent event) { - System.out.println("Received my event - " + event.getMessage()); - } -} -``` - -运行结果发现,这表示监听器成功地捕获了该事件并处理了它。 - -```java -Received my event - hello world -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: BeanClassLoaderAware时序图 - participant ApplicationContextAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant ApplicationContextAwareProcessor - participant MyApplicationContextAware - - ApplicationContextAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 - AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 - ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 - ApplicationContextAwareProcessor->>MyApplicationContextAware:setApplicationContext(context)
设置应用上下文 - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>ApplicationContextAwareApplication:初始化完成 - -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationContextAware`类型的bean,最后调用`publish`方法用于发布一个事件。 - -```java -public class ApplicationContextAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyApplicationContextAware contextAware = context.getBean(MyApplicationContextAware.class); - contextAware.publish("hello world"); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - Object wrappedBean = bean; - if (mbd == null || !mbd.isSynthetic()) { - wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); - } - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 - -```java -@Override -public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) - throws BeansException { - - Object result = existingBean; - for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; - } - result = current; - } - return result; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 - -```java -@Override -@Nullable -public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || - bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || - bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || - bean instanceof ApplicationStartupAware)) { - return bean; - } - - // ... [代码部分省略以简化] - - invokeAwareInterfaces(bean); - - return bean; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 - -```java -private void invokeAwareInterfaces(Object bean) { - // ... [代码部分省略以简化] - - // 对ApplicationContextAware接口进行回调 - if (bean instanceof ApplicationContextAware) { - ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); - } -} -``` - -最后执行到我们自定义的逻辑中,使用 `ApplicationContextAware` 来获取 `ApplicationContext` 的引用,并使用这个引用来发布自定义事件。 - -```java -public class MyApplicationContextAware implements ApplicationContextAware { - - private ApplicationContext context; - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; - } - - public void publish(String message) { - context.publishEvent(new MyEvent(this, message)); - } -} -``` - -### 七、注意事项 - -**记住生命周期**:当我们实现 `ApplicationContextAware` 时,记住上下文是在 bean 的生命周期的一个特定点被注入的。这通常是在属性注入后、初始化方法前。 - -**记住上下文层次结构**:在更复杂的应用中,可能会有多个 `ApplicationContext` 层次结构(例如,一个根上下文和一个或多个子上下文)。确保我们了解从哪个上下文检索 beans,以及这些 beans 的生命周期和可见性。 - -**小心与懒加载 beans 的交互**:如果我们使用 `ApplicationContextAware` 来动态检索一个定义为懒加载的 bean,那么这将导致该 bean 被立即初始化。 - -**避免创建循环依赖**:如果使用 `ApplicationContext` 来动态查找 beans,要确保不会创建意外的循环依赖。 - -**避免在构造函数中使用 ApplicationContext**:当 bean 实现 `ApplicationContextAware` 时,`setApplicationContext` 方法是在 bean 的属性注入之后、初始化方法(如 `afterPropertiesSet` 或自定义的 init 方法)之前调用的。因此,不应该试图在构造函数中访问 `ApplicationContext`,因为它在那时可能还没有被设置。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**应用启动与上下文初始化**: 在 `ApplicationContextAwareApplication` 类中,我们启动了一个基于 Java 注解配置的 Spring 应用。使用 `AnnotationConfigApplicationContext` 与 `MyConfiguration` 作为参数,我们成功地启动了 Spring 容器。 - -**配置类的定义**: `MyConfiguration` 是一个配置类,标注了 `@Configuration` 注解。这里,我们定义了两个 beans,分别是 `MyApplicationContextAware` 和 `MyEventListener`。由于它们都被定义为 `@Bean`,Spring 容器在启动时会实例化并管理它们。 - -**获取并使用 ApplicationContextAware bean**: 从启动的 Spring 容器中,我们获取了 `MyApplicationContextAware` bean 的实例,并调用其 `publish` 方法来发布一个自定义事件(携带消息 "hello world")。 - -**ApplicationContextAware 的实现**: `MyApplicationContextAware` 实现了 `ApplicationContextAware` 接口,这允许它获取和存储其运行环境的 `ApplicationContext` 引用。它提供了一个 `publish` 方法,用于使用保存的 ApplicationContext 来发布事件。 - -**自定义事件的定义**: `MyEvent` 是一个自定义事件,继承自 `ApplicationEvent`。它携带了一个字符串消息,这个消息在事件发布后可以由监听器获取和处理。 - -**事件监听器的定义**: `MyEventListener` 实现了 `ApplicationListener`,这使得它成为了一个专门用于监听和处理 `MyEvent` 事件的监听器。当 `MyEvent` 事件被发布时,监听器的 `onApplicationEvent` 方法被自动触发,并打印出消息内容。 - -**运行结果**: 当应用运行时,`MyEventListener` 成功地捕获了由 `MyApplicationContextAware` 发布的事件,并输出了 "Received my event - hello world",证明整个事件发布和处理的机制都工作正常。 - -#### 8.2、源码分析总结 - -**启动与上下文初始化**:使用 `AnnotationConfigApplicationContext` 启动 Spring 应用,并传递 `MyConfiguration` 作为参数进行上下文初始化,从上下文中获取 `MyApplicationContextAware` bean,并发布自定义事件。 - -**Spring 上下文刷新**:在 `refresh()` 方法中,主要关注点是调用 `finishBeanFactoryInitialization(beanFactory)`,负责实例化所有非延迟加载的单例 bean。 - -**Bean 实例化**:`preInstantiateSingletons()` 方法在 Spring 上下文初始化完成后被调用,确保所有非延迟加载的单例 beans 都被实例化,对于每个 bean,都会调用 `getBean(beanName)`,这会触发 bean 的实例化、初始化以及依赖注入。 - -**Bean 创建与初始化**:在 `doCreateBean` 方法中,核心操作是调用 `initializeBean` 进行 bean 初始化,确保 bean 完全配置并准备好,`initializeBean` 中会调用 `applyBeanPostProcessorsBeforeInitialization`,在 bean 初始化之前遍历所有已注册的 `BeanPostProcessor`。 - -**处理 Aware 接口**:`ApplicationContextAwareProcessor` 的作用是对实现了 "Aware" 接口的 beans 进行特殊处理。在 `invokeAwareInterfaces` 方法中,针对不同的 "Aware" 接口进行了处理,使得 beans 可以自动感知其运行环境或特定依赖。 - +## ApplicationContextAware + +- [ApplicationContextAware](#applicationcontextaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`ApplicationContextAware` 接口,允许我们访问当前的应用上下文 (`ApplicationContext`)。这通常在某些Spring bean需要访问应用上下文本身或其内部其他bean时会有用。 + +### 二、接口源码 + +`ApplicationContextAware` 是 Spring 框架中的一个接口,主要用于那些需要感知或交互其所在的 ApplicationContext 的 Spring beans。例如,可能需要发布事件或查询 ApplicationContext 中的其他 beans。 + +```java +/** + * 该接口应由希望接收其运行的 ApplicationContext 通知的任何对象实现。 + * + * 例如,当对象需要访问一组合作的bean时,实现此接口是有意义的。 + * 注意,通过bean引用进行配置优于仅为查找bean的目的而实现此接口。 + * + * 如果对象需要访问文件资源(即希望调用getResource), + * 想要发布一个应用事件,或需要访问MessageSource,也可以实现此接口。 + * 但在这种特定情况下,最好实现更为特定的 ResourceLoaderAware, + * ApplicationEventPublisherAware 或 MessageSourceAware 接口。 + * + * 注意,文件资源依赖也可以作为类型为 org.springframework.core.io.Resource 的bean属性暴露, + * 通过字符串填充,由bean工厂自动进行类型转换。这样就不需要为访问特定文件资源而实现任何回调接口了。 + * + * org.springframework.context.support.ApplicationObjectSupport 是应用对象的便利基类, + * 它实现了此接口。 + * + * 关于所有bean生命周期方法的列表,请参阅 + * org.springframework.beans.factory.BeanFactory BeanFactory的javadocs。 + * + * @author Rod Johnson + * @author Juergen Hoeller + * @author Chris Beams + * @see ResourceLoaderAware + * @see ApplicationEventPublisherAware + * @see MessageSourceAware + * @see org.springframework.context.support.ApplicationObjectSupport + * @see org.springframework.beans.factory.BeanFactoryAware + */ +public interface ApplicationContextAware extends Aware { + + /** + * 设置此对象运行的 ApplicationContext。 + * 此调用通常用于初始化对象。 + * 此方法在普通bean属性被填充之后调用,但在诸如 org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + * 这样的初始化回调或自定义初始化方法之前调用。 + * 在 ResourceLoaderAware#setResourceLoader, + * ApplicationEventPublisherAware#setApplicationEventPublisher 和 + * MessageSourceAware 之后调用(如果适用)。 + * + * @param applicationContext 要由此对象使用的 ApplicationContext 对象 + * @throws ApplicationContextException 如果上下文初始化出错 + * @throws BeansException 如果应用上下文方法抛出异常 + * @see org.springframework.beans.factory.BeanInitializationException + */ + void setApplicationContext(ApplicationContext applicationContext) throws BeansException; + +} +``` + +### 三、主要功能 + +**动态查找其他Beans**:尽管我们通常使用依赖注入来获取其他beans的引用,但在某些动态或复杂情况下,bean可能需要在运行时查找其他beans。 + +**发布事件**:通过 `ApplicationContext`,bean可以发布应用级事件,这些事件可以被其他beans捕获和处理,这是实现松耦合交互的一种方法。 + +**资源加载**:`ApplicationContext` 扩展了 `ResourceLoader`,因此bean可以使用它来加载外部资源,如文件或URL。 + +**访问消息源**:对于支持国际化的应用程序,bean可以通过 `ApplicationContext` 访问消息源,从而解析特定的消息。 + +**访问其他应用上下文服务**:除了上述功能,`ApplicationContext` 还提供了其他一些服务,例如与JNDI交互、访问应用的环境属性等。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationContextAware`类型的bean,最后调用`publish`方法用于发布一个事件。 + +```java +public class ApplicationContextAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyApplicationContextAware contextAware = context.getBean(MyApplicationContextAware.class); + contextAware.publish("hello world"); + } +} +``` + +这里使用`@Bean`注解,定义了两个Bean,是为了确保`MyEventListener`, `MyApplicationContextAware` 被 Spring 容器执行 + +```java +@Configuration +public class MyConfiguration { + + @Bean + public MyApplicationContextAware myApplicationContextAware(){ + return new MyApplicationContextAware(); + } + + @Bean + public MyEventListener MyEventListener(){ + return new MyEventListener(); + } +} +``` + + `MyApplicationContextAware` 类使用 `ApplicationContextAware` 来获取 `ApplicationContext` 的引用,并使用这个引用来发布自定义事件。 + +```java +public class MyApplicationContextAware implements ApplicationContextAware { + + private ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + + public void publish(String message) { + context.publishEvent(new MyEvent(this, message)); + } +} +``` + +`MyEvent` 是我们自定义的 Spring 应用事件,用于传递一个字符串消息。 + +```java +public class MyEvent extends ApplicationEvent { + + private final String message; + + public MyEvent(Object source, String message) { + super(source); + this.message = message; + } + + public String getMessage() { + return message; + } +} +``` + +`MyEventListener` 是一个监听器。当 `MyEvent` 事件被发布时,此监听器会自动被触发,执行 `onApplicationEvent` 方法的逻辑。 + +```java +public class MyEventListener implements ApplicationListener { + + @Override + public void onApplicationEvent(MyEvent event) { + System.out.println("Received my event - " + event.getMessage()); + } +} +``` + +运行结果发现,这表示监听器成功地捕获了该事件并处理了它。 + +```java +Received my event - hello world +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: BeanClassLoaderAware时序图 + participant ApplicationContextAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant ApplicationContextAwareProcessor + participant MyApplicationContextAware + + ApplicationContextAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 + AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 + ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 + ApplicationContextAwareProcessor->>MyApplicationContextAware:setApplicationContext(context)
设置应用上下文 + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>ApplicationContextAwareApplication:初始化完成 + +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationContextAware`类型的bean,最后调用`publish`方法用于发布一个事件。 + +```java +public class ApplicationContextAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyApplicationContextAware contextAware = context.getBean(MyApplicationContextAware.class); + contextAware.publish("hello world"); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 + +```java +@Override +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 + +```java +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || + bean instanceof ApplicationStartupAware)) { + return bean; + } + + // ... [代码部分省略以简化] + + invokeAwareInterfaces(bean); + + return bean; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 + +```java +private void invokeAwareInterfaces(Object bean) { + // ... [代码部分省略以简化] + + // 对ApplicationContextAware接口进行回调 + if (bean instanceof ApplicationContextAware) { + ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); + } +} +``` + +最后执行到我们自定义的逻辑中,使用 `ApplicationContextAware` 来获取 `ApplicationContext` 的引用,并使用这个引用来发布自定义事件。 + +```java +public class MyApplicationContextAware implements ApplicationContextAware { + + private ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + + public void publish(String message) { + context.publishEvent(new MyEvent(this, message)); + } +} +``` + +### 七、注意事项 + +**记住生命周期**:当我们实现 `ApplicationContextAware` 时,记住上下文是在 bean 的生命周期的一个特定点被注入的。这通常是在属性注入后、初始化方法前。 + +**记住上下文层次结构**:在更复杂的应用中,可能会有多个 `ApplicationContext` 层次结构(例如,一个根上下文和一个或多个子上下文)。确保我们了解从哪个上下文检索 beans,以及这些 beans 的生命周期和可见性。 + +**小心与懒加载 beans 的交互**:如果我们使用 `ApplicationContextAware` 来动态检索一个定义为懒加载的 bean,那么这将导致该 bean 被立即初始化。 + +**避免创建循环依赖**:如果使用 `ApplicationContext` 来动态查找 beans,要确保不会创建意外的循环依赖。 + +**避免在构造函数中使用 ApplicationContext**:当 bean 实现 `ApplicationContextAware` 时,`setApplicationContext` 方法是在 bean 的属性注入之后、初始化方法(如 `afterPropertiesSet` 或自定义的 init 方法)之前调用的。因此,不应该试图在构造函数中访问 `ApplicationContext`,因为它在那时可能还没有被设置。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**应用启动与上下文初始化**: 在 `ApplicationContextAwareApplication` 类中,我们启动了一个基于 Java 注解配置的 Spring 应用。使用 `AnnotationConfigApplicationContext` 与 `MyConfiguration` 作为参数,我们成功地启动了 Spring 容器。 + +**配置类的定义**: `MyConfiguration` 是一个配置类,标注了 `@Configuration` 注解。这里,我们定义了两个 beans,分别是 `MyApplicationContextAware` 和 `MyEventListener`。由于它们都被定义为 `@Bean`,Spring 容器在启动时会实例化并管理它们。 + +**获取并使用 ApplicationContextAware bean**: 从启动的 Spring 容器中,我们获取了 `MyApplicationContextAware` bean 的实例,并调用其 `publish` 方法来发布一个自定义事件(携带消息 "hello world")。 + +**ApplicationContextAware 的实现**: `MyApplicationContextAware` 实现了 `ApplicationContextAware` 接口,这允许它获取和存储其运行环境的 `ApplicationContext` 引用。它提供了一个 `publish` 方法,用于使用保存的 ApplicationContext 来发布事件。 + +**自定义事件的定义**: `MyEvent` 是一个自定义事件,继承自 `ApplicationEvent`。它携带了一个字符串消息,这个消息在事件发布后可以由监听器获取和处理。 + +**事件监听器的定义**: `MyEventListener` 实现了 `ApplicationListener`,这使得它成为了一个专门用于监听和处理 `MyEvent` 事件的监听器。当 `MyEvent` 事件被发布时,监听器的 `onApplicationEvent` 方法被自动触发,并打印出消息内容。 + +**运行结果**: 当应用运行时,`MyEventListener` 成功地捕获了由 `MyApplicationContextAware` 发布的事件,并输出了 "Received my event - hello world",证明整个事件发布和处理的机制都工作正常。 + +#### 8.2、源码分析总结 + +**启动与上下文初始化**:使用 `AnnotationConfigApplicationContext` 启动 Spring 应用,并传递 `MyConfiguration` 作为参数进行上下文初始化,从上下文中获取 `MyApplicationContextAware` bean,并发布自定义事件。 + +**Spring 上下文刷新**:在 `refresh()` 方法中,主要关注点是调用 `finishBeanFactoryInitialization(beanFactory)`,负责实例化所有非延迟加载的单例 bean。 + +**Bean 实例化**:`preInstantiateSingletons()` 方法在 Spring 上下文初始化完成后被调用,确保所有非延迟加载的单例 beans 都被实例化,对于每个 bean,都会调用 `getBean(beanName)`,这会触发 bean 的实例化、初始化以及依赖注入。 + +**Bean 创建与初始化**:在 `doCreateBean` 方法中,核心操作是调用 `initializeBean` 进行 bean 初始化,确保 bean 完全配置并准备好,`initializeBean` 中会调用 `applyBeanPostProcessorsBeforeInitialization`,在 bean 初始化之前遍历所有已注册的 `BeanPostProcessor`。 + +**处理 Aware 接口**:`ApplicationContextAwareProcessor` 的作用是对实现了 "Aware" 接口的 beans 进行特殊处理。在 `invokeAwareInterfaces` 方法中,针对不同的 "Aware" 接口进行了处理,使得 beans 可以自动感知其运行环境或特定依赖。 + **自定义事件发布**:通过实现 `ApplicationContextAware`,`MyApplicationContextAware` 能够获得 `ApplicationContext` 的引用,随后使用此引用发布自定义事件。 \ No newline at end of file diff --git a/spring-aware-applicationContextAware/pom.xml b/spring-aware/spring-aware-applicationContextAware/pom.xml similarity index 89% rename from spring-aware-applicationContextAware/pom.xml rename to spring-aware/spring-aware-applicationContextAware/pom.xml index 517d5c8..f5d639e 100644 --- a/spring-aware-applicationContextAware/pom.xml +++ b/spring-aware/spring-aware-applicationContextAware/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-aware com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/ApplicationContextAwareApplication.java b/spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/ApplicationContextAwareApplication.java similarity index 100% rename from spring-aware-applicationContextAware/src/main/java/com/xcs/spring/ApplicationContextAwareApplication.java rename to spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/ApplicationContextAwareApplication.java diff --git a/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyApplicationContextAware.java b/spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyApplicationContextAware.java similarity index 96% rename from spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyApplicationContextAware.java rename to spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyApplicationContextAware.java index c6aa6dc..f377a6e 100644 --- a/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyApplicationContextAware.java +++ b/spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyApplicationContextAware.java @@ -1,20 +1,20 @@ -package com.xcs.spring.config; - -import com.xcs.spring.event.MyEvent; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -public class MyApplicationContextAware implements ApplicationContextAware { - - private ApplicationContext context; - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; - } - - public void publish(String message) { - context.publishEvent(new MyEvent(this, message)); - } -} +package com.xcs.spring.config; + +import com.xcs.spring.event.MyEvent; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +public class MyApplicationContextAware implements ApplicationContextAware { + + private ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + + public void publish(String message) { + context.publishEvent(new MyEvent(this, message)); + } +} diff --git a/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEvent.java b/spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEvent.java similarity index 95% rename from spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEvent.java rename to spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEvent.java index b946c64..dcda298 100644 --- a/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEvent.java +++ b/spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEvent.java @@ -1,17 +1,17 @@ -package com.xcs.spring.event; - -import org.springframework.context.ApplicationEvent; - -public class MyEvent extends ApplicationEvent { - - private final String message; - - public MyEvent(Object source, String message) { - super(source); - this.message = message; - } - - public String getMessage() { - return message; - } +package com.xcs.spring.event; + +import org.springframework.context.ApplicationEvent; + +public class MyEvent extends ApplicationEvent { + + private final String message; + + public MyEvent(Object source, String message) { + super(source); + this.message = message; + } + + public String getMessage() { + return message; + } } \ No newline at end of file diff --git a/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEventListener.java b/spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEventListener.java similarity index 96% rename from spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEventListener.java rename to spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEventListener.java index 46b78a8..a40c469 100644 --- a/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEventListener.java +++ b/spring-aware/spring-aware-applicationContextAware/src/main/java/com/xcs/spring/event/MyEventListener.java @@ -1,11 +1,11 @@ -package com.xcs.spring.event; - -import org.springframework.context.ApplicationListener; - -public class MyEventListener implements ApplicationListener { - - @Override - public void onApplicationEvent(MyEvent event) { - System.out.println("Received my event - " + event.getMessage()); - } +package com.xcs.spring.event; + +import org.springframework.context.ApplicationListener; + +public class MyEventListener implements ApplicationListener { + + @Override + public void onApplicationEvent(MyEvent event) { + System.out.println("Received my event - " + event.getMessage()); + } } \ No newline at end of file diff --git a/spring-aware-applicationEventPublisherAware/README.md b/spring-aware/spring-aware-applicationEventPublisherAware/README.md similarity index 97% rename from spring-aware-applicationEventPublisherAware/README.md rename to spring-aware/spring-aware-applicationEventPublisherAware/README.md index c674e73..45dbb2a 100644 --- a/spring-aware-applicationEventPublisherAware/README.md +++ b/spring-aware/spring-aware-applicationEventPublisherAware/README.md @@ -1,506 +1,506 @@ -## ApplicationEventPublisherAware - -- [ApplicationEventPublisherAware](#applicationeventpublisheraware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`ApplicationEventPublisherAware` 接口,用于给需要发布应用事件的bean提供一个便捷的方式。实现此接口的bean可以接收到一个 `ApplicationEventPublisher` 的引用,这样它们就可以发布事件到 Spring 应用上下文中。 - -### 二、接口源码 - -`ApplicationEventPublisherAware` 是 Spring 框架自 1.1.1 开始引入的一个核心接口。需要发布一些事件到Spring上下文,我们可以实现此接口,然后使用注入的 `ApplicationEventPublisher` 来发布事件(通常是 `ApplicationContext`)。 - -```java -/** - * 任何希望被通知 ApplicationEventPublisher(通常是 ApplicationContext)的对象都应该实现的接口。 - * 当对象运行在某个 ApplicationEventPublisher 中时,它将被通知。 - * - * @author Juergen Hoeller - * @author Chris Beams - * @since 1.1.1 - * @see ApplicationContextAware - */ -public interface ApplicationEventPublisherAware extends Aware { - - /** - * 设置此对象运行的 ApplicationEventPublisher。 - * 此方法在正常bean属性填充之后被调用,但在init回调(如 InitializingBean 的 afterPropertiesSet 或自定义的 init-method)之前被调用。 - * 并且在 ApplicationContextAware 的 setApplicationContext 之前被调用。 - * - * @param applicationEventPublisher 由这个对象使用的事件发布器 - */ - void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher); - -} -``` - -### 三、主要功能 - -**事件发布能力**:它允许 Spring beans 获得事件发布的能力,使它们能够发布事件到 Spring 应用上下文中。 - -**回调机制**:当一个 bean 实现了 `ApplicationEventPublisherAware` 接口时,Spring 容器会自动注入 `ApplicationEventPublisher` 实例到该 bean 中。 - -**与 ApplicationContext 的关联**:通常,所注入的 `ApplicationEventPublisher` 实例实际上就是 `ApplicationContext` 本身,这意味着 beans 可以使用它来发布事件。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationEventPublisherAware`类型的bean,最后调用`publish`方法用于发布一个事件。 - -```java -public class ApplicationEventPublisherAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyApplicationEventPublisherAware publisherAware = context.getBean(MyApplicationEventPublisherAware.class); - publisherAware.publish("hello world"); - } -} -``` - -这里使用`@Bean`注解,定义了两个Bean,是为了确保`MyEventListener`, `MyApplicationEventPublisherAware` 被 Spring 容器执行 - -```java -@Configuration -public class MyConfiguration { - - @Bean - public MyApplicationEventPublisherAware myApplicationEventPublisherAware(){ - return new MyApplicationEventPublisherAware(); - } - - @Bean - public MyEventListener MyEventListener(){ - return new MyEventListener(); - } -} -``` - -`MyApplicationContextAware` 类使用 `ApplicationEventPublisherAware` 来获取 `ApplicationEventPublisher` 的引用,并使用这个引用来发布自定义事件。 - -```java -public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { - - private ApplicationEventPublisher publisher; - - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { - this.publisher = publisher; - } - - public void publish(String message) { - publisher.publishEvent(new MyEvent(this, message)); - } -} -``` - -`MyEvent` 是我们自定义的 Spring 应用事件,用于传递一个字符串消息。 - -```java -public class MyEvent extends ApplicationEvent { - - private final String message; - - public MyEvent(Object source, String message) { - super(source); - this.message = message; - } - - public String getMessage() { - return message; - } -} -``` - -`MyEventListener` 是一个监听器。当 `MyEvent` 事件被发布时,此监听器会自动被触发,执行 `onApplicationEvent` 方法的逻辑。 - -```java -public class MyEventListener implements ApplicationListener { - - @Override - public void onApplicationEvent(MyEvent event) { - System.out.println("Received my event - " + event.getMessage()); - } -} -``` - -运行结果发现,这表示监听器成功地捕获了该事件并处理了它。 - -```java -Received my event - hello world -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: ApplicationEventPublisherAware时序图 - participant ApplicationEventPublisherAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant ApplicationContextAwareProcessor - participant MyApplicationEventPublisherAware - - ApplicationEventPublisherAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 -AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理 -AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
前置处理器方法 -ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
调用Aware接口 -ApplicationContextAwareProcessor->>MyApplicationEventPublisherAware:setApplicationEventPublisher(publisher)
注入事件发布器 - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>ApplicationEventPublisherAwareApplication:初始化完成 -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationEventPublisherAware`类型的bean,最后调用`publish`方法用于发布一个事件。 - -```java -public class ApplicationEventPublisherAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyApplicationEventPublisherAware publisherAware = context.getBean(MyApplicationEventPublisherAware.class); - publisherAware.publish("hello world"); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - Object wrappedBean = bean; - if (mbd == null || !mbd.isSynthetic()) { - wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); - } - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 - -```java -@Override -public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) - throws BeansException { - - Object result = existingBean; - for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; - } - result = current; - } - return result; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 - -```java -@Override -@Nullable -public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || - bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || - bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || - bean instanceof ApplicationStartupAware)) { - return bean; - } - - // ... [代码部分省略以简化] - - invokeAwareInterfaces(bean); - - return bean; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 - -```java -private void invokeAwareInterfaces(Object bean) { - // ... [代码部分省略以简化] - // 对ApplicationEventPublisherAware接口进行回调 - if (bean instanceof ApplicationEventPublisherAware) { - ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); - } - // ... [代码部分省略以简化] -} -``` - -最后执行到我们自定义的逻辑中,使用 `ApplicationEventPublisherAware` 来获取 `ApplicationEventPublisher` 的引用,并使用这个引用来发布自定义事件。 - -```java -public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { - - private ApplicationEventPublisher publisher; - - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { - this.publisher = publisher; - } - - public void publish(String message) { - publisher.publishEvent(new MyEvent(this, message)); - } -} -``` - -### 七、注意事项 - -**确保容器支持**:不是所有的Spring容器都支持 `Aware` 接口。例如,基本的 `BeanFactory` 不支持,而 `ApplicationContext` 支持。确保您的bean是由支持 `ApplicationEventPublisherAware` 的容器管理的。 - -**避免复杂的业务逻辑**:在实现的 `setApplicationEventPublisher` 方法中,尽量避免放入复杂的业务逻辑,该方法主要是用于注入 `ApplicationEventPublisher` 的。 - -**注意事件的目标**:当使用 `ApplicationEventPublisher` 发布事件时,这些事件会被所有相应的监听器捕获。确保你了解这些监听器的存在和它们的行为,以避免出现一些奇奇怪怪的问题。 - -**不要手动调用**:`setApplicationEventPublisher` 方法是为了由Spring容器调用的,而不是为了应用程序代码调用的。你不应该在业务逻辑中手动调用这个方法。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**启动应用**:在 `ApplicationEventPublisherAwareApplication` 的主方法中,使用 `AnnotationConfigApplicationContext` 初始化了 Spring 上下文,并指定了配置类 `MyConfiguration`。 - -**配置类**:`MyConfiguration` 使用了 `@Configuration` 注解,表示它是一个 Spring 配置类。此类中使用 `@Bean` 注解定义了两个 Bean:`MyApplicationEventPublisherAware` 和 `MyEventListener`,确保它们被 Spring 容器管理。 - -**事件发布者**:`MyApplicationEventPublisherAware` 类实现了 `ApplicationEventPublisherAware` 接口,从而可以获取 `ApplicationEventPublisher` 的引用。它还定义了一个 `publish` 方法,用于发布自定义的 `MyEvent` 事件。 - -**自定义事件**:`MyEvent` 是一个自定义事件类,继承自 `ApplicationEvent`。它携带一个字符串消息。 - -**事件监听器**:`MyEventListener` 是一个事件监听器,它实现了 `ApplicationListener` 并专门用于监听 `MyEvent` 事件。当相应事件被发布时,它的 `onApplicationEvent` 方法会被自动触发。 - -**执行结果**:当运行 `ApplicationEventPublisherAwareApplication` 主方法时,应用发布了一个 `MyEvent` 事件,携带了 "hello world" 消息。`MyEventListener` 成功捕获此事件,并输出了相应的消息。 - -#### 8.2、源码分析总结 - -**启动应用**:通过 `ApplicationEventPublisherAwareApplication` 的主方法,使用 `AnnotationConfigApplicationContext` 初始化了 Spring 上下文,并指定了配置类 `MyConfiguration`。 - -**注册和刷新**:在 `AnnotationConfigApplicationContext` 构造函数中,先注册组件类,然后调用 `refresh()` 来启动Spring容器的初始化过程。 - -**初始化Bean工厂**:在 `AbstractApplicationContext#refresh` 方法中,调用 `finishBeanFactoryInitialization` 以实例化所有非懒加载的单例Bean。 - -**预实例化单例**:在 `DefaultListableBeanFactory` 中,通过 `preInstantiateSingletons` 方法预先实例化所有非懒加载的单例Bean。 - -**Bean创建**:在 `AbstractBeanFactory#getBean` 中,调用 `doGetBean` 来真正执行Bean的创建过程。此方法中涉及到真正的Bean实例化、属性注入和初始化。 - -**初始化Bean**:在 `AbstractAutowireCapableBeanFactory` 类中,`initializeBean` 方法用于确保bean完全配置并准备就绪。这个过程中会应用所有的 `BeanPostProcessor`,它们能在bean初始化前后做额外的处理。 - -**处理Aware接口**:在 `ApplicationContextAwareProcessor` 中,`invokeAwareInterfaces` 方法负责处理实现了 `Aware` 接口的beans,为它们自动注入对应的依赖或运行环境信息。 - +## ApplicationEventPublisherAware + +- [ApplicationEventPublisherAware](#applicationeventpublisheraware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`ApplicationEventPublisherAware` 接口,用于给需要发布应用事件的bean提供一个便捷的方式。实现此接口的bean可以接收到一个 `ApplicationEventPublisher` 的引用,这样它们就可以发布事件到 Spring 应用上下文中。 + +### 二、接口源码 + +`ApplicationEventPublisherAware` 是 Spring 框架自 1.1.1 开始引入的一个核心接口。需要发布一些事件到Spring上下文,我们可以实现此接口,然后使用注入的 `ApplicationEventPublisher` 来发布事件(通常是 `ApplicationContext`)。 + +```java +/** + * 任何希望被通知 ApplicationEventPublisher(通常是 ApplicationContext)的对象都应该实现的接口。 + * 当对象运行在某个 ApplicationEventPublisher 中时,它将被通知。 + * + * @author Juergen Hoeller + * @author Chris Beams + * @since 1.1.1 + * @see ApplicationContextAware + */ +public interface ApplicationEventPublisherAware extends Aware { + + /** + * 设置此对象运行的 ApplicationEventPublisher。 + * 此方法在正常bean属性填充之后被调用,但在init回调(如 InitializingBean 的 afterPropertiesSet 或自定义的 init-method)之前被调用。 + * 并且在 ApplicationContextAware 的 setApplicationContext 之前被调用。 + * + * @param applicationEventPublisher 由这个对象使用的事件发布器 + */ + void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher); + +} +``` + +### 三、主要功能 + +**事件发布能力**:它允许 Spring beans 获得事件发布的能力,使它们能够发布事件到 Spring 应用上下文中。 + +**回调机制**:当一个 bean 实现了 `ApplicationEventPublisherAware` 接口时,Spring 容器会自动注入 `ApplicationEventPublisher` 实例到该 bean 中。 + +**与 ApplicationContext 的关联**:通常,所注入的 `ApplicationEventPublisher` 实例实际上就是 `ApplicationContext` 本身,这意味着 beans 可以使用它来发布事件。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationEventPublisherAware`类型的bean,最后调用`publish`方法用于发布一个事件。 + +```java +public class ApplicationEventPublisherAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyApplicationEventPublisherAware publisherAware = context.getBean(MyApplicationEventPublisherAware.class); + publisherAware.publish("hello world"); + } +} +``` + +这里使用`@Bean`注解,定义了两个Bean,是为了确保`MyEventListener`, `MyApplicationEventPublisherAware` 被 Spring 容器执行 + +```java +@Configuration +public class MyConfiguration { + + @Bean + public MyApplicationEventPublisherAware myApplicationEventPublisherAware(){ + return new MyApplicationEventPublisherAware(); + } + + @Bean + public MyEventListener MyEventListener(){ + return new MyEventListener(); + } +} +``` + +`MyApplicationContextAware` 类使用 `ApplicationEventPublisherAware` 来获取 `ApplicationEventPublisher` 的引用,并使用这个引用来发布自定义事件。 + +```java +public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { + + private ApplicationEventPublisher publisher; + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + + public void publish(String message) { + publisher.publishEvent(new MyEvent(this, message)); + } +} +``` + +`MyEvent` 是我们自定义的 Spring 应用事件,用于传递一个字符串消息。 + +```java +public class MyEvent extends ApplicationEvent { + + private final String message; + + public MyEvent(Object source, String message) { + super(source); + this.message = message; + } + + public String getMessage() { + return message; + } +} +``` + +`MyEventListener` 是一个监听器。当 `MyEvent` 事件被发布时,此监听器会自动被触发,执行 `onApplicationEvent` 方法的逻辑。 + +```java +public class MyEventListener implements ApplicationListener { + + @Override + public void onApplicationEvent(MyEvent event) { + System.out.println("Received my event - " + event.getMessage()); + } +} +``` + +运行结果发现,这表示监听器成功地捕获了该事件并处理了它。 + +```java +Received my event - hello world +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: ApplicationEventPublisherAware时序图 + participant ApplicationEventPublisherAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant ApplicationContextAwareProcessor + participant MyApplicationEventPublisherAware + + ApplicationEventPublisherAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 +AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理 +AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
前置处理器方法 +ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
调用Aware接口 +ApplicationContextAwareProcessor->>MyApplicationEventPublisherAware:setApplicationEventPublisher(publisher)
注入事件发布器 + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>ApplicationEventPublisherAwareApplication:初始化完成 +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyApplicationEventPublisherAware`类型的bean,最后调用`publish`方法用于发布一个事件。 + +```java +public class ApplicationEventPublisherAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyApplicationEventPublisherAware publisherAware = context.getBean(MyApplicationEventPublisherAware.class); + publisherAware.publish("hello world"); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 + +```java +@Override +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 + +```java +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || + bean instanceof ApplicationStartupAware)) { + return bean; + } + + // ... [代码部分省略以简化] + + invokeAwareInterfaces(bean); + + return bean; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 + +```java +private void invokeAwareInterfaces(Object bean) { + // ... [代码部分省略以简化] + // 对ApplicationEventPublisherAware接口进行回调 + if (bean instanceof ApplicationEventPublisherAware) { + ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); + } + // ... [代码部分省略以简化] +} +``` + +最后执行到我们自定义的逻辑中,使用 `ApplicationEventPublisherAware` 来获取 `ApplicationEventPublisher` 的引用,并使用这个引用来发布自定义事件。 + +```java +public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { + + private ApplicationEventPublisher publisher; + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + + public void publish(String message) { + publisher.publishEvent(new MyEvent(this, message)); + } +} +``` + +### 七、注意事项 + +**确保容器支持**:不是所有的Spring容器都支持 `Aware` 接口。例如,基本的 `BeanFactory` 不支持,而 `ApplicationContext` 支持。确保您的bean是由支持 `ApplicationEventPublisherAware` 的容器管理的。 + +**避免复杂的业务逻辑**:在实现的 `setApplicationEventPublisher` 方法中,尽量避免放入复杂的业务逻辑,该方法主要是用于注入 `ApplicationEventPublisher` 的。 + +**注意事件的目标**:当使用 `ApplicationEventPublisher` 发布事件时,这些事件会被所有相应的监听器捕获。确保你了解这些监听器的存在和它们的行为,以避免出现一些奇奇怪怪的问题。 + +**不要手动调用**:`setApplicationEventPublisher` 方法是为了由Spring容器调用的,而不是为了应用程序代码调用的。你不应该在业务逻辑中手动调用这个方法。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**启动应用**:在 `ApplicationEventPublisherAwareApplication` 的主方法中,使用 `AnnotationConfigApplicationContext` 初始化了 Spring 上下文,并指定了配置类 `MyConfiguration`。 + +**配置类**:`MyConfiguration` 使用了 `@Configuration` 注解,表示它是一个 Spring 配置类。此类中使用 `@Bean` 注解定义了两个 Bean:`MyApplicationEventPublisherAware` 和 `MyEventListener`,确保它们被 Spring 容器管理。 + +**事件发布者**:`MyApplicationEventPublisherAware` 类实现了 `ApplicationEventPublisherAware` 接口,从而可以获取 `ApplicationEventPublisher` 的引用。它还定义了一个 `publish` 方法,用于发布自定义的 `MyEvent` 事件。 + +**自定义事件**:`MyEvent` 是一个自定义事件类,继承自 `ApplicationEvent`。它携带一个字符串消息。 + +**事件监听器**:`MyEventListener` 是一个事件监听器,它实现了 `ApplicationListener` 并专门用于监听 `MyEvent` 事件。当相应事件被发布时,它的 `onApplicationEvent` 方法会被自动触发。 + +**执行结果**:当运行 `ApplicationEventPublisherAwareApplication` 主方法时,应用发布了一个 `MyEvent` 事件,携带了 "hello world" 消息。`MyEventListener` 成功捕获此事件,并输出了相应的消息。 + +#### 8.2、源码分析总结 + +**启动应用**:通过 `ApplicationEventPublisherAwareApplication` 的主方法,使用 `AnnotationConfigApplicationContext` 初始化了 Spring 上下文,并指定了配置类 `MyConfiguration`。 + +**注册和刷新**:在 `AnnotationConfigApplicationContext` 构造函数中,先注册组件类,然后调用 `refresh()` 来启动Spring容器的初始化过程。 + +**初始化Bean工厂**:在 `AbstractApplicationContext#refresh` 方法中,调用 `finishBeanFactoryInitialization` 以实例化所有非懒加载的单例Bean。 + +**预实例化单例**:在 `DefaultListableBeanFactory` 中,通过 `preInstantiateSingletons` 方法预先实例化所有非懒加载的单例Bean。 + +**Bean创建**:在 `AbstractBeanFactory#getBean` 中,调用 `doGetBean` 来真正执行Bean的创建过程。此方法中涉及到真正的Bean实例化、属性注入和初始化。 + +**初始化Bean**:在 `AbstractAutowireCapableBeanFactory` 类中,`initializeBean` 方法用于确保bean完全配置并准备就绪。这个过程中会应用所有的 `BeanPostProcessor`,它们能在bean初始化前后做额外的处理。 + +**处理Aware接口**:在 `ApplicationContextAwareProcessor` 中,`invokeAwareInterfaces` 方法负责处理实现了 `Aware` 接口的beans,为它们自动注入对应的依赖或运行环境信息。 + **发布事件**:在我们的自定义逻辑中,使用 `ApplicationEventPublisherAware` 接口来获取Spring的事件发布器。然后,使用这个事件发布器,我们可以发布自定义事件。 \ No newline at end of file diff --git a/spring-aware-applicationEventPublisherAware/pom.xml b/spring-aware/spring-aware-applicationEventPublisherAware/pom.xml similarity index 89% rename from spring-aware-applicationEventPublisherAware/pom.xml rename to spring-aware/spring-aware-applicationEventPublisherAware/pom.xml index 9f054cf..520c200 100644 --- a/spring-aware-applicationEventPublisherAware/pom.xml +++ b/spring-aware/spring-aware-applicationEventPublisherAware/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-aware com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/ApplicationEventPublisherAwareApplication.java b/spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/ApplicationEventPublisherAwareApplication.java similarity index 100% rename from spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/ApplicationEventPublisherAwareApplication.java rename to spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/ApplicationEventPublisherAwareApplication.java diff --git a/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyApplicationEventPublisherAware.java b/spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyApplicationEventPublisherAware.java similarity index 96% rename from spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyApplicationEventPublisherAware.java rename to spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyApplicationEventPublisherAware.java index 549a975..0a91933 100644 --- a/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyApplicationEventPublisherAware.java +++ b/spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyApplicationEventPublisherAware.java @@ -1,19 +1,19 @@ -package com.xcs.spring.config; - -import com.xcs.spring.event.MyEvent; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.ApplicationEventPublisherAware; - -public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { - - private ApplicationEventPublisher publisher; - - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { - this.publisher = publisher; - } - - public void publish(String message) { - publisher.publishEvent(new MyEvent(this, message)); - } -} +package com.xcs.spring.config; + +import com.xcs.spring.event.MyEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; + +public class MyApplicationEventPublisherAware implements ApplicationEventPublisherAware { + + private ApplicationEventPublisher publisher; + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + + public void publish(String message) { + publisher.publishEvent(new MyEvent(this, message)); + } +} diff --git a/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEvent.java b/spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEvent.java similarity index 95% rename from spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEvent.java rename to spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEvent.java index b946c64..dcda298 100644 --- a/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEvent.java +++ b/spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEvent.java @@ -1,17 +1,17 @@ -package com.xcs.spring.event; - -import org.springframework.context.ApplicationEvent; - -public class MyEvent extends ApplicationEvent { - - private final String message; - - public MyEvent(Object source, String message) { - super(source); - this.message = message; - } - - public String getMessage() { - return message; - } +package com.xcs.spring.event; + +import org.springframework.context.ApplicationEvent; + +public class MyEvent extends ApplicationEvent { + + private final String message; + + public MyEvent(Object source, String message) { + super(source); + this.message = message; + } + + public String getMessage() { + return message; + } } \ No newline at end of file diff --git a/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEventListener.java b/spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEventListener.java similarity index 96% rename from spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEventListener.java rename to spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEventListener.java index 46b78a8..a40c469 100644 --- a/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEventListener.java +++ b/spring-aware/spring-aware-applicationEventPublisherAware/src/main/java/com/xcs/spring/event/MyEventListener.java @@ -1,11 +1,11 @@ -package com.xcs.spring.event; - -import org.springframework.context.ApplicationListener; - -public class MyEventListener implements ApplicationListener { - - @Override - public void onApplicationEvent(MyEvent event) { - System.out.println("Received my event - " + event.getMessage()); - } +package com.xcs.spring.event; + +import org.springframework.context.ApplicationListener; + +public class MyEventListener implements ApplicationListener { + + @Override + public void onApplicationEvent(MyEvent event) { + System.out.println("Received my event - " + event.getMessage()); + } } \ No newline at end of file diff --git a/spring-aware-applicationStartupAware/README.md b/spring-aware/spring-aware-applicationStartupAware/README.md similarity index 98% rename from spring-aware-applicationStartupAware/README.md rename to spring-aware/spring-aware-applicationStartupAware/README.md index 1439807..a9592f9 100644 --- a/spring-aware-applicationStartupAware/README.md +++ b/spring-aware/spring-aware-applicationStartupAware/README.md @@ -1,480 +1,480 @@ -## ApplicationStartupAware - -- [ApplicationStartupAware](#applicationstartupaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`ApplicationStartup`接口,是为了提供对这一过程的细粒度跟踪。通过`StartupStep`,我们可以定义应用启动过程中的各个步骤,并收集关于它们的性能和上下文信息。 - -### 二、接口源码 - -`ApplicationStartupAware` 是 Spring 框架自 5.3 开始引入的一个核心接口。实现`ApplicationStartupAware`接口的对象会在Spring容器中被自动注入一个`ApplicationStartup`实例。 - -```java -/** - * 任何希望在运行时被通知其关联的ApplicationStartup实例的对象都应实现此接口。 - * 在更简单的术语中,这是一个使bean意识到应用启动跟踪机制的接口。 - * - * @author Brian Clozel - * @since 5.3 - * @see ApplicationContextAware - */ -public interface ApplicationStartupAware extends Aware { - - /** - * 设置此对象运行时的ApplicationStartup。 - * 此方法的调用时机为正常bean属性填充之后,但在任何初始化回调(例如InitializingBean的afterPropertiesSet或自定义的初始化方法)之前。 - * 并且在ApplicationContextAware的setApplicationContext之前调用。 - * - * @param applicationStartup 由此对象使用的application startup实例 - */ - void setApplicationStartup(ApplicationStartup applicationStartup); -} -``` - -### 三、主要功能 - -**启动性能跟踪**:通过提供对`ApplicationStartup`的访问,实现此接口的beans可以使用`StartupStep`API来跟踪它们在启动过程中的各个步骤。这对于检测和优化启动性能非常有用。 - -**为beans提供跟踪能力**:而不仅仅是Spring框架内部使用。这意味着开发者可以为他们的自定义beans或组件提供与Spring框架同样的启动跟踪能力。 - -**细粒度控制**:与`StartupStep`一起使用,`ApplicationStartupAware`允许beans对其启动过程中的特定部分进行跟踪,例如数据库初始化、外部服务连接或任何其他可能需要时间的操作。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),首先设置了`BufferingApplicationStartup`,这是Spring Boot提供的一个`ApplicationStartup`实现,缓存了最后的100个启动步骤。这使得我们可以在应用启动后查看并分析这些步骤,以便了解哪些操作可能会影响启动性能,然后使用`register`方法,我们告诉Spring上下文加载`MyConfiguration`类,最后调用`refresh`方法会触发应用上下文的初始化,包括bean的创建和依赖注入。 - -```java -public class ApplicationStartupAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.setApplicationStartup(new BufferingApplicationStartup(100)); - context.register(MyConfiguration.class); - context.refresh(); - context.close(); - } -} -``` - -这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyApplicationStartupAware` 被 Spring 容器执行。 - -```java -@Configuration -public class MyConfiguration { - - @Bean - public MyApplicationStartupAware myApplicationStartupAware(){ - return new MyApplicationStartupAware(); - } -} -``` - -`MyApplicationStartupAware`类的主要目的是展示如何使用`ApplicationStartup`来跟踪Spring应用程序启动过程中的特定逻辑。这对于我们程序来说是有用的,因为我们可以看到哪些启动步骤是最消耗时间的,然后据此进行优化。在这个具体的实现中,仅仅模拟了两个步骤,但在实际应用中,可以跟踪任意数量和类型的步骤。 - -```java -public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { - - private ApplicationStartup applicationStartup; - - @Override - public void setApplicationStartup(ApplicationStartup applicationStartup) { - this.applicationStartup = applicationStartup; - } - - @Override - public void afterPropertiesSet() throws Exception { - StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); - // 自定义逻辑 - Thread.sleep(200); - step1.tag("status", "done").end(); - - StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); - // 自定义逻辑 - Thread.sleep(300); - step2.tag("status", "done").end(); - } -} -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: ApplicationStartupAware时序图 - participant ApplicationStartupAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant ApplicationContextAwareProcessor - participant MyApplicationStartupAware - - ApplicationStartupAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 - AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 - ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 - ApplicationContextAwareProcessor->>MyApplicationStartupAware:setApplicationStartup(applicationStartup)
设置运行环境 - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>ApplicationStartupAwareApplication:初始化完成 -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),首先设置了`BufferingApplicationStartup`,这是Spring Boot提供的一个`ApplicationStartup`实现,缓存了最后的100个启动步骤。这使得我们可以在应用启动后查看并分析这些步骤,以便了解哪些操作可能会影响启动性能,然后使用`register`方法,我们告诉Spring上下文加载`MyConfiguration`类,最后调用`refresh`方法会触发应用上下文的初始化,包括bean的创建和依赖注入。 - -```java -public class ApplicationStartupAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.setApplicationStartup(new BufferingApplicationStartup(100)); - context.register(MyConfiguration.class); - context.refresh(); - context.close(); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - Object wrappedBean = bean; - if (mbd == null || !mbd.isSynthetic()) { - wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); - } - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 - -```java -@Override -public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) - throws BeansException { - - Object result = existingBean; - for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; - } - result = current; - } - return result; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 - -```java -@Override -@Nullable -public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || - bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || - bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || - bean instanceof ApplicationStartupAware)) { - return bean; - } - - // ... [代码部分省略以简化] - - invokeAwareInterfaces(bean); - - return bean; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 - -```java -private void invokeAwareInterfaces(Object bean) { - // ... [代码部分省略以简化] - if (bean instanceof ApplicationStartupAware) { - ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup()); - } - // ... [代码部分省略以简化] -} -``` - -最后执行到我们自定义的逻辑中,`MyApplicationStartupAware`类的主要目的是展示如何使用`ApplicationStartup`来跟踪Spring应用程序启动过程中的特定逻辑。这对于我们程序来说是有用的,因为我们可以看到哪些启动步骤是最消耗时间的,然后据此进行优化。在这个具体的实现中,仅仅模拟了两个步骤,但在实际应用中,可以跟踪任意数量和类型的步骤。 - -```java -public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { - - private ApplicationStartup applicationStartup; - - @Override - public void setApplicationStartup(ApplicationStartup applicationStartup) { - this.applicationStartup = applicationStartup; - } - - @Override - public void afterPropertiesSet() throws Exception { - StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); - // 自定义逻辑 - Thread.sleep(200); - step1.tag("status", "done").end(); - - StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); - // 自定义逻辑 - Thread.sleep(300); - step2.tag("status", "done").end(); - } -} -``` - -### 七、注意事项 - -**生命周期时机**:`setApplicationStartup`方法在其他bean属性设置之后、`InitializingBean`的`afterPropertiesSet`方法之前调用。确保你的bean在这一阶段不依赖于其他尚未初始化或注入的属性。 - -**性能考虑**:虽然启动跟踪对于分析应用程序启动时间很有用,但添加太多的启动步骤跟踪可能会对性能产生微小的影响。在生产环境中,你可能需要权衡跟踪的详细程度和性能的关系。 - -**清晰的步骤名称**:当定义`StartupStep`时,为其提供清晰、描述性的名称,这样其他开发者可以更容易地理解它代表的步骤。 - -**不要滥用**:尽量只为那些真正重要和可能影响启动性能的步骤使用启动跟踪。不需要为每个小操作都添加跟踪。 - -**不要忘记结束步骤**:每当你开始一个`StartupStep`,记得在适当的时机调用`end`方法结束它。否则,该步骤可能会在报告中显示为仍在运行,这可能会导致混淆。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**启动类概述**:使用`AnnotationConfigApplicationContext`,一个基于Java注解的Spring上下文初始化方法。设置`BufferingApplicationStartup`来缓存应用启动过程的最后100个步骤。这样可以分析哪些步骤可能影响启动性能。注册`MyConfiguration`类以加载相应的配置。刷新并初始化应用上下文,从而触发bean的创建和依赖注入。关闭上下文。 - -**配置类概述**:使用`@Configuration`注解标记,告诉Spring这是一个配置类。通过`@Bean`注解定义了`MyApplicationStartupAware` bean。这样可以确保它被Spring容器处理,并在容器启动时执行其生命周期方法。 - -**`MyApplicationStartupAware`类概述**:实现了`ApplicationStartupAware`接口,允许它在启动时获知`ApplicationStartup`实例。定义了两个启动步骤来模拟潜在的长时间运行任务,并使用`StartupStep`进行跟踪。在每个步骤的末尾,都有一个标记状态为"done",然后结束该步骤。 - -#### 8.2、源码分析总结 - -**实例化Beans**:在`AbstractApplicationContext`的`refresh()`方法中,`finishBeanFactoryInitialization`方法被调用,确保所有单例Bean被预先实例化。 - -**Bean预实例化**:`DefaultListableBeanFactory`的`preInstantiateSingletons`方法确保所有非懒加载的单例Beans被实例化。核心操作是调用`getBean(beanName)`。 - -**获取Bean实例**:`AbstractBeanFactory`的`getBean`方法进一步调用`doGetBean`来真正实例化Bean,处理异常和依赖,并返回Bean实例。 - -**Bean单例获取**:`DefaultSingletonBeanRegistry`的`getSingleton`方法确保Bean以单例形式存在,从缓存获取或使用提供的`ObjectFactory`创建新实例。 - -**创建Bean实例**:`AbstractAutowireCapableBeanFactory`的`createBean`方法调用`doCreateBean`进行Bean的实际实例化,并进行初始化,确保Bean完全配置并准备就绪。 - -**Bean初始化**:`AbstractAutowireCapableBeanFactory`的`initializeBean`方法确保Bean被正确初始化,其中调用`applyBeanPostProcessorsBeforeInitialization`方法是Spring生命周期中的关键点,允许BeanPostProcessors在Bean初始化之前进行操作。 - -**处理Aware接口**:在Bean初始化过程中,`ApplicationContextAwareProcessor`确保实现了`Aware`接口的Beans被正确处理,这些Beans会自动"感知"并获得其运行环境或特定依赖的引用。 - +## ApplicationStartupAware + +- [ApplicationStartupAware](#applicationstartupaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`ApplicationStartup`接口,是为了提供对这一过程的细粒度跟踪。通过`StartupStep`,我们可以定义应用启动过程中的各个步骤,并收集关于它们的性能和上下文信息。 + +### 二、接口源码 + +`ApplicationStartupAware` 是 Spring 框架自 5.3 开始引入的一个核心接口。实现`ApplicationStartupAware`接口的对象会在Spring容器中被自动注入一个`ApplicationStartup`实例。 + +```java +/** + * 任何希望在运行时被通知其关联的ApplicationStartup实例的对象都应实现此接口。 + * 在更简单的术语中,这是一个使bean意识到应用启动跟踪机制的接口。 + * + * @author Brian Clozel + * @since 5.3 + * @see ApplicationContextAware + */ +public interface ApplicationStartupAware extends Aware { + + /** + * 设置此对象运行时的ApplicationStartup。 + * 此方法的调用时机为正常bean属性填充之后,但在任何初始化回调(例如InitializingBean的afterPropertiesSet或自定义的初始化方法)之前。 + * 并且在ApplicationContextAware的setApplicationContext之前调用。 + * + * @param applicationStartup 由此对象使用的application startup实例 + */ + void setApplicationStartup(ApplicationStartup applicationStartup); +} +``` + +### 三、主要功能 + +**启动性能跟踪**:通过提供对`ApplicationStartup`的访问,实现此接口的beans可以使用`StartupStep`API来跟踪它们在启动过程中的各个步骤。这对于检测和优化启动性能非常有用。 + +**为beans提供跟踪能力**:而不仅仅是Spring框架内部使用。这意味着开发者可以为他们的自定义beans或组件提供与Spring框架同样的启动跟踪能力。 + +**细粒度控制**:与`StartupStep`一起使用,`ApplicationStartupAware`允许beans对其启动过程中的特定部分进行跟踪,例如数据库初始化、外部服务连接或任何其他可能需要时间的操作。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),首先设置了`BufferingApplicationStartup`,这是Spring Boot提供的一个`ApplicationStartup`实现,缓存了最后的100个启动步骤。这使得我们可以在应用启动后查看并分析这些步骤,以便了解哪些操作可能会影响启动性能,然后使用`register`方法,我们告诉Spring上下文加载`MyConfiguration`类,最后调用`refresh`方法会触发应用上下文的初始化,包括bean的创建和依赖注入。 + +```java +public class ApplicationStartupAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.setApplicationStartup(new BufferingApplicationStartup(100)); + context.register(MyConfiguration.class); + context.refresh(); + context.close(); + } +} +``` + +这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyApplicationStartupAware` 被 Spring 容器执行。 + +```java +@Configuration +public class MyConfiguration { + + @Bean + public MyApplicationStartupAware myApplicationStartupAware(){ + return new MyApplicationStartupAware(); + } +} +``` + +`MyApplicationStartupAware`类的主要目的是展示如何使用`ApplicationStartup`来跟踪Spring应用程序启动过程中的特定逻辑。这对于我们程序来说是有用的,因为我们可以看到哪些启动步骤是最消耗时间的,然后据此进行优化。在这个具体的实现中,仅仅模拟了两个步骤,但在实际应用中,可以跟踪任意数量和类型的步骤。 + +```java +public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { + + private ApplicationStartup applicationStartup; + + @Override + public void setApplicationStartup(ApplicationStartup applicationStartup) { + this.applicationStartup = applicationStartup; + } + + @Override + public void afterPropertiesSet() throws Exception { + StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); + // 自定义逻辑 + Thread.sleep(200); + step1.tag("status", "done").end(); + + StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); + // 自定义逻辑 + Thread.sleep(300); + step2.tag("status", "done").end(); + } +} +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: ApplicationStartupAware时序图 + participant ApplicationStartupAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant ApplicationContextAwareProcessor + participant MyApplicationStartupAware + + ApplicationStartupAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 + AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 + ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 + ApplicationContextAwareProcessor->>MyApplicationStartupAware:setApplicationStartup(applicationStartup)
设置运行环境 + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>ApplicationStartupAwareApplication:初始化完成 +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),首先设置了`BufferingApplicationStartup`,这是Spring Boot提供的一个`ApplicationStartup`实现,缓存了最后的100个启动步骤。这使得我们可以在应用启动后查看并分析这些步骤,以便了解哪些操作可能会影响启动性能,然后使用`register`方法,我们告诉Spring上下文加载`MyConfiguration`类,最后调用`refresh`方法会触发应用上下文的初始化,包括bean的创建和依赖注入。 + +```java +public class ApplicationStartupAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.setApplicationStartup(new BufferingApplicationStartup(100)); + context.register(MyConfiguration.class); + context.refresh(); + context.close(); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 + +```java +@Override +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 + +```java +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || + bean instanceof ApplicationStartupAware)) { + return bean; + } + + // ... [代码部分省略以简化] + + invokeAwareInterfaces(bean); + + return bean; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 + +```java +private void invokeAwareInterfaces(Object bean) { + // ... [代码部分省略以简化] + if (bean instanceof ApplicationStartupAware) { + ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup()); + } + // ... [代码部分省略以简化] +} +``` + +最后执行到我们自定义的逻辑中,`MyApplicationStartupAware`类的主要目的是展示如何使用`ApplicationStartup`来跟踪Spring应用程序启动过程中的特定逻辑。这对于我们程序来说是有用的,因为我们可以看到哪些启动步骤是最消耗时间的,然后据此进行优化。在这个具体的实现中,仅仅模拟了两个步骤,但在实际应用中,可以跟踪任意数量和类型的步骤。 + +```java +public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { + + private ApplicationStartup applicationStartup; + + @Override + public void setApplicationStartup(ApplicationStartup applicationStartup) { + this.applicationStartup = applicationStartup; + } + + @Override + public void afterPropertiesSet() throws Exception { + StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); + // 自定义逻辑 + Thread.sleep(200); + step1.tag("status", "done").end(); + + StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); + // 自定义逻辑 + Thread.sleep(300); + step2.tag("status", "done").end(); + } +} +``` + +### 七、注意事项 + +**生命周期时机**:`setApplicationStartup`方法在其他bean属性设置之后、`InitializingBean`的`afterPropertiesSet`方法之前调用。确保你的bean在这一阶段不依赖于其他尚未初始化或注入的属性。 + +**性能考虑**:虽然启动跟踪对于分析应用程序启动时间很有用,但添加太多的启动步骤跟踪可能会对性能产生微小的影响。在生产环境中,你可能需要权衡跟踪的详细程度和性能的关系。 + +**清晰的步骤名称**:当定义`StartupStep`时,为其提供清晰、描述性的名称,这样其他开发者可以更容易地理解它代表的步骤。 + +**不要滥用**:尽量只为那些真正重要和可能影响启动性能的步骤使用启动跟踪。不需要为每个小操作都添加跟踪。 + +**不要忘记结束步骤**:每当你开始一个`StartupStep`,记得在适当的时机调用`end`方法结束它。否则,该步骤可能会在报告中显示为仍在运行,这可能会导致混淆。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**启动类概述**:使用`AnnotationConfigApplicationContext`,一个基于Java注解的Spring上下文初始化方法。设置`BufferingApplicationStartup`来缓存应用启动过程的最后100个步骤。这样可以分析哪些步骤可能影响启动性能。注册`MyConfiguration`类以加载相应的配置。刷新并初始化应用上下文,从而触发bean的创建和依赖注入。关闭上下文。 + +**配置类概述**:使用`@Configuration`注解标记,告诉Spring这是一个配置类。通过`@Bean`注解定义了`MyApplicationStartupAware` bean。这样可以确保它被Spring容器处理,并在容器启动时执行其生命周期方法。 + +**`MyApplicationStartupAware`类概述**:实现了`ApplicationStartupAware`接口,允许它在启动时获知`ApplicationStartup`实例。定义了两个启动步骤来模拟潜在的长时间运行任务,并使用`StartupStep`进行跟踪。在每个步骤的末尾,都有一个标记状态为"done",然后结束该步骤。 + +#### 8.2、源码分析总结 + +**实例化Beans**:在`AbstractApplicationContext`的`refresh()`方法中,`finishBeanFactoryInitialization`方法被调用,确保所有单例Bean被预先实例化。 + +**Bean预实例化**:`DefaultListableBeanFactory`的`preInstantiateSingletons`方法确保所有非懒加载的单例Beans被实例化。核心操作是调用`getBean(beanName)`。 + +**获取Bean实例**:`AbstractBeanFactory`的`getBean`方法进一步调用`doGetBean`来真正实例化Bean,处理异常和依赖,并返回Bean实例。 + +**Bean单例获取**:`DefaultSingletonBeanRegistry`的`getSingleton`方法确保Bean以单例形式存在,从缓存获取或使用提供的`ObjectFactory`创建新实例。 + +**创建Bean实例**:`AbstractAutowireCapableBeanFactory`的`createBean`方法调用`doCreateBean`进行Bean的实际实例化,并进行初始化,确保Bean完全配置并准备就绪。 + +**Bean初始化**:`AbstractAutowireCapableBeanFactory`的`initializeBean`方法确保Bean被正确初始化,其中调用`applyBeanPostProcessorsBeforeInitialization`方法是Spring生命周期中的关键点,允许BeanPostProcessors在Bean初始化之前进行操作。 + +**处理Aware接口**:在Bean初始化过程中,`ApplicationContextAwareProcessor`确保实现了`Aware`接口的Beans被正确处理,这些Beans会自动"感知"并获得其运行环境或特定依赖的引用。 + **自定义逻辑执行**:`MyApplicationStartupAware`类实现了`ApplicationStartupAware`接口,它将接收一个`ApplicationStartup`实例。 \ No newline at end of file diff --git a/spring-aware-applicationStartupAware/pom.xml b/spring-aware/spring-aware-applicationStartupAware/pom.xml similarity index 92% rename from spring-aware-applicationStartupAware/pom.xml rename to spring-aware/spring-aware-applicationStartupAware/pom.xml index e03dcc3..17c309a 100644 --- a/spring-aware-applicationStartupAware/pom.xml +++ b/spring-aware/spring-aware-applicationStartupAware/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-aware com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/ApplicationStartupAwareApplication.java b/spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/ApplicationStartupAwareApplication.java similarity index 100% rename from spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/ApplicationStartupAwareApplication.java rename to spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/ApplicationStartupAwareApplication.java diff --git a/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyApplicationStartupAware.java b/spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyApplicationStartupAware.java similarity index 97% rename from spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyApplicationStartupAware.java rename to spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyApplicationStartupAware.java index 082fdca..f289aab 100644 --- a/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyApplicationStartupAware.java +++ b/spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyApplicationStartupAware.java @@ -1,31 +1,31 @@ -package com.xcs.spring.config; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationStartupAware; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; -import org.springframework.core.metrics.ApplicationStartup; -import org.springframework.core.metrics.StartupStep; - -public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { - - private ApplicationStartup applicationStartup; - - @Override - public void setApplicationStartup(ApplicationStartup applicationStartup) { - this.applicationStartup = applicationStartup; - } - - @Override - public void afterPropertiesSet() throws Exception { - StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); - // 自定义逻辑 - Thread.sleep(200); - step1.tag("status", "done").end(); - - StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); - // 自定义逻辑 - Thread.sleep(300); - step2.tag("status", "done").end(); - } -} +package com.xcs.spring.config; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationStartupAware; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; +import org.springframework.core.metrics.ApplicationStartup; +import org.springframework.core.metrics.StartupStep; + +public class MyApplicationStartupAware implements ApplicationStartupAware, InitializingBean { + + private ApplicationStartup applicationStartup; + + @Override + public void setApplicationStartup(ApplicationStartup applicationStartup) { + this.applicationStartup = applicationStartup; + } + + @Override + public void afterPropertiesSet() throws Exception { + StartupStep step1 = applicationStartup.start("MyApplicationStartupAware Logic Step 1"); + // 自定义逻辑 + Thread.sleep(200); + step1.tag("status", "done").end(); + + StartupStep step2 = applicationStartup.start("MyApplicationStartupAware Logic Step 2"); + // 自定义逻辑 + Thread.sleep(300); + step2.tag("status", "done").end(); + } +} diff --git a/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-applicationStartupAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-beanClassLoaderAware/README.md b/spring-aware/spring-aware-beanClassLoaderAware/README.md similarity index 97% rename from spring-aware-beanClassLoaderAware/README.md rename to spring-aware/spring-aware-beanClassLoaderAware/README.md index f5c7378..2abc2bc 100644 --- a/spring-aware-beanClassLoaderAware/README.md +++ b/spring-aware/spring-aware-beanClassLoaderAware/README.md @@ -1,474 +1,474 @@ -## BeanClassLoaderAware - -- [BeanClassLoaderAware](#beanclassloaderaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`BeanClassLoaderAware` 接口,允许 bean 得知其加载的类加载器。当一个 bean 实现了这个接口时,Spring 容器在 bean 初始化的时候会设置它的类加载器。 - -### 二、接口源码 - -`BeanClassLoaderAware` 是 Spring 框架自 2.0 版本开始引入的一个核心接口。当一个 bean 实现了这个接口,并在 Spring 容器中被初始化时,Spring 容器会自动调用 `setClassLoader` 方法,并将加载该 bean 的类加载器传入。 - -```java -/** - * 回调接口,允许 bean 了解其所使用的 ClassLoader 类加载器; - * 也就是当前 bean 工厂用于加载 bean 类的类加载器。 - * - * 主要目的是供那些需要按名称选择应用类的框架类实现,尽管这些框架类可能是由共享的类加载器加载的。 - * - * 对于所有 bean 生命周期方法的列表,请参阅 BeanFactory BeanFactory 的 javadocs。 - * - * @author Juergen Hoeller - * @author Chris Beams - * @since 2.0 - * @see BeanNameAware - * @see BeanFactoryAware - * @see InitializingBean - */ -public interface BeanClassLoaderAware extends Aware { - - /** - * 提供 bean 的 ClassLoader 类加载器 的回调方法。 - * 在填充普通的 bean 属性之后但在初始化回调(如 InitializingBean InitializingBean 的 - * InitializingBean#afterPropertiesSet() 方法或自定义初始化方法)之前调用此方法。 - * - * @param classLoader 拥有的类加载器 - */ - void setBeanClassLoader(ClassLoader classLoader); - -} -``` - -### 三、主要功能 - -**提供类加载器信息**:Bean 可以得知其加载的类加载器,从而可以利用该类加载器进行动态的类加载或资源查找。 - -**框架与应用类加载器隔离**:在某些复杂的环境中,框架类和应用程序类可能是由不同的类加载器加载的。例如,在某些应用服务器环境中,框架可能是由共享的类加载器加载的,而应用程序类是由专门的类加载器加载的。通过 `BeanClassLoaderAware`,框架类可以确保使用正确的类加载器来加载或访问应用程序类。 - -**生命周期管理**:`setBeanClassLoader` 方法会在填充 bean 的普通属性之后但在调用任何初始化回调(如 `InitializingBean#afterPropertiesSet()`)之前被调用。这确保了在 bean 的生命周期的合适阶段提供类加载器信息。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBeanClassLoaderAware`类型的bean,最后调用`loadAndExecute`方法。 - -```java -public class BeanClassLoaderAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyBeanClassLoaderAware myBeanClassLoaderAware = context.getBean(MyBeanClassLoaderAware.class); - myBeanClassLoaderAware.loadAndExecute(); - } -} -``` - -这里使用`@Bean`注解,定义了一个Bean,是为了确保`MyBeanClassLoaderAware` 被 Spring 容器执行 - -```java -@Configuration -public class MyConfiguration { - - @Bean - public MyBeanClassLoaderAware myBeanClassLoaderAware(){ - return new MyBeanClassLoaderAware(); - } -} -``` - -在`MyBeanClassLoaderAware` 类中,我们实现了 `BeanClassLoaderAware` 接口,允许这个 bean 在初始化时获取其 `ClassLoader`。接着,在 `loadAndExecute` 方法中,你使用这个 `ClassLoader` 来动态加载一个名为 `com.xcs.spring.service.UserServiceImpl` 的类并执行它的 `getUserInfo` 方法。 - -```java -public class MyBeanClassLoaderAware implements BeanClassLoaderAware { - - private ClassLoader classLoader; - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - public void loadAndExecute() { - try { - Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); - UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); - System.out.println("UserInfo = " + instance.getUserInfo()); - } catch (Exception e) { - e.printStackTrace(); - } - } -} -``` - -定义一个接口与此接口的实现类。 - -```java -package com.xcs.spring.service; - -public interface UserService { - String getUserInfo(); -} - -public class UserServiceImpl implements UserService { - @Override - public String getUserInfo() { - return "this is user info"; - } -} -``` - -运行结果发现,通过这种方式,我们保证了`MyBeanClassLoaderAware`的代码与`UserServiceImpl`的具体实现解耦。这意味着,如果`UserServiceImpl`的具体实现发生了变化,或者有了新的实现,只要我们遵循`UserService`接口,我们的`MyBeanClassLoaderAware`代码就不需要任何更改。 - -```java -UserInfo = this is user info -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: BeanClassLoaderAware时序图 - participant BeanFactoryAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant BeanClassLoaderAware - - BeanFactoryAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 - AbstractAutowireCapableBeanFactory->>BeanClassLoaderAware:setBeanClassLoader(classLoader)
设置classLoader - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>BeanFactoryAwareApplication:初始化完成 - -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBeanClassLoaderAware`类型的bean,最后调用`loadAndExecute`方法。 - -```java -public class BeanClassLoaderAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyBeanClassLoaderAware myBeanClassLoaderAware = context.getBean(MyBeanClassLoaderAware.class); - myBeanClassLoaderAware.loadAndExecute(); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); - -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - invokeAwareMethods(beanName, bean); - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 - -```java -private void invokeAwareMethods(String beanName, Object bean) { - if (bean instanceof Aware) { - if (bean instanceof BeanNameAware) { - // ... [代码部分省略以简化] - } - if (bean instanceof BeanClassLoaderAware) { - ClassLoader bcl = getBeanClassLoader(); - if (bcl != null) { - ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); - } - } - if (bean instanceof BeanFactoryAware) { - // ... [代码部分省略以简化] - } - } -} -``` - -最后执行到我们自定义的逻辑中,我们实现了 `BeanClassLoaderAware` 接口,允许这个 bean 在初始化时获取其 `ClassLoader`。 - -```java -public class MyBeanClassLoaderAware implements BeanClassLoaderAware { - - private ClassLoader classLoader; - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - public void loadAndExecute() { - try { - Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); - UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); - System.out.println("UserInfo = " + instance.getUserInfo()); - } catch (Exception e) { - e.printStackTrace(); - } - } -} -``` - -### 七、注意事项 - -**避免过度使用**:只有当你真的需要访问类加载器时才使用 `BeanClassLoaderAware`。不要滥用它,因为这可能会导致代码与Spring框架过度耦合。 - -**考虑类加载器层次结构**:在Java中,类加载器通常有一个父子关系。如果你不能使用当前的类加载器找到一个类,可能需要检查其父类加载器。 - -**不要在setter中执行复杂的逻辑**:`setBeanClassLoader` 是一个setter方法,应该避免在其中执行复杂的逻辑。它应该只用于设置类加载器。 - -**避免存储状态**:尽量不要在实现了`BeanClassLoaderAware`的bean中存储状态或依赖于其他bean的状态。这会使bean的生命周期和初始化更加复杂。 - -**考虑使用其他技术**:在某些情况下,可能有其他技术或方法可以达到同样的目的,而无需使用 `BeanClassLoaderAware`。例如,使用Spring的`@Value`注解或`ResourceLoader`来加载资源,而不是直接使用类加载器。 - -**考虑类加载器层次结构**:在Java中,类加载器通常有一个父子关系。如果你不能使用当前的类加载器找到一个类,可能需要检查其父类加载器。 - -**资源管理**:类加载器不仅可以加载类,还可以用来加载其他资源(例如,属性文件)。但是,要小心确保资源路径正确,并记住类加载器的搜索行为可能与文件系统或其他加载机制不同。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**启动及上下文配置:**利用 `AnnotationConfigApplicationContext` 初始化Spring容器,使用 `MyConfiguration` 作为配置类来为Spring上下文提供设置。 - -**配置类定义:**标记配置类为 `@Configuration`,使用 `@Bean` 注解来确保 `MyBeanClassLoaderAware` 被Spring容器管理。 - -**实现 `BeanClassLoaderAware`:**通过实现 `BeanClassLoaderAware` 接口,bean 可以在初始化时获得其加载的 `ClassLoader`,在 `loadAndExecute` 方法中,动态加载并执行特定的服务方法。 - -**接口与实现:**定义清晰的 `UserService` 接口和相应的 `UserServiceImpl` 实现。 - -**结果及结论:**运行程序后,我们能够看到预期输出,这表明成功地将 `MyBeanClassLoaderAware` 与特定实现解耦。 - -#### 8.2、源码分析总结 - -**应用启动入口**: 通过`AnnotationConfigApplicationContext`,以`MyConfiguration`为配置类,初始化Spring容器。随后获取`MyBeanClassLoaderAware` bean并调用其`loadAndExecute`方法。 - -**初始化Spring上下文**: 在`AnnotationConfigApplicationContext`构造函数中,`refresh()`方法被调用来刷新或初始化Spring容器。 - -**Bean的预实例化**: 在Spring的上下文初始化的`refresh()`方法中,`finishBeanFactoryInitialization(beanFactory)`方法确保所有非延迟加载的单例bean被实例化。 - -**单例Bean的创建**: `DefaultListableBeanFactory`中的`preInstantiateSingletons`方法负责预先实例化所有非懒加载的单例bean。它会对容器中的每个单例bean调用`getBean`方法。 - -**Bean的实例化及初始化**: 在获取bean的过程中,如果bean还未创建,`doCreateBean`方法会被调用,完成bean的实例化、属性填充和初始化。 - -**处理Aware接口族**: 在bean的初始化阶段,`invokeAwareMethods`方法确保任何实现了`Aware`接口族(如`BeanNameAware`、`BeanClassLoaderAware`等)的bean都会得到适当的回调。 - -**BeanClassLoaderAware的实现**: 对于实现了`BeanClassLoaderAware`接口的bean,Spring容器在初始化阶段会通过`setBeanClassLoader`方法设置bean的`ClassLoader`。 - +## BeanClassLoaderAware + +- [BeanClassLoaderAware](#beanclassloaderaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`BeanClassLoaderAware` 接口,允许 bean 得知其加载的类加载器。当一个 bean 实现了这个接口时,Spring 容器在 bean 初始化的时候会设置它的类加载器。 + +### 二、接口源码 + +`BeanClassLoaderAware` 是 Spring 框架自 2.0 版本开始引入的一个核心接口。当一个 bean 实现了这个接口,并在 Spring 容器中被初始化时,Spring 容器会自动调用 `setClassLoader` 方法,并将加载该 bean 的类加载器传入。 + +```java +/** + * 回调接口,允许 bean 了解其所使用的 ClassLoader 类加载器; + * 也就是当前 bean 工厂用于加载 bean 类的类加载器。 + * + * 主要目的是供那些需要按名称选择应用类的框架类实现,尽管这些框架类可能是由共享的类加载器加载的。 + * + * 对于所有 bean 生命周期方法的列表,请参阅 BeanFactory BeanFactory 的 javadocs。 + * + * @author Juergen Hoeller + * @author Chris Beams + * @since 2.0 + * @see BeanNameAware + * @see BeanFactoryAware + * @see InitializingBean + */ +public interface BeanClassLoaderAware extends Aware { + + /** + * 提供 bean 的 ClassLoader 类加载器 的回调方法。 + * 在填充普通的 bean 属性之后但在初始化回调(如 InitializingBean InitializingBean 的 + * InitializingBean#afterPropertiesSet() 方法或自定义初始化方法)之前调用此方法。 + * + * @param classLoader 拥有的类加载器 + */ + void setBeanClassLoader(ClassLoader classLoader); + +} +``` + +### 三、主要功能 + +**提供类加载器信息**:Bean 可以得知其加载的类加载器,从而可以利用该类加载器进行动态的类加载或资源查找。 + +**框架与应用类加载器隔离**:在某些复杂的环境中,框架类和应用程序类可能是由不同的类加载器加载的。例如,在某些应用服务器环境中,框架可能是由共享的类加载器加载的,而应用程序类是由专门的类加载器加载的。通过 `BeanClassLoaderAware`,框架类可以确保使用正确的类加载器来加载或访问应用程序类。 + +**生命周期管理**:`setBeanClassLoader` 方法会在填充 bean 的普通属性之后但在调用任何初始化回调(如 `InitializingBean#afterPropertiesSet()`)之前被调用。这确保了在 bean 的生命周期的合适阶段提供类加载器信息。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBeanClassLoaderAware`类型的bean,最后调用`loadAndExecute`方法。 + +```java +public class BeanClassLoaderAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyBeanClassLoaderAware myBeanClassLoaderAware = context.getBean(MyBeanClassLoaderAware.class); + myBeanClassLoaderAware.loadAndExecute(); + } +} +``` + +这里使用`@Bean`注解,定义了一个Bean,是为了确保`MyBeanClassLoaderAware` 被 Spring 容器执行 + +```java +@Configuration +public class MyConfiguration { + + @Bean + public MyBeanClassLoaderAware myBeanClassLoaderAware(){ + return new MyBeanClassLoaderAware(); + } +} +``` + +在`MyBeanClassLoaderAware` 类中,我们实现了 `BeanClassLoaderAware` 接口,允许这个 bean 在初始化时获取其 `ClassLoader`。接着,在 `loadAndExecute` 方法中,你使用这个 `ClassLoader` 来动态加载一个名为 `com.xcs.spring.service.UserServiceImpl` 的类并执行它的 `getUserInfo` 方法。 + +```java +public class MyBeanClassLoaderAware implements BeanClassLoaderAware { + + private ClassLoader classLoader; + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + public void loadAndExecute() { + try { + Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); + UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); + System.out.println("UserInfo = " + instance.getUserInfo()); + } catch (Exception e) { + e.printStackTrace(); + } + } +} +``` + +定义一个接口与此接口的实现类。 + +```java +package com.xcs.spring.service; + +public interface UserService { + String getUserInfo(); +} + +public class UserServiceImpl implements UserService { + @Override + public String getUserInfo() { + return "this is user info"; + } +} +``` + +运行结果发现,通过这种方式,我们保证了`MyBeanClassLoaderAware`的代码与`UserServiceImpl`的具体实现解耦。这意味着,如果`UserServiceImpl`的具体实现发生了变化,或者有了新的实现,只要我们遵循`UserService`接口,我们的`MyBeanClassLoaderAware`代码就不需要任何更改。 + +```java +UserInfo = this is user info +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: BeanClassLoaderAware时序图 + participant BeanFactoryAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant BeanClassLoaderAware + + BeanFactoryAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 + AbstractAutowireCapableBeanFactory->>BeanClassLoaderAware:setBeanClassLoader(classLoader)
设置classLoader + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>BeanFactoryAwareApplication:初始化完成 + +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyBeanClassLoaderAware`类型的bean,最后调用`loadAndExecute`方法。 + +```java +public class BeanClassLoaderAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyBeanClassLoaderAware myBeanClassLoaderAware = context.getBean(MyBeanClassLoaderAware.class); + myBeanClassLoaderAware.loadAndExecute(); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); + +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + invokeAwareMethods(beanName, bean); + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 + +```java +private void invokeAwareMethods(String beanName, Object bean) { + if (bean instanceof Aware) { + if (bean instanceof BeanNameAware) { + // ... [代码部分省略以简化] + } + if (bean instanceof BeanClassLoaderAware) { + ClassLoader bcl = getBeanClassLoader(); + if (bcl != null) { + ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); + } + } + if (bean instanceof BeanFactoryAware) { + // ... [代码部分省略以简化] + } + } +} +``` + +最后执行到我们自定义的逻辑中,我们实现了 `BeanClassLoaderAware` 接口,允许这个 bean 在初始化时获取其 `ClassLoader`。 + +```java +public class MyBeanClassLoaderAware implements BeanClassLoaderAware { + + private ClassLoader classLoader; + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + public void loadAndExecute() { + try { + Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); + UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); + System.out.println("UserInfo = " + instance.getUserInfo()); + } catch (Exception e) { + e.printStackTrace(); + } + } +} +``` + +### 七、注意事项 + +**避免过度使用**:只有当你真的需要访问类加载器时才使用 `BeanClassLoaderAware`。不要滥用它,因为这可能会导致代码与Spring框架过度耦合。 + +**考虑类加载器层次结构**:在Java中,类加载器通常有一个父子关系。如果你不能使用当前的类加载器找到一个类,可能需要检查其父类加载器。 + +**不要在setter中执行复杂的逻辑**:`setBeanClassLoader` 是一个setter方法,应该避免在其中执行复杂的逻辑。它应该只用于设置类加载器。 + +**避免存储状态**:尽量不要在实现了`BeanClassLoaderAware`的bean中存储状态或依赖于其他bean的状态。这会使bean的生命周期和初始化更加复杂。 + +**考虑使用其他技术**:在某些情况下,可能有其他技术或方法可以达到同样的目的,而无需使用 `BeanClassLoaderAware`。例如,使用Spring的`@Value`注解或`ResourceLoader`来加载资源,而不是直接使用类加载器。 + +**考虑类加载器层次结构**:在Java中,类加载器通常有一个父子关系。如果你不能使用当前的类加载器找到一个类,可能需要检查其父类加载器。 + +**资源管理**:类加载器不仅可以加载类,还可以用来加载其他资源(例如,属性文件)。但是,要小心确保资源路径正确,并记住类加载器的搜索行为可能与文件系统或其他加载机制不同。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**启动及上下文配置:**利用 `AnnotationConfigApplicationContext` 初始化Spring容器,使用 `MyConfiguration` 作为配置类来为Spring上下文提供设置。 + +**配置类定义:**标记配置类为 `@Configuration`,使用 `@Bean` 注解来确保 `MyBeanClassLoaderAware` 被Spring容器管理。 + +**实现 `BeanClassLoaderAware`:**通过实现 `BeanClassLoaderAware` 接口,bean 可以在初始化时获得其加载的 `ClassLoader`,在 `loadAndExecute` 方法中,动态加载并执行特定的服务方法。 + +**接口与实现:**定义清晰的 `UserService` 接口和相应的 `UserServiceImpl` 实现。 + +**结果及结论:**运行程序后,我们能够看到预期输出,这表明成功地将 `MyBeanClassLoaderAware` 与特定实现解耦。 + +#### 8.2、源码分析总结 + +**应用启动入口**: 通过`AnnotationConfigApplicationContext`,以`MyConfiguration`为配置类,初始化Spring容器。随后获取`MyBeanClassLoaderAware` bean并调用其`loadAndExecute`方法。 + +**初始化Spring上下文**: 在`AnnotationConfigApplicationContext`构造函数中,`refresh()`方法被调用来刷新或初始化Spring容器。 + +**Bean的预实例化**: 在Spring的上下文初始化的`refresh()`方法中,`finishBeanFactoryInitialization(beanFactory)`方法确保所有非延迟加载的单例bean被实例化。 + +**单例Bean的创建**: `DefaultListableBeanFactory`中的`preInstantiateSingletons`方法负责预先实例化所有非懒加载的单例bean。它会对容器中的每个单例bean调用`getBean`方法。 + +**Bean的实例化及初始化**: 在获取bean的过程中,如果bean还未创建,`doCreateBean`方法会被调用,完成bean的实例化、属性填充和初始化。 + +**处理Aware接口族**: 在bean的初始化阶段,`invokeAwareMethods`方法确保任何实现了`Aware`接口族(如`BeanNameAware`、`BeanClassLoaderAware`等)的bean都会得到适当的回调。 + +**BeanClassLoaderAware的实现**: 对于实现了`BeanClassLoaderAware`接口的bean,Spring容器在初始化阶段会通过`setBeanClassLoader`方法设置bean的`ClassLoader`。 + **自定义逻辑的执行**: 在`MyBeanClassLoaderAware`中,已经保存了bean的类加载器,然后在`loadAndExecute`方法中,利用这个类加载器动态加载并执行特定的类和方法。 \ No newline at end of file diff --git a/spring-aware/spring-aware-beanClassLoaderAware/pom.xml b/spring-aware/spring-aware-beanClassLoaderAware/pom.xml new file mode 100644 index 0000000..e66c7b8 --- /dev/null +++ b/spring-aware/spring-aware-beanClassLoaderAware/pom.xml @@ -0,0 +1,14 @@ + + + + spring-aware + com.xcs.spring + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aware-beanClassLoaderAware + + \ No newline at end of file diff --git a/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/BeanClassLoaderAwareApplication.java b/spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/BeanClassLoaderAwareApplication.java similarity index 100% rename from spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/BeanClassLoaderAwareApplication.java rename to spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/BeanClassLoaderAwareApplication.java diff --git a/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyBeanClassLoaderAware.java b/spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyBeanClassLoaderAware.java similarity index 97% rename from spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyBeanClassLoaderAware.java rename to spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyBeanClassLoaderAware.java index 77dbd5b..d23e33b 100644 --- a/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyBeanClassLoaderAware.java +++ b/spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyBeanClassLoaderAware.java @@ -1,24 +1,24 @@ -package com.xcs.spring.config; - -import com.xcs.spring.service.UserServiceImpl; -import org.springframework.beans.factory.BeanClassLoaderAware; - -public class MyBeanClassLoaderAware implements BeanClassLoaderAware { - - private ClassLoader classLoader; - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - public void loadAndExecute() { - try { - Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); - UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); - System.out.println("UserInfo = " + instance.getUserInfo()); - } catch (Exception e) { - e.printStackTrace(); - } - } -} +package com.xcs.spring.config; + +import com.xcs.spring.service.UserServiceImpl; +import org.springframework.beans.factory.BeanClassLoaderAware; + +public class MyBeanClassLoaderAware implements BeanClassLoaderAware { + + private ClassLoader classLoader; + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + public void loadAndExecute() { + try { + Class clazz = classLoader.loadClass("com.xcs.spring.service.UserServiceImpl"); + UserServiceImpl instance = (UserServiceImpl) clazz.getDeclaredConstructor().newInstance(); + System.out.println("UserInfo = " + instance.getUserInfo()); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserService.java b/spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserService.java similarity index 93% rename from spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserService.java rename to spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserService.java index 127e799..c5f7d9e 100644 --- a/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserService.java +++ b/spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserService.java @@ -1,6 +1,6 @@ -package com.xcs.spring.service; - -public interface UserService { - - String getUserInfo(); -} +package com.xcs.spring.service; + +public interface UserService { + + String getUserInfo(); +} diff --git a/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserServiceImpl.java b/spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserServiceImpl.java similarity index 95% rename from spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserServiceImpl.java rename to spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserServiceImpl.java index 421e397..5371bf8 100644 --- a/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserServiceImpl.java +++ b/spring-aware/spring-aware-beanClassLoaderAware/src/main/java/com/xcs/spring/service/UserServiceImpl.java @@ -1,8 +1,8 @@ -package com.xcs.spring.service; - -public class UserServiceImpl implements UserService { - @Override - public String getUserInfo() { - return "this is user info"; - } -} +package com.xcs.spring.service; + +public class UserServiceImpl implements UserService { + @Override + public String getUserInfo() { + return "this is user info"; + } +} diff --git a/spring-aware-beanFactoryAware/README.md b/spring-aware/spring-aware-beanFactoryAware/README.md similarity index 97% rename from spring-aware-beanFactoryAware/README.md rename to spring-aware/spring-aware-beanFactoryAware/README.md index c38559a..d6c03ea 100644 --- a/spring-aware-beanFactoryAware/README.md +++ b/spring-aware/spring-aware-beanFactoryAware/README.md @@ -1,494 +1,494 @@ -## BeanFactoryAware - -- [BeanFactoryAware](#beanfactoryaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`BeanFactoryAware` 接口,允许 Spring bean 获得其所在的 `BeanFactory` 的引用。当一个 bean 实现了这个接口,Spring 容器在初始化该 bean 时,会自动调用 `setBeanFactory()` 方法,并传递一个 `BeanFactory` 实例。 - -### 二、接口源码 - -`BeanFactoryAware` 是 Spring 框架自 11.03.2003 开始引入的一个核心接口。允许 Spring beans 获知并与其所在的 `BeanFactory` 进行交互。这为 beans 提供了直接访问 `BeanFactory` 的能力,进而可以查询和交互其他的 beans。 - -```java -/** - * 由希望知道其所属的 BeanFactory 的 beans 实现的接口。 - * - * 例如,beans 可以通过工厂查找合作的 beans(依赖查找)。 - * 请注意,大多数 beans 会选择通过相应的 bean 属性或构造函数参数 - * 接收对合作 beans 的引用(依赖注入)。 - * - * 有关所有 bean 生命周期方法的列表,请参阅 - * BeanFactory BeanFactory javadocs。 - * - * @author Rod Johnson - * @author Chris Beams - * @since 11.03.2003 - * @see BeanNameAware - * @see BeanClassLoaderAware - * @see InitializingBean - * @see org.springframework.context.ApplicationContextAware - */ -public interface BeanFactoryAware extends Aware { - - /** - * 向 bean 实例提供其拥有的工厂的回调。 - * 在正常 bean 属性填充之后但在初始化回调之前(如 - * InitializingBean#afterPropertiesSet() 或自定义的初始化方法)调用。 - * @param beanFactory 拥有的 BeanFactory(永远不会是 null)。 - * bean 可以立即调用工厂上的方法。 - * @throws BeansException 初始化错误的情况下 - * @see BeanInitializationException - */ - void setBeanFactory(BeanFactory beanFactory) throws BeansException; - -} -``` - -### 三、主要功能 - -**获取 `BeanFactory` 引用**: 通过实现 `BeanFactoryAware` 接口并重写 `setBeanFactory` 方法,bean 在初始化过程中会收到其所属的 `BeanFactory` 的引用。Spring 容器会自动为实现了该接口的 bean 调用 `setBeanFactory` 方法。 - -**依赖查找**: 一旦 bean 有了 `BeanFactory` 的引用,它就可以使用这个工厂来动态地查找其他 beans。这种方式被称为“依赖查找”(Dependency Lookup),与常见的“依赖注入”(Dependency Injection)方式相对。 - -**与 `BeanFactory` 进行交互**: 获取 `BeanFactory` 的引用不仅仅是为了查找其他 beans,bean 还可以与其所在的 `BeanFactory` 进行更广泛的互动,例如检查 bean 的作用域、检查 bean 是否为单例、或获取 bean 的别名等。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`UserService`类型的bean,最后调用`validateUser`方法。 - -```java -public class BeanNameAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - UserService userService = context.getBean(UserService.class); - userService.validateUser("root", "123456"); - } -} -``` - -使用`@ComponentScan`注解,告诉 Spring 容器去 "`com.xcs.spring.validate`" "`com.xcs.spring.service`"扫描包及其子包 - -```java -@Configuration -@ComponentScan({"com.xcs.spring.validate", "com.xcs.spring.service"}) -public class MyConfiguration { - -} -``` - -**UserValidator**是一个简单的验证器接口,具有一个方法 `validate`,用于验证用户名和密码是否有效。SimpleUserValidator是一个实现。它进行简单的验证,仅检查用户名和密码是否为非空。`ComplexUserValidator` 是 `UserValidator` 接口的另一个实现。这个验证器有点复杂,除了检查用户名和密码是否为空外,还检查用户名的长度是否大于 5 以及密码的长度是否大于 8。 - -```java -public interface UserValidator { - boolean validate(String username, String password); -} - -@Component("simpleUserValidator") -public class SimpleUserValidator implements UserValidator { - @Override - public boolean validate(String username, String password) { - System.out.println("使用SimpleUserValidator"); - return username != null && password != null; - } -} - -@Component("complexUserValidator") -public class ComplexUserValidator implements UserValidator { - @Override - public boolean validate(String username, String password) { - System.out.println("使用ComplexUserValidator"); - return username != null && username.length() > 5 && password != null && password.length() > 8; - } -} -``` - -`UserService` 类利用了 Spring 的 `BeanFactoryAware` 和 `InitializingBean` 接口,动态地选择了一个验证器。这种设计提供了极大的灵活性,允许 `UserService` 根据不同的配置或条件使用不同的验证方法。这也意味着在未来,如果需要添加更多的验证方法,只需简单地添加新的验证器实现,然后在 `someConfigurationMethod()` 中进行相应的调整。 - -```java -@Service -public class UserService implements BeanFactoryAware, InitializingBean { - - private BeanFactory beanFactory; - private UserValidator userValidator; - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - @Override - public void afterPropertiesSet() throws Exception { - if (someConfigurationMethod()) { - userValidator = beanFactory.getBean("simpleUserValidator", UserValidator.class); - } else { - userValidator = beanFactory.getBean("complexUserValidator", UserValidator.class); - } - } - - public void validateUser(String username, String password) { - boolean success = userValidator.validate(username, password); - if (success){ - System.out.println("验证账号密码成功"); - }else{ - System.out.println("验证账号密码失败"); - } - } - - private boolean someConfigurationMethod() { - return true; - } -} -``` - -运行结果发现,使用了`SimpleUserValidator`来验证账号密码,并且验证成功。 - -```java -使用SimpleUserValidator -验证账号密码成功 -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: BeanFactoryAware时序图 - participant BeanFactoryAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant UserService - - BeanFactoryAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 - AbstractAutowireCapableBeanFactory->>UserService:setBeanFactory(beanFactory)
设置beanFactory - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>BeanFactoryAwareApplication:初始化完成 - -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`UserService`类型的bean,最后调用`validateUser`方法。 - -```java -public class BeanNameAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - UserService userService = context.getBean(UserService.class); - userService.validateUser("root", "123456"); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - invokeAwareMethods(beanName, bean); - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 - -```java -private void invokeAwareMethods(String beanName, Object bean) { - if (bean instanceof Aware) { - if (bean instanceof BeanNameAware) { - // ... [代码部分省略以简化] - } - if (bean instanceof BeanClassLoaderAware) { - // ... [代码部分省略以简化] - } - if (bean instanceof BeanFactoryAware) { - ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); - } - } -} -``` - -最后执行到我们自定义的逻辑中,容器将调用 `setBeanFactory()` 方法,并将当前的 bean factory 实例作为参数传递。 - -```java -@Service -public class UserService implements BeanFactoryAware, InitializingBean { - - private BeanFactory beanFactory; - private UserValidator userValidator; - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - @Override - public void afterPropertiesSet() throws Exception { - // ... [代码部分省略以简化] - } - - public void validateUser(String username, String password) { - // ... [代码部分省略以简化] - } - - private boolean someConfigurationMethod() { - return true; - } -} -``` - -### 七、注意事项 - -**生命周期时机**:`setBeanFactory` 方法是在 bean 属性设置之后但在其他初始化方法(如 `@PostConstruct`、`InitializingBean#afterPropertiesSet` 或指定的初始化方法)之前调用的。 - -**避免循环依赖**:当 beans 通过 `BeanFactory` 查找其他 beans 时,可能会出现循环依赖的情况。例如,bean A 在其 `setBeanFactory` 方法中查找 bean B,而 bean B 在其 `setBeanFactory` 方法中查找 bean A。这种情况会导致容器初始化失败。 - -**知道 bean 的作用域**:当从 `BeanFactory` 获取 beans 时,请记住 bean 的作用域。如果 bean 是原型作用域的,每次 `getBean` 调用都会返回一个新的实例。 - -**不要过度自定义**:除非有很好的理由,否则应避免在 `setBeanFactory` 方法中执行大量的自定义逻辑。这会使 bean 的初始化过程变得复杂,并可能导致不可预见的副作用。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**构建与配置**:在 `BeanNameAwareApplication` 启动类中,使用了 `AnnotationConfigApplicationContext` 来基于 Java 配置类 (`MyConfiguration`) 初始化 Spring 上下文。这是一个 Java-based 的配置方法,与传统的 XML-based 配置相比,更加直观和灵活。 - -**组件扫描**:`MyConfiguration` 配置类使用 `@ComponentScan` 注解指定了需要被扫描的包路径。Spring 容器会自动扫描这些包以及其子包下的组件,并将它们注册为 Spring beans。 - -**验证器设计**:我们设计了一个 `UserValidator` 接口,以及两个实现该接口的类:`SimpleUserValidator` 和 `ComplexUserValidator`。这两个验证器具有不同的验证逻辑,以满足不同的验证需求。 - -**动态选择验证器**:`UserService` 类是此应用的核心,它根据某些配置动态地从 `BeanFactory` 中选择一个验证器。这是通过实现 `BeanFactoryAware` 和 `InitializingBean` 接口来完成的:`BeanFactoryAware` 允许 `UserService` 访问 Spring 容器的 `BeanFactory`。InitializingBean` 确保在所有属性(例如依赖注入)设置完毕后,选择合适的验证器。 - -**运行与输出**:当调用 `validateUser` 方法验证用户名和密码时,根据所选择的验证器(在此示例中是 `SimpleUserValidator`),将输出相应的验证信息。此外,验证器本身也输出了它正在使用的验证方法。 - -#### 8.2、源码分析总结 - -**应用启动与上下文初始化**: 当启动类 `BeanNameAwareApplication` 被执行,一个新的 `AnnotationConfigApplicationContext` 被创建并初始化,其中传入了配置类 `MyConfiguration`。 - -**配置类与组件扫描**: `MyConfiguration` 是一个 Java 配置类,它告诉 Spring 容器去扫描特定的包以查找组件。 - -**单例bean的预实例化**: 在上下文的 `refresh()` 方法中,Spring 会预先实例化所有非懒加载的单例bean。这意味着在容器启动时,这些bean会被初始化。 - -**Bean的实例化和初始化**: 在上下文刷新的过程中,Spring 容器会逐个创建并初始化所有的单例bean。`doCreateBean` 方法负责实例化bean、注入依赖、并调用任何初始化方法。 - -**处理 Aware 接口**: 对于实现了 `Aware` 接口的bean,如 `BeanFactoryAware`,在初始化过程中,Spring 容器会调用相应的 `Aware` 方法(例如,`setBeanFactory`)。这使得bean可以获得关于其运行环境的信息或其他 Spring 功能。 - -**自定义逻辑执行**: 一旦bean被初始化,并且所有的 `Aware` 方法都被调用,就可以执行自定义逻辑。在这个例子中,这是通过 `UserService` 的 `validateUser` 方法来完成的。 - +## BeanFactoryAware + +- [BeanFactoryAware](#beanfactoryaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`BeanFactoryAware` 接口,允许 Spring bean 获得其所在的 `BeanFactory` 的引用。当一个 bean 实现了这个接口,Spring 容器在初始化该 bean 时,会自动调用 `setBeanFactory()` 方法,并传递一个 `BeanFactory` 实例。 + +### 二、接口源码 + +`BeanFactoryAware` 是 Spring 框架自 11.03.2003 开始引入的一个核心接口。允许 Spring beans 获知并与其所在的 `BeanFactory` 进行交互。这为 beans 提供了直接访问 `BeanFactory` 的能力,进而可以查询和交互其他的 beans。 + +```java +/** + * 由希望知道其所属的 BeanFactory 的 beans 实现的接口。 + * + * 例如,beans 可以通过工厂查找合作的 beans(依赖查找)。 + * 请注意,大多数 beans 会选择通过相应的 bean 属性或构造函数参数 + * 接收对合作 beans 的引用(依赖注入)。 + * + * 有关所有 bean 生命周期方法的列表,请参阅 + * BeanFactory BeanFactory javadocs。 + * + * @author Rod Johnson + * @author Chris Beams + * @since 11.03.2003 + * @see BeanNameAware + * @see BeanClassLoaderAware + * @see InitializingBean + * @see org.springframework.context.ApplicationContextAware + */ +public interface BeanFactoryAware extends Aware { + + /** + * 向 bean 实例提供其拥有的工厂的回调。 + * 在正常 bean 属性填充之后但在初始化回调之前(如 + * InitializingBean#afterPropertiesSet() 或自定义的初始化方法)调用。 + * @param beanFactory 拥有的 BeanFactory(永远不会是 null)。 + * bean 可以立即调用工厂上的方法。 + * @throws BeansException 初始化错误的情况下 + * @see BeanInitializationException + */ + void setBeanFactory(BeanFactory beanFactory) throws BeansException; + +} +``` + +### 三、主要功能 + +**获取 `BeanFactory` 引用**: 通过实现 `BeanFactoryAware` 接口并重写 `setBeanFactory` 方法,bean 在初始化过程中会收到其所属的 `BeanFactory` 的引用。Spring 容器会自动为实现了该接口的 bean 调用 `setBeanFactory` 方法。 + +**依赖查找**: 一旦 bean 有了 `BeanFactory` 的引用,它就可以使用这个工厂来动态地查找其他 beans。这种方式被称为“依赖查找”(Dependency Lookup),与常见的“依赖注入”(Dependency Injection)方式相对。 + +**与 `BeanFactory` 进行交互**: 获取 `BeanFactory` 的引用不仅仅是为了查找其他 beans,bean 还可以与其所在的 `BeanFactory` 进行更广泛的互动,例如检查 bean 的作用域、检查 bean 是否为单例、或获取 bean 的别名等。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`UserService`类型的bean,最后调用`validateUser`方法。 + +```java +public class BeanNameAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + UserService userService = context.getBean(UserService.class); + userService.validateUser("root", "123456"); + } +} +``` + +使用`@ComponentScan`注解,告诉 Spring 容器去 "`com.xcs.spring.validate`" "`com.xcs.spring.service`"扫描包及其子包 + +```java +@Configuration +@ComponentScan({"com.xcs.spring.validate", "com.xcs.spring.service"}) +public class MyConfiguration { + +} +``` + +**UserValidator**是一个简单的验证器接口,具有一个方法 `validate`,用于验证用户名和密码是否有效。SimpleUserValidator是一个实现。它进行简单的验证,仅检查用户名和密码是否为非空。`ComplexUserValidator` 是 `UserValidator` 接口的另一个实现。这个验证器有点复杂,除了检查用户名和密码是否为空外,还检查用户名的长度是否大于 5 以及密码的长度是否大于 8。 + +```java +public interface UserValidator { + boolean validate(String username, String password); +} + +@Component("simpleUserValidator") +public class SimpleUserValidator implements UserValidator { + @Override + public boolean validate(String username, String password) { + System.out.println("使用SimpleUserValidator"); + return username != null && password != null; + } +} + +@Component("complexUserValidator") +public class ComplexUserValidator implements UserValidator { + @Override + public boolean validate(String username, String password) { + System.out.println("使用ComplexUserValidator"); + return username != null && username.length() > 5 && password != null && password.length() > 8; + } +} +``` + +`UserService` 类利用了 Spring 的 `BeanFactoryAware` 和 `InitializingBean` 接口,动态地选择了一个验证器。这种设计提供了极大的灵活性,允许 `UserService` 根据不同的配置或条件使用不同的验证方法。这也意味着在未来,如果需要添加更多的验证方法,只需简单地添加新的验证器实现,然后在 `someConfigurationMethod()` 中进行相应的调整。 + +```java +@Service +public class UserService implements BeanFactoryAware, InitializingBean { + + private BeanFactory beanFactory; + private UserValidator userValidator; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + @Override + public void afterPropertiesSet() throws Exception { + if (someConfigurationMethod()) { + userValidator = beanFactory.getBean("simpleUserValidator", UserValidator.class); + } else { + userValidator = beanFactory.getBean("complexUserValidator", UserValidator.class); + } + } + + public void validateUser(String username, String password) { + boolean success = userValidator.validate(username, password); + if (success){ + System.out.println("验证账号密码成功"); + }else{ + System.out.println("验证账号密码失败"); + } + } + + private boolean someConfigurationMethod() { + return true; + } +} +``` + +运行结果发现,使用了`SimpleUserValidator`来验证账号密码,并且验证成功。 + +```java +使用SimpleUserValidator +验证账号密码成功 +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: BeanFactoryAware时序图 + participant BeanFactoryAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant UserService + + BeanFactoryAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 + AbstractAutowireCapableBeanFactory->>UserService:setBeanFactory(beanFactory)
设置beanFactory + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>BeanFactoryAwareApplication:初始化完成 + +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`UserService`类型的bean,最后调用`validateUser`方法。 + +```java +public class BeanNameAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + UserService userService = context.getBean(UserService.class); + userService.validateUser("root", "123456"); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + invokeAwareMethods(beanName, bean); + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 + +```java +private void invokeAwareMethods(String beanName, Object bean) { + if (bean instanceof Aware) { + if (bean instanceof BeanNameAware) { + // ... [代码部分省略以简化] + } + if (bean instanceof BeanClassLoaderAware) { + // ... [代码部分省略以简化] + } + if (bean instanceof BeanFactoryAware) { + ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); + } + } +} +``` + +最后执行到我们自定义的逻辑中,容器将调用 `setBeanFactory()` 方法,并将当前的 bean factory 实例作为参数传递。 + +```java +@Service +public class UserService implements BeanFactoryAware, InitializingBean { + + private BeanFactory beanFactory; + private UserValidator userValidator; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + @Override + public void afterPropertiesSet() throws Exception { + // ... [代码部分省略以简化] + } + + public void validateUser(String username, String password) { + // ... [代码部分省略以简化] + } + + private boolean someConfigurationMethod() { + return true; + } +} +``` + +### 七、注意事项 + +**生命周期时机**:`setBeanFactory` 方法是在 bean 属性设置之后但在其他初始化方法(如 `@PostConstruct`、`InitializingBean#afterPropertiesSet` 或指定的初始化方法)之前调用的。 + +**避免循环依赖**:当 beans 通过 `BeanFactory` 查找其他 beans 时,可能会出现循环依赖的情况。例如,bean A 在其 `setBeanFactory` 方法中查找 bean B,而 bean B 在其 `setBeanFactory` 方法中查找 bean A。这种情况会导致容器初始化失败。 + +**知道 bean 的作用域**:当从 `BeanFactory` 获取 beans 时,请记住 bean 的作用域。如果 bean 是原型作用域的,每次 `getBean` 调用都会返回一个新的实例。 + +**不要过度自定义**:除非有很好的理由,否则应避免在 `setBeanFactory` 方法中执行大量的自定义逻辑。这会使 bean 的初始化过程变得复杂,并可能导致不可预见的副作用。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**构建与配置**:在 `BeanNameAwareApplication` 启动类中,使用了 `AnnotationConfigApplicationContext` 来基于 Java 配置类 (`MyConfiguration`) 初始化 Spring 上下文。这是一个 Java-based 的配置方法,与传统的 XML-based 配置相比,更加直观和灵活。 + +**组件扫描**:`MyConfiguration` 配置类使用 `@ComponentScan` 注解指定了需要被扫描的包路径。Spring 容器会自动扫描这些包以及其子包下的组件,并将它们注册为 Spring beans。 + +**验证器设计**:我们设计了一个 `UserValidator` 接口,以及两个实现该接口的类:`SimpleUserValidator` 和 `ComplexUserValidator`。这两个验证器具有不同的验证逻辑,以满足不同的验证需求。 + +**动态选择验证器**:`UserService` 类是此应用的核心,它根据某些配置动态地从 `BeanFactory` 中选择一个验证器。这是通过实现 `BeanFactoryAware` 和 `InitializingBean` 接口来完成的:`BeanFactoryAware` 允许 `UserService` 访问 Spring 容器的 `BeanFactory`。InitializingBean` 确保在所有属性(例如依赖注入)设置完毕后,选择合适的验证器。 + +**运行与输出**:当调用 `validateUser` 方法验证用户名和密码时,根据所选择的验证器(在此示例中是 `SimpleUserValidator`),将输出相应的验证信息。此外,验证器本身也输出了它正在使用的验证方法。 + +#### 8.2、源码分析总结 + +**应用启动与上下文初始化**: 当启动类 `BeanNameAwareApplication` 被执行,一个新的 `AnnotationConfigApplicationContext` 被创建并初始化,其中传入了配置类 `MyConfiguration`。 + +**配置类与组件扫描**: `MyConfiguration` 是一个 Java 配置类,它告诉 Spring 容器去扫描特定的包以查找组件。 + +**单例bean的预实例化**: 在上下文的 `refresh()` 方法中,Spring 会预先实例化所有非懒加载的单例bean。这意味着在容器启动时,这些bean会被初始化。 + +**Bean的实例化和初始化**: 在上下文刷新的过程中,Spring 容器会逐个创建并初始化所有的单例bean。`doCreateBean` 方法负责实例化bean、注入依赖、并调用任何初始化方法。 + +**处理 Aware 接口**: 对于实现了 `Aware` 接口的bean,如 `BeanFactoryAware`,在初始化过程中,Spring 容器会调用相应的 `Aware` 方法(例如,`setBeanFactory`)。这使得bean可以获得关于其运行环境的信息或其他 Spring 功能。 + +**自定义逻辑执行**: 一旦bean被初始化,并且所有的 `Aware` 方法都被调用,就可以执行自定义逻辑。在这个例子中,这是通过 `UserService` 的 `validateUser` 方法来完成的。 + **BeanFactoryAware 的特性**: 通过实现 `BeanFactoryAware`,`UserService` 能够获得对 `BeanFactory` 的访问权限。这使得它可以在运行时动态地从 `BeanFactory` 中获取bean,如在示例中的 `UserValidator`。 \ No newline at end of file diff --git a/spring-aware/spring-aware-beanFactoryAware/pom.xml b/spring-aware/spring-aware-beanFactoryAware/pom.xml new file mode 100644 index 0000000..767f259 --- /dev/null +++ b/spring-aware/spring-aware-beanFactoryAware/pom.xml @@ -0,0 +1,14 @@ + + + + spring-aware + com.xcs.spring + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aware-beanFactoryAware + + \ No newline at end of file diff --git a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/BeanFactoryAwareApplication.java b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/BeanFactoryAwareApplication.java similarity index 100% rename from spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/BeanFactoryAwareApplication.java rename to spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/BeanFactoryAwareApplication.java diff --git a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/service/UserService.java b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/service/UserService.java similarity index 97% rename from spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/service/UserService.java rename to spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/service/UserService.java index 2aa2979..734ddd2 100644 --- a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/service/UserService.java +++ b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/service/UserService.java @@ -1,42 +1,42 @@ -package com.xcs.spring.service; - -import com.xcs.spring.validate.UserValidator; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.stereotype.Service; - -@Service -public class UserService implements BeanFactoryAware, InitializingBean { - - private BeanFactory beanFactory; - private UserValidator userValidator; - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - @Override - public void afterPropertiesSet() throws Exception { - if (someConfigurationMethod()) { - userValidator = beanFactory.getBean("simpleUserValidator", UserValidator.class); - } else { - userValidator = beanFactory.getBean("complexUserValidator", UserValidator.class); - } - } - - public void validateUser(String username, String password) { - boolean success = userValidator.validate(username, password); - if (success){ - System.out.println("验证账号密码成功"); - }else{ - System.out.println("验证账号密码失败"); - } - } - - private boolean someConfigurationMethod() { - return true; - } -} +package com.xcs.spring.service; + +import com.xcs.spring.validate.UserValidator; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Service; + +@Service +public class UserService implements BeanFactoryAware, InitializingBean { + + private BeanFactory beanFactory; + private UserValidator userValidator; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + @Override + public void afterPropertiesSet() throws Exception { + if (someConfigurationMethod()) { + userValidator = beanFactory.getBean("simpleUserValidator", UserValidator.class); + } else { + userValidator = beanFactory.getBean("complexUserValidator", UserValidator.class); + } + } + + public void validateUser(String username, String password) { + boolean success = userValidator.validate(username, password); + if (success){ + System.out.println("验证账号密码成功"); + }else{ + System.out.println("验证账号密码失败"); + } + } + + private boolean someConfigurationMethod() { + return true; + } +} diff --git a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/ComplexUserValidator.java b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/ComplexUserValidator.java similarity index 97% rename from spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/ComplexUserValidator.java rename to spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/ComplexUserValidator.java index 4530188..eb9c3a4 100644 --- a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/ComplexUserValidator.java +++ b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/ComplexUserValidator.java @@ -1,12 +1,12 @@ -package com.xcs.spring.validate; - -import org.springframework.stereotype.Component; - -@Component("complexUserValidator") -public class ComplexUserValidator implements UserValidator { - @Override - public boolean validate(String username, String password) { - System.out.println("使用ComplexUserValidator"); - return username != null && username.length() > 5 && password != null && password.length() > 8; - } -} +package com.xcs.spring.validate; + +import org.springframework.stereotype.Component; + +@Component("complexUserValidator") +public class ComplexUserValidator implements UserValidator { + @Override + public boolean validate(String username, String password) { + System.out.println("使用ComplexUserValidator"); + return username != null && username.length() > 5 && password != null && password.length() > 8; + } +} diff --git a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/SimpleUserValidator.java b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/SimpleUserValidator.java similarity index 96% rename from spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/SimpleUserValidator.java rename to spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/SimpleUserValidator.java index e3290d3..b88ca58 100644 --- a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/SimpleUserValidator.java +++ b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/SimpleUserValidator.java @@ -1,12 +1,12 @@ -package com.xcs.spring.validate; - -import org.springframework.stereotype.Component; - -@Component("simpleUserValidator") -public class SimpleUserValidator implements UserValidator { - @Override - public boolean validate(String username, String password) { - System.out.println("使用SimpleUserValidator"); - return username != null && password != null; - } -} +package com.xcs.spring.validate; + +import org.springframework.stereotype.Component; + +@Component("simpleUserValidator") +public class SimpleUserValidator implements UserValidator { + @Override + public boolean validate(String username, String password) { + System.out.println("使用SimpleUserValidator"); + return username != null && password != null; + } +} diff --git a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/UserValidator.java b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/UserValidator.java similarity index 96% rename from spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/UserValidator.java rename to spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/UserValidator.java index 7bb8bc1..8c8f2ee 100644 --- a/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/UserValidator.java +++ b/spring-aware/spring-aware-beanFactoryAware/src/main/java/com/xcs/spring/validate/UserValidator.java @@ -1,5 +1,5 @@ -package com.xcs.spring.validate; - -public interface UserValidator { - boolean validate(String username, String password); -} +package com.xcs.spring.validate; + +public interface UserValidator { + boolean validate(String username, String password); +} diff --git a/spring-aware-beanNameAware/README.md b/spring-aware/spring-aware-beanNameAware/README.md similarity index 97% rename from spring-aware-beanNameAware/README.md rename to spring-aware/spring-aware-beanNameAware/README.md index 7c8d074..8c1659d 100644 --- a/spring-aware-beanNameAware/README.md +++ b/spring-aware/spring-aware-beanNameAware/README.md @@ -1,454 +1,454 @@ -## BeanNameAware - -- [BeanNameAware](#beannameaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`BeanNameAware` 接口。当一个 Bean 实现了此接口,可以感知其在 Spring 容器中的名称。 - -### 二、接口源码 - -`BeanNameAware` 是 Spring 框架自 01.11.2003 开始引入的一个核心接口。这个接口是为那些想要了解其在 bean 工厂中的名称的 beans 设计的。 - -```java -/** - * 由希望知道其在 bean 工厂中名称的 beans 实现的接口。 - * 注意通常不推荐一个对象依赖于其 bean 名称,因为这可能导致对外部配置的脆弱依赖, - * 以及可能的不必要的对 Spring API 的依赖。 - * - * 有关所有 bean 生命周期方法的列表,请参见 - * BeanFactory BeanFactory javadocs。 - * - * @author Juergen Hoeller - * @author Chris Beams - * @since 01.11.2003 - * @see BeanClassLoaderAware - * @see BeanFactoryAware - * @see InitializingBean - */ -public interface BeanNameAware extends Aware { - - /** - * 设置在创建此 bean 的 bean 工厂中的 bean 的名称。 - * 此方法在填充常规 bean 属性之后被调用,但在如 InitializingBean#afterPropertiesSet() 这样的 - * 初始化回调或自定义初始化方法之前被调用。 - * @param name 工厂中的 bean 的名称。注意,这个名称是工厂中使用的实际 bean 名称, - * 这可能与最初指定的名称不同:尤其对于内部 bean 名称,实际的 bean 名称可能已通过添加 "#..." 后缀变得唯一。 - * 如果需要,可以使用 BeanFactoryUtils#originalBeanName(String) 方法来提取没有后缀的原始 bean 名称。 - */ - void setBeanName(String name); - -} -``` - -### 三、主要功能 - -**提供 `setBeanName` 方法**:当一个 Bean 实现了 `BeanNameAware` 接口,它需要提供 `setBeanName` 方法的实现。这个方法有一个参数,即该 Bean 在 Spring 容器中的名称。 - -**自动回调**:当 Spring 容器创建并配置一个实现了 `BeanNameAware` 接口的 Bean 时,容器会自动回调 `setBeanName` 方法,并传入该 Bean 在容器中的名称。这意味着开发者不需要显式地调用这个方法;Spring 容器会自动处理。 - -**获取 Bean 的名称**:有时,Bean 可能需要知道其在容器中的名称以执行特定的逻辑或功能,或者为了日志记录或其他目的。通过实现 `BeanNameAware`,Bean 可以轻松获得此信息。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 - -```java -public class BeanNameAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - } -} -``` - -使用`@ComponentScan`注解,告诉 Spring 容器去 "`com.xcs.spring.service`" 扫描包及其子包 - -```java -@Configuration -@ComponentScan("com.xcs.spring.service") -public class MyConfiguration { - -} -``` - -`MyBasePayService` 是一个抽象类,结合了 Spring 的三个特殊接口:`BeanNameAware`(让 Bean 知道其名字)、`InitializingBean`(Bean 属性设置后的初始化操作)和 `DisposableBean`(Bean 销毁前的操作)。 - -```java -public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { - - private String beanName; - - @Override - public void setBeanName(String beanName) { - this.beanName = beanName; - } - - @Override - public void afterPropertiesSet() throws Exception { - System.out.println("Module " + beanName + " has been registered."); - } - - @Override - public void destroy() throws Exception { - System.out.println("Module " + beanName + " has been unregistered."); - } -} -``` - -`MyAliPayService` 和 `MyWeChatPayService` 是两个支付服务类,都继承自 `MyBasePayService`,因此它们会自动获得与 Spring 容器生命周期相关的基本功能。这种设计方式为多个支付服务提供了一个共同的生命周期管理模式,同时允许每个服务添加其特定的支付逻辑。 - -```java -@Service -public class MyAliPayService extends MyBasePayService{ - -} - -@Service -public class MyWeChatPayService extends MyBasePayService{ - -} -``` - -运行结果发现,当 Spring 容器启动并初始化 Beans 时,它正确地识别并实例化了 `MyAliPayService` 和 `MyWeChatPayService` 这两个服务。由于这两个服务都继承自 `MyBasePayService`,在 Bean 的属性被设置之后(即在 `afterPropertiesSet()` 方法中),它们分别打印出了 "Module myAliPayService has been registered." 和 "Module myWeChatPayService has been registered." 这两条信息,提供了一个共同的生命周期管理模式。 - -```java -Module myAliPayService has been registered. -Module myWeChatPayService has been registered. -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: BeanNameAware时序图 - participant BeanNameAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant MyBasePayService - - BeanNameAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 - AbstractAutowireCapableBeanFactory->>MyBasePayService:setBeanName(beanName)
设置Bean名称 - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>BeanNameAwareApplication:初始化完成 -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 - -```java -public class BeanNameAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - invokeAwareMethods(beanName, bean); - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 - -```java -private void invokeAwareMethods(String beanName, Object bean) { - if (bean instanceof Aware) { - if (bean instanceof BeanNameAware) { - ((BeanNameAware) bean).setBeanName(beanName); - } - if (bean instanceof BeanClassLoaderAware) { - // ... [代码部分省略以简化] - } - if (bean instanceof BeanFactoryAware) { - // ... [代码部分省略以简化] - } - } -} -``` - -最后执行到我们自定义的逻辑中,我们将这个名称存储在 `beanName` 变量中,以便后续使用。 - -```java -public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { - - private String beanName; - - @Override - public void setBeanName(String beanName) { - this.beanName = beanName; - } - - @Override - public void afterPropertiesSet() throws Exception { - System.out.println("Module " + beanName + " has been registered."); - } - - @Override - public void destroy() throws Exception { - System.out.println("Module " + beanName + " has been unregistered."); - } -} -``` - -### 七、注意事项 - -**与其他生命周期方法的顺序**:`setBeanName` 方法的调用是在其他许多生命周期方法之前的,例如 `InitializingBean#afterPropertiesSet` 和任何定义的初始化方法。因此,我们不应该在 `setBeanName` 方法内部预期其他配置或初始化逻辑已经完成。 - -**仅在容器管理的 Beans 中有效**:只有当 bean 是由 Spring 容器管理时,`BeanNameAware` 才会生效。简单地创建一个类的实例(例如通过 `new` 关键字)并不会触发 `BeanNameAware` 功能。 - -**与其他 Aware 接口的组合使用**:当一个 bean 同时实现多个 `Aware` 接口时,需要注意它们的调用顺序。例如,`BeanNameAware`、`BeanFactoryAware` 和 `ApplicationContextAware` 的回调方法调用顺序是固定的。 - -**Bean 名称的唯一性**:Spring 容器内的 bean 名称是唯一的,但如果使用别名,同一个 bean 可能会有多个名称。当实现 `BeanNameAware` 时,我们获得的是 bean 的主要名称。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**启动及配置**:我们使用了 `AnnotationConfigApplicationContext` 作为 Spring 容器的入口,专门为基于 Java 的配置设计。该容器被初始化并加载了 `MyConfiguration` 类,它定义了应用的主要配置。 - -**组件扫描**:通过在 `MyConfiguration` 类中使用 `@ComponentScan` 注解,我们告诉 Spring 容器去扫描 "`com.xcs.spring.service`" 包及其子包,以找到和管理 Beans。 - -**生命周期管理**:**MyBasePayService** 类展示了如何利用 Spring 的特殊接口,例如 `BeanNameAware`、`InitializingBean` 和 `DisposableBean`,来插入到 Bean 的生命周期的特定阶段。当一个 Bean 实例被创建并管理 by Spring, 它会被赋予一个名称(通过 `BeanNameAware`)、在所有属性设置后初始化(通过 `InitializingBean`)以及在应用结束或 Bean 被销毁时执行特定操作(通过 `DisposableBean`)。 - -**具体的服务实现**:有两个具体的支付服务,`MyAliPayService` 和 `MyWeChatPayService`,它们都继承了 `MyBasePayService`。这意味着它们都自动继承了上述的生命周期管理功能。当 Spring 容器启动时,这两个服务的相关生命周期方法会被调用,如我们从打印的消息中所看到的。 - -**实际效果**:当应用运行时,每个服务类都会打印出其已经被注册和注销的消息,这是由于它们都继承了 `MyBasePayService` 中定义的生命周期方法。 - -#### 8.2、源码分析总结 - -**启动和上下文初始化**:使用`AnnotationConfigApplicationContext`初始化Spring容器,其中传递了配置类`MyConfiguration`。 - -**注册和刷新上下文**:在`AnnotationConfigApplicationContext`构造函数中,`register()`方法注册配置类,而`refresh()`方法开始加载和初始化beans。 - -**开始bean的实例化**:`refresh()`方法进一步调用了`finishBeanFactoryInitialization(beanFactory)`,该方法负责预先实例化所有非懒加载的单例bean。 - -**实例化单例bean**:`preInstantiateSingletons()`方法遍历所有bean名称,并通过调用`getBean(beanName)`来实例化和初始化bean。 - -**创建bean实例**:`doGetBean()`是实际进行bean创建的核心方法,它处理了bean的实例化、依赖注入和初始化等逻辑。 - -**处理Aware接口族**:在bean的初始化过程中,`invokeAwareMethods(beanName, bean)`被调用,负责处理实现了`Aware`接口族的beans。这是我们的`BeanNameAware`接口发挥作用的地方,当bean实现此接口时,其`setBeanName`方法会被调用。 - +## BeanNameAware + +- [BeanNameAware](#beannameaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`BeanNameAware` 接口。当一个 Bean 实现了此接口,可以感知其在 Spring 容器中的名称。 + +### 二、接口源码 + +`BeanNameAware` 是 Spring 框架自 01.11.2003 开始引入的一个核心接口。这个接口是为那些想要了解其在 bean 工厂中的名称的 beans 设计的。 + +```java +/** + * 由希望知道其在 bean 工厂中名称的 beans 实现的接口。 + * 注意通常不推荐一个对象依赖于其 bean 名称,因为这可能导致对外部配置的脆弱依赖, + * 以及可能的不必要的对 Spring API 的依赖。 + * + * 有关所有 bean 生命周期方法的列表,请参见 + * BeanFactory BeanFactory javadocs。 + * + * @author Juergen Hoeller + * @author Chris Beams + * @since 01.11.2003 + * @see BeanClassLoaderAware + * @see BeanFactoryAware + * @see InitializingBean + */ +public interface BeanNameAware extends Aware { + + /** + * 设置在创建此 bean 的 bean 工厂中的 bean 的名称。 + * 此方法在填充常规 bean 属性之后被调用,但在如 InitializingBean#afterPropertiesSet() 这样的 + * 初始化回调或自定义初始化方法之前被调用。 + * @param name 工厂中的 bean 的名称。注意,这个名称是工厂中使用的实际 bean 名称, + * 这可能与最初指定的名称不同:尤其对于内部 bean 名称,实际的 bean 名称可能已通过添加 "#..." 后缀变得唯一。 + * 如果需要,可以使用 BeanFactoryUtils#originalBeanName(String) 方法来提取没有后缀的原始 bean 名称。 + */ + void setBeanName(String name); + +} +``` + +### 三、主要功能 + +**提供 `setBeanName` 方法**:当一个 Bean 实现了 `BeanNameAware` 接口,它需要提供 `setBeanName` 方法的实现。这个方法有一个参数,即该 Bean 在 Spring 容器中的名称。 + +**自动回调**:当 Spring 容器创建并配置一个实现了 `BeanNameAware` 接口的 Bean 时,容器会自动回调 `setBeanName` 方法,并传入该 Bean 在容器中的名称。这意味着开发者不需要显式地调用这个方法;Spring 容器会自动处理。 + +**获取 Bean 的名称**:有时,Bean 可能需要知道其在容器中的名称以执行特定的逻辑或功能,或者为了日志记录或其他目的。通过实现 `BeanNameAware`,Bean 可以轻松获得此信息。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 + +```java +public class BeanNameAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + } +} +``` + +使用`@ComponentScan`注解,告诉 Spring 容器去 "`com.xcs.spring.service`" 扫描包及其子包 + +```java +@Configuration +@ComponentScan("com.xcs.spring.service") +public class MyConfiguration { + +} +``` + +`MyBasePayService` 是一个抽象类,结合了 Spring 的三个特殊接口:`BeanNameAware`(让 Bean 知道其名字)、`InitializingBean`(Bean 属性设置后的初始化操作)和 `DisposableBean`(Bean 销毁前的操作)。 + +```java +public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { + + private String beanName; + + @Override + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + @Override + public void afterPropertiesSet() throws Exception { + System.out.println("Module " + beanName + " has been registered."); + } + + @Override + public void destroy() throws Exception { + System.out.println("Module " + beanName + " has been unregistered."); + } +} +``` + +`MyAliPayService` 和 `MyWeChatPayService` 是两个支付服务类,都继承自 `MyBasePayService`,因此它们会自动获得与 Spring 容器生命周期相关的基本功能。这种设计方式为多个支付服务提供了一个共同的生命周期管理模式,同时允许每个服务添加其特定的支付逻辑。 + +```java +@Service +public class MyAliPayService extends MyBasePayService{ + +} + +@Service +public class MyWeChatPayService extends MyBasePayService{ + +} +``` + +运行结果发现,当 Spring 容器启动并初始化 Beans 时,它正确地识别并实例化了 `MyAliPayService` 和 `MyWeChatPayService` 这两个服务。由于这两个服务都继承自 `MyBasePayService`,在 Bean 的属性被设置之后(即在 `afterPropertiesSet()` 方法中),它们分别打印出了 "Module myAliPayService has been registered." 和 "Module myWeChatPayService has been registered." 这两条信息,提供了一个共同的生命周期管理模式。 + +```java +Module myAliPayService has been registered. +Module myWeChatPayService has been registered. +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: BeanNameAware时序图 + participant BeanNameAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant MyBasePayService + + BeanNameAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:invokeAwareMethods(beanName, bean)
调用Aware方法 + AbstractAutowireCapableBeanFactory->>MyBasePayService:setBeanName(beanName)
设置Bean名称 + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>BeanNameAwareApplication:初始化完成 +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。 + +```java +public class BeanNameAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,`invokeAwareMethods(beanName, bean)`是一个非常重要的步骤。这个方法是为了处理实现了Spring的`Aware`接口族的Beans(例如`BeanNameAware`, `BeanFactoryAware`等)。如果提供的bean实现了任何这些接口,该方法会回调相应的`Aware`方法。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + invokeAwareMethods(beanName, bean); + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods`方法中,用于处理实现了 Spring `Aware` 接口族的 beans。当一个 bean 实现了如 `BeanNameAware`、`BeanClassLoaderAware` 或 `BeanFactoryAware` 等接口时,此方法确保正确的回调方法被调用,从而为 bean 提供关于其运行环境或其他相关信息。 + +```java +private void invokeAwareMethods(String beanName, Object bean) { + if (bean instanceof Aware) { + if (bean instanceof BeanNameAware) { + ((BeanNameAware) bean).setBeanName(beanName); + } + if (bean instanceof BeanClassLoaderAware) { + // ... [代码部分省略以简化] + } + if (bean instanceof BeanFactoryAware) { + // ... [代码部分省略以简化] + } + } +} +``` + +最后执行到我们自定义的逻辑中,我们将这个名称存储在 `beanName` 变量中,以便后续使用。 + +```java +public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { + + private String beanName; + + @Override + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + @Override + public void afterPropertiesSet() throws Exception { + System.out.println("Module " + beanName + " has been registered."); + } + + @Override + public void destroy() throws Exception { + System.out.println("Module " + beanName + " has been unregistered."); + } +} +``` + +### 七、注意事项 + +**与其他生命周期方法的顺序**:`setBeanName` 方法的调用是在其他许多生命周期方法之前的,例如 `InitializingBean#afterPropertiesSet` 和任何定义的初始化方法。因此,我们不应该在 `setBeanName` 方法内部预期其他配置或初始化逻辑已经完成。 + +**仅在容器管理的 Beans 中有效**:只有当 bean 是由 Spring 容器管理时,`BeanNameAware` 才会生效。简单地创建一个类的实例(例如通过 `new` 关键字)并不会触发 `BeanNameAware` 功能。 + +**与其他 Aware 接口的组合使用**:当一个 bean 同时实现多个 `Aware` 接口时,需要注意它们的调用顺序。例如,`BeanNameAware`、`BeanFactoryAware` 和 `ApplicationContextAware` 的回调方法调用顺序是固定的。 + +**Bean 名称的唯一性**:Spring 容器内的 bean 名称是唯一的,但如果使用别名,同一个 bean 可能会有多个名称。当实现 `BeanNameAware` 时,我们获得的是 bean 的主要名称。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**启动及配置**:我们使用了 `AnnotationConfigApplicationContext` 作为 Spring 容器的入口,专门为基于 Java 的配置设计。该容器被初始化并加载了 `MyConfiguration` 类,它定义了应用的主要配置。 + +**组件扫描**:通过在 `MyConfiguration` 类中使用 `@ComponentScan` 注解,我们告诉 Spring 容器去扫描 "`com.xcs.spring.service`" 包及其子包,以找到和管理 Beans。 + +**生命周期管理**:**MyBasePayService** 类展示了如何利用 Spring 的特殊接口,例如 `BeanNameAware`、`InitializingBean` 和 `DisposableBean`,来插入到 Bean 的生命周期的特定阶段。当一个 Bean 实例被创建并管理 by Spring, 它会被赋予一个名称(通过 `BeanNameAware`)、在所有属性设置后初始化(通过 `InitializingBean`)以及在应用结束或 Bean 被销毁时执行特定操作(通过 `DisposableBean`)。 + +**具体的服务实现**:有两个具体的支付服务,`MyAliPayService` 和 `MyWeChatPayService`,它们都继承了 `MyBasePayService`。这意味着它们都自动继承了上述的生命周期管理功能。当 Spring 容器启动时,这两个服务的相关生命周期方法会被调用,如我们从打印的消息中所看到的。 + +**实际效果**:当应用运行时,每个服务类都会打印出其已经被注册和注销的消息,这是由于它们都继承了 `MyBasePayService` 中定义的生命周期方法。 + +#### 8.2、源码分析总结 + +**启动和上下文初始化**:使用`AnnotationConfigApplicationContext`初始化Spring容器,其中传递了配置类`MyConfiguration`。 + +**注册和刷新上下文**:在`AnnotationConfigApplicationContext`构造函数中,`register()`方法注册配置类,而`refresh()`方法开始加载和初始化beans。 + +**开始bean的实例化**:`refresh()`方法进一步调用了`finishBeanFactoryInitialization(beanFactory)`,该方法负责预先实例化所有非懒加载的单例bean。 + +**实例化单例bean**:`preInstantiateSingletons()`方法遍历所有bean名称,并通过调用`getBean(beanName)`来实例化和初始化bean。 + +**创建bean实例**:`doGetBean()`是实际进行bean创建的核心方法,它处理了bean的实例化、依赖注入和初始化等逻辑。 + +**处理Aware接口族**:在bean的初始化过程中,`invokeAwareMethods(beanName, bean)`被调用,负责处理实现了`Aware`接口族的beans。这是我们的`BeanNameAware`接口发挥作用的地方,当bean实现此接口时,其`setBeanName`方法会被调用。 + **用户定义的逻辑**:在`MyBasePayService`类中,我们实现了`BeanNameAware`接口,并重写了`setBeanName`方法来保存bean的名称。此外,还使用了`InitializingBean`和`DisposableBean`接口来在bean的生命周期的特定时刻执行自定义的逻辑。 \ No newline at end of file diff --git a/spring-aware/spring-aware-beanNameAware/pom.xml b/spring-aware/spring-aware-beanNameAware/pom.xml new file mode 100644 index 0000000..dc22a28 --- /dev/null +++ b/spring-aware/spring-aware-beanNameAware/pom.xml @@ -0,0 +1,14 @@ + + + + spring-aware + com.xcs.spring + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aware-beanNameAware + + \ No newline at end of file diff --git a/spring-aware-beanNameAware/src/main/java/com/xcs/spring/BeanNameAwareApplication.java b/spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/BeanNameAwareApplication.java similarity index 100% rename from spring-aware-beanNameAware/src/main/java/com/xcs/spring/BeanNameAwareApplication.java rename to spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/BeanNameAwareApplication.java diff --git a/spring-aware-beanNameAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-beanNameAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyAliPayService.java b/spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyAliPayService.java similarity index 95% rename from spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyAliPayService.java rename to spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyAliPayService.java index 49159eb..0f86c3a 100644 --- a/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyAliPayService.java +++ b/spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyAliPayService.java @@ -1,7 +1,7 @@ -package com.xcs.spring.service; - -import org.springframework.stereotype.Service; - -@Service -public class MyAliPayService extends MyBasePayService{ -} +package com.xcs.spring.service; + +import org.springframework.stereotype.Service; + +@Service +public class MyAliPayService extends MyBasePayService{ +} diff --git a/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyBasePayService.java b/spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyBasePayService.java similarity index 96% rename from spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyBasePayService.java rename to spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyBasePayService.java index 9a272e5..d03fc8f 100644 --- a/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyBasePayService.java +++ b/spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyBasePayService.java @@ -1,25 +1,25 @@ -package com.xcs.spring.service; - -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; - -public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { - - private String beanName; - - @Override - public void setBeanName(String beanName) { - this.beanName = beanName; - } - - @Override - public void afterPropertiesSet() throws Exception { - System.out.println("Module " + beanName + " has been registered."); - } - - @Override - public void destroy() throws Exception { - System.out.println("Module " + beanName + " has been unregistered."); - } -} +package com.xcs.spring.service; + +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; + +public abstract class MyBasePayService implements BeanNameAware, InitializingBean, DisposableBean { + + private String beanName; + + @Override + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + @Override + public void afterPropertiesSet() throws Exception { + System.out.println("Module " + beanName + " has been registered."); + } + + @Override + public void destroy() throws Exception { + System.out.println("Module " + beanName + " has been unregistered."); + } +} diff --git a/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyWeChatPayService.java b/spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyWeChatPayService.java similarity index 95% rename from spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyWeChatPayService.java rename to spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyWeChatPayService.java index 82a2f0f..e0acea2 100644 --- a/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyWeChatPayService.java +++ b/spring-aware/spring-aware-beanNameAware/src/main/java/com/xcs/spring/service/MyWeChatPayService.java @@ -1,7 +1,7 @@ -package com.xcs.spring.service; - -import org.springframework.stereotype.Service; - -@Service -public class MyWeChatPayService extends MyBasePayService{ -} +package com.xcs.spring.service; + +import org.springframework.stereotype.Service; + +@Service +public class MyWeChatPayService extends MyBasePayService{ +} diff --git a/spring-aware-embeddedValueResolverAware/README.md b/spring-aware/spring-aware-embeddedValueResolverAware/README.md similarity index 97% rename from spring-aware-embeddedValueResolverAware/README.md rename to spring-aware/spring-aware-embeddedValueResolverAware/README.md index b8b2731..831fbf4 100644 --- a/spring-aware-embeddedValueResolverAware/README.md +++ b/spring-aware/spring-aware-embeddedValueResolverAware/README.md @@ -1,474 +1,474 @@ -## EmbeddedValueResolverAware - -- [EmbeddedValueResolverAware](#embeddedvalueresolveraware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`EmbeddedValueResolverAware` 接口,主要用于提供一个字符串值解析器,这可以在 Bean 属性中解析占位符和表达式。如果我们熟悉 Spring 的 `${...}` 占位符和 `#{...}` 表达式,那么这个接口将帮助我们在自定义组件中解析这些值。 - -### 二、接口源码 - -`EmbeddedValueResolverAware` 是 Spring 框架自 3.0.3 开始引入的一个核心接口。允许对象在初始化时得到一个 `StringValueResolver`,并使用它来解析嵌入的字符串值,如配置文件中的占位符或 SpEL 表达式。 - -```java -/** - * 如果对象希望被通知一个 StringValueResolver,以解析嵌入的定义值,那么它应实现此接口。 - * - * 这提供了一个通过 ApplicationContextAware/BeanFactoryAware 接口 - * 依赖于完整的 ConfigurableBeanFactory 的替代方法。 - * - * @author Juergen Hoeller - * @author Chris Beams - * @since 3.0.3 - * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#resolveEmbeddedValue(String) - * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanExpressionResolver() - * @see org.springframework.beans.factory.config.EmbeddedValueResolver - */ -public interface EmbeddedValueResolverAware extends Aware { - - /** - * 设置用于解析嵌入定义值的 StringValueResolver。 - */ - void setEmbeddedValueResolver(StringValueResolver resolver); - -} -``` - -### 三、主要功能 - -**解析嵌入的字符串值**: 当我们在 Bean 的属性或构造函数参数中有一个值,如 `${property.name}` 或 `#{some.expression}`,这需要被解析成实际的值时,`StringValueResolver` 可以帮助做这件事。 - -**避免对 `ConfigurableBeanFactory` 的直接依赖**: 通过使用 `EmbeddedValueResolverAware`,我们可以间接地得到这种解析功能,而不必直接依赖于整个 `ConfigurableBeanFactory`。这提供了一种更轻量级、更关注特定功能的方法来解析嵌入的值。 - -**自动注入 `StringValueResolver`**: 当我们的 Bean 实现了 `EmbeddedValueResolverAware` 接口,Spring 容器会在 Bean 初始化时自动调用 `setEmbeddedValueResolver` 方法,为其注入一个 `StringValueResolver` 实例。这样,Bean 可以在其生命周期中任何时候使用它来解析字符串值。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEmbeddedValueResolverAware`类型的bean,最后调用`resolve`方法。 - -```java -public class EmbeddedValueResolverAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyEmbeddedValueResolverAware resolverAware = context.getBean(MyEmbeddedValueResolverAware.class); - resolverAware.resolve(); - } -} -``` - -这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyEmbeddedValueResolverAware` 被 Spring 容器执行。 - -```java -@Configuration -public class MyConfiguration { - - @Bean - public MyEmbeddedValueResolverAware myEmbeddedValueResolverAware(){ - return new MyEmbeddedValueResolverAware(); - } -} -``` - -`MyEmbeddedValueResolverAware`类实现了`EmbeddedValueResolverAware`接口,当Spring容器初始化此类的Bean时,此方法将被调用,容器将传入一个`StringValueResolver`实例,然后通过`resolve()`方法,使用注入的`stringValueResolver`来解析包含占位符和SpEL表达式的字符串,并将解析后的字符串打印到控制台。 - -```java -public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { - - private StringValueResolver stringValueResolver; - - @Override - public void setEmbeddedValueResolver(StringValueResolver resolver) { - this.stringValueResolver = resolver; - } - - public void resolve() { - String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); - System.out.println(resolvedValue); - } -} -``` - -运行结果发现,结合 Spring 的 `Environment` 和 SpEL 功能来解析嵌入的字符串值,并得到了预期的运行结果。 - -```java -Hello, Lex! Today is 2023-10-03 -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: EnvironmentAware时序图 - participant EmbeddedValueResolverAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant ApplicationContextAwareProcessor - participant MyEmbeddedValueResolverAware - - EmbeddedValueResolverAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 - AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 - ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 - ApplicationContextAwareProcessor->>MyEmbeddedValueResolverAware:setEmbeddedValueResolver(resolver)
设置StringValueResolver - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>EmbeddedValueResolverAwareApplication:初始化完成 -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEmbeddedValueResolverAware`类型的bean,最后调用`resolve`方法。 - -```java -public class EmbeddedValueResolverAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyEmbeddedValueResolverAware resolverAware = context.getBean(MyEmbeddedValueResolverAware.class); - resolverAware.resolve(); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - Object wrappedBean = bean; - if (mbd == null || !mbd.isSynthetic()) { - wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); - } - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 - -```java -@Override -public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) - throws BeansException { - - Object result = existingBean; - for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; - } - result = current; - } - return result; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 - -```java -@Override -@Nullable -public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || - bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || - bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || - bean instanceof ApplicationStartupAware)) { - return bean; - } - - // ... [代码部分省略以简化] - - invokeAwareInterfaces(bean); - - return bean; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 - -```java -private void invokeAwareInterfaces(Object bean) { - // ... [代码部分省略以简化] - if (bean instanceof EmbeddedValueResolverAware) { - ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); - } - // ... [代码部分省略以简化] -} -``` - -最后执行到我们自定义的逻辑中,`MyEmbeddedValueResolverAware`类实现了`EmbeddedValueResolverAware`接口,当Spring容器初始化此类的Bean时,此方法将被调用,容器将传入一个`StringValueResolver`实例,然后通过`resolve()`方法,使用注入的`stringValueResolver`来解析包含占位符和SpEL表达式的字符串,并将解析后的字符串打印到控制台。 - -```java -public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { - - private StringValueResolver stringValueResolver; - - @Override - public void setEmbeddedValueResolver(StringValueResolver resolver) { - this.stringValueResolver = resolver; - } - - public void resolve() { - String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); - System.out.println(resolvedValue); - } -} -``` - -### 七、注意事项 - -**正确的环境**: 确保你在 Spring 的环境中使用它,因为 `StringValueResolver` 需要 Spring 上下文来正确解析嵌入的值。 - -**非延迟依赖注入**: `setEmbeddedValueResolver` 方法在 Bean 初始化时调用。如果你太早地尝试使用 `StringValueResolver`(例如,在构造函数中),它可能还没有被注入。 - -**默认值**: 当使用 `${user.name:xcs}` 语法时,如果 `user.name` 没有在环境中定义,它将使用 `xcs`。这可以避免因缺少配置而导致的错误。 - -**明确解析的范围**: `EmbeddedValueResolverAware` 通常用于解析占位符和 SpEL 表达式。确保不将它与更复杂的 Bean 解析逻辑混淆。 - -**错误处理**: 当解析一个字符串值失败时,Spring 通常会抛出一个异常。确保在代码中适当地处理这些异常。 - -**与其他 Aware 接口的交互**: 如果你的 Bean 实现了多个 `Aware` 接口,需要确保你理解了每个接口的初始化时机和顺序,以及如何与其他 Aware 方法(如 `setBeanFactory` 或 `setApplicationContext`)交互。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**启动类**:在 `EmbeddedValueResolverAwareApplication` 中,我们初始化了 Spring 的 `AnnotationConfigApplicationContext` 并加载了 `MyConfiguration` 作为配置类。接着,我们从上下文中取得 `MyEmbeddedValueResolverAware` 的 Bean,并调用了其 `resolve` 方法。 - -**配置与Bean声明**:在 `MyConfiguration` 配置类中,我们声明了 `MyEmbeddedValueResolverAware` 为一个 Bean,这确保了它会被 Spring 容器管理,并且会接收到 `StringValueResolver` 的实例注入。 - -**嵌入值解析**:`MyEmbeddedValueResolverAware` 类实现了 `EmbeddedValueResolverAware` 接口,这意味着在该 Bean 被初始化时,Spring 会自动提供一个 `StringValueResolver` 实例。这个解析器之后被用于解析字符串 "Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"。 - -#### 8.2、源码分析总结 - -**应用启动**:在`EmbeddedValueResolverAwareApplication`类中,使用`AnnotationConfigApplicationContext`来启动Spring应用并加载`MyConfiguration`配置类。 - -**容器初始化**:在构造函数`AnnotationConfigApplicationContext`中,`refresh()`方法被调用来初始化Spring容器。 - -**实例化Beans**:在`AbstractApplicationContext`的`refresh()`方法中,`finishBeanFactoryInitialization`方法被调用,确保所有单例Bean被预先实例化。 - -**Bean预实例化**:`DefaultListableBeanFactory`的`preInstantiateSingletons`方法确保所有非懒加载的单例Beans被实例化。核心操作是调用`getBean(beanName)`。 - -**获取Bean实例**:`AbstractBeanFactory`的`getBean`方法进一步调用`doGetBean`来真正实例化Bean,处理异常和依赖,并返回Bean实例。 - -**Bean单例获取**:`DefaultSingletonBeanRegistry`的`getSingleton`方法确保Bean以单例形式存在,从缓存获取或使用提供的`ObjectFactory`创建新实例。 - -**创建Bean实例**:`AbstractAutowireCapableBeanFactory`的`createBean`方法调用`doCreateBean`进行Bean的实际实例化,并进行初始化,确保Bean完全配置并准备就绪。 - -**Bean初始化**:`AbstractAutowireCapableBeanFactory`的`initializeBean`方法确保Bean被正确初始化,其中调用`applyBeanPostProcessorsBeforeInitialization`方法是Spring生命周期中的关键点,允许BeanPostProcessors在Bean初始化之前进行操作。 - -**处理Aware接口**:在Bean初始化过程中,`ApplicationContextAwareProcessor`确保实现了`Aware`接口的Beans被正确处理,这些Beans会自动"感知"并获得其运行环境或特定依赖的引用。 - +## EmbeddedValueResolverAware + +- [EmbeddedValueResolverAware](#embeddedvalueresolveraware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`EmbeddedValueResolverAware` 接口,主要用于提供一个字符串值解析器,这可以在 Bean 属性中解析占位符和表达式。如果我们熟悉 Spring 的 `${...}` 占位符和 `#{...}` 表达式,那么这个接口将帮助我们在自定义组件中解析这些值。 + +### 二、接口源码 + +`EmbeddedValueResolverAware` 是 Spring 框架自 3.0.3 开始引入的一个核心接口。允许对象在初始化时得到一个 `StringValueResolver`,并使用它来解析嵌入的字符串值,如配置文件中的占位符或 SpEL 表达式。 + +```java +/** + * 如果对象希望被通知一个 StringValueResolver,以解析嵌入的定义值,那么它应实现此接口。 + * + * 这提供了一个通过 ApplicationContextAware/BeanFactoryAware 接口 + * 依赖于完整的 ConfigurableBeanFactory 的替代方法。 + * + * @author Juergen Hoeller + * @author Chris Beams + * @since 3.0.3 + * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#resolveEmbeddedValue(String) + * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanExpressionResolver() + * @see org.springframework.beans.factory.config.EmbeddedValueResolver + */ +public interface EmbeddedValueResolverAware extends Aware { + + /** + * 设置用于解析嵌入定义值的 StringValueResolver。 + */ + void setEmbeddedValueResolver(StringValueResolver resolver); + +} +``` + +### 三、主要功能 + +**解析嵌入的字符串值**: 当我们在 Bean 的属性或构造函数参数中有一个值,如 `${property.name}` 或 `#{some.expression}`,这需要被解析成实际的值时,`StringValueResolver` 可以帮助做这件事。 + +**避免对 `ConfigurableBeanFactory` 的直接依赖**: 通过使用 `EmbeddedValueResolverAware`,我们可以间接地得到这种解析功能,而不必直接依赖于整个 `ConfigurableBeanFactory`。这提供了一种更轻量级、更关注特定功能的方法来解析嵌入的值。 + +**自动注入 `StringValueResolver`**: 当我们的 Bean 实现了 `EmbeddedValueResolverAware` 接口,Spring 容器会在 Bean 初始化时自动调用 `setEmbeddedValueResolver` 方法,为其注入一个 `StringValueResolver` 实例。这样,Bean 可以在其生命周期中任何时候使用它来解析字符串值。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEmbeddedValueResolverAware`类型的bean,最后调用`resolve`方法。 + +```java +public class EmbeddedValueResolverAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyEmbeddedValueResolverAware resolverAware = context.getBean(MyEmbeddedValueResolverAware.class); + resolverAware.resolve(); + } +} +``` + +这里使用`@Bean`注解,定义了一个Bean,是为了确保 `MyEmbeddedValueResolverAware` 被 Spring 容器执行。 + +```java +@Configuration +public class MyConfiguration { + + @Bean + public MyEmbeddedValueResolverAware myEmbeddedValueResolverAware(){ + return new MyEmbeddedValueResolverAware(); + } +} +``` + +`MyEmbeddedValueResolverAware`类实现了`EmbeddedValueResolverAware`接口,当Spring容器初始化此类的Bean时,此方法将被调用,容器将传入一个`StringValueResolver`实例,然后通过`resolve()`方法,使用注入的`stringValueResolver`来解析包含占位符和SpEL表达式的字符串,并将解析后的字符串打印到控制台。 + +```java +public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { + + private StringValueResolver stringValueResolver; + + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.stringValueResolver = resolver; + } + + public void resolve() { + String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); + System.out.println(resolvedValue); + } +} +``` + +运行结果发现,结合 Spring 的 `Environment` 和 SpEL 功能来解析嵌入的字符串值,并得到了预期的运行结果。 + +```java +Hello, Lex! Today is 2023-10-03 +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: EnvironmentAware时序图 + participant EmbeddedValueResolverAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant ApplicationContextAwareProcessor + participant MyEmbeddedValueResolverAware + + EmbeddedValueResolverAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 + AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 + ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 + ApplicationContextAwareProcessor->>MyEmbeddedValueResolverAware:setEmbeddedValueResolver(resolver)
设置StringValueResolver + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>EmbeddedValueResolverAwareApplication:初始化完成 +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEmbeddedValueResolverAware`类型的bean,最后调用`resolve`方法。 + +```java +public class EmbeddedValueResolverAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyEmbeddedValueResolverAware resolverAware = context.getBean(MyEmbeddedValueResolverAware.class); + resolverAware.resolve(); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 + +```java +@Override +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 + +```java +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || + bean instanceof ApplicationStartupAware)) { + return bean; + } + + // ... [代码部分省略以简化] + + invokeAwareInterfaces(bean); + + return bean; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 + +```java +private void invokeAwareInterfaces(Object bean) { + // ... [代码部分省略以简化] + if (bean instanceof EmbeddedValueResolverAware) { + ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); + } + // ... [代码部分省略以简化] +} +``` + +最后执行到我们自定义的逻辑中,`MyEmbeddedValueResolverAware`类实现了`EmbeddedValueResolverAware`接口,当Spring容器初始化此类的Bean时,此方法将被调用,容器将传入一个`StringValueResolver`实例,然后通过`resolve()`方法,使用注入的`stringValueResolver`来解析包含占位符和SpEL表达式的字符串,并将解析后的字符串打印到控制台。 + +```java +public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { + + private StringValueResolver stringValueResolver; + + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.stringValueResolver = resolver; + } + + public void resolve() { + String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); + System.out.println(resolvedValue); + } +} +``` + +### 七、注意事项 + +**正确的环境**: 确保你在 Spring 的环境中使用它,因为 `StringValueResolver` 需要 Spring 上下文来正确解析嵌入的值。 + +**非延迟依赖注入**: `setEmbeddedValueResolver` 方法在 Bean 初始化时调用。如果你太早地尝试使用 `StringValueResolver`(例如,在构造函数中),它可能还没有被注入。 + +**默认值**: 当使用 `${user.name:xcs}` 语法时,如果 `user.name` 没有在环境中定义,它将使用 `xcs`。这可以避免因缺少配置而导致的错误。 + +**明确解析的范围**: `EmbeddedValueResolverAware` 通常用于解析占位符和 SpEL 表达式。确保不将它与更复杂的 Bean 解析逻辑混淆。 + +**错误处理**: 当解析一个字符串值失败时,Spring 通常会抛出一个异常。确保在代码中适当地处理这些异常。 + +**与其他 Aware 接口的交互**: 如果你的 Bean 实现了多个 `Aware` 接口,需要确保你理解了每个接口的初始化时机和顺序,以及如何与其他 Aware 方法(如 `setBeanFactory` 或 `setApplicationContext`)交互。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**启动类**:在 `EmbeddedValueResolverAwareApplication` 中,我们初始化了 Spring 的 `AnnotationConfigApplicationContext` 并加载了 `MyConfiguration` 作为配置类。接着,我们从上下文中取得 `MyEmbeddedValueResolverAware` 的 Bean,并调用了其 `resolve` 方法。 + +**配置与Bean声明**:在 `MyConfiguration` 配置类中,我们声明了 `MyEmbeddedValueResolverAware` 为一个 Bean,这确保了它会被 Spring 容器管理,并且会接收到 `StringValueResolver` 的实例注入。 + +**嵌入值解析**:`MyEmbeddedValueResolverAware` 类实现了 `EmbeddedValueResolverAware` 接口,这意味着在该 Bean 被初始化时,Spring 会自动提供一个 `StringValueResolver` 实例。这个解析器之后被用于解析字符串 "Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"。 + +#### 8.2、源码分析总结 + +**应用启动**:在`EmbeddedValueResolverAwareApplication`类中,使用`AnnotationConfigApplicationContext`来启动Spring应用并加载`MyConfiguration`配置类。 + +**容器初始化**:在构造函数`AnnotationConfigApplicationContext`中,`refresh()`方法被调用来初始化Spring容器。 + +**实例化Beans**:在`AbstractApplicationContext`的`refresh()`方法中,`finishBeanFactoryInitialization`方法被调用,确保所有单例Bean被预先实例化。 + +**Bean预实例化**:`DefaultListableBeanFactory`的`preInstantiateSingletons`方法确保所有非懒加载的单例Beans被实例化。核心操作是调用`getBean(beanName)`。 + +**获取Bean实例**:`AbstractBeanFactory`的`getBean`方法进一步调用`doGetBean`来真正实例化Bean,处理异常和依赖,并返回Bean实例。 + +**Bean单例获取**:`DefaultSingletonBeanRegistry`的`getSingleton`方法确保Bean以单例形式存在,从缓存获取或使用提供的`ObjectFactory`创建新实例。 + +**创建Bean实例**:`AbstractAutowireCapableBeanFactory`的`createBean`方法调用`doCreateBean`进行Bean的实际实例化,并进行初始化,确保Bean完全配置并准备就绪。 + +**Bean初始化**:`AbstractAutowireCapableBeanFactory`的`initializeBean`方法确保Bean被正确初始化,其中调用`applyBeanPostProcessorsBeforeInitialization`方法是Spring生命周期中的关键点,允许BeanPostProcessors在Bean初始化之前进行操作。 + +**处理Aware接口**:在Bean初始化过程中,`ApplicationContextAwareProcessor`确保实现了`Aware`接口的Beans被正确处理,这些Beans会自动"感知"并获得其运行环境或特定依赖的引用。 + **值解析**:最后,我们的`MyEmbeddedValueResolverAware` Bean接收到了一个`StringValueResolver`实例。此时,当`resolve`方法被调用,它会使用这个解析器来解析嵌入的字符串值,并打印到控制台。 \ No newline at end of file diff --git a/spring-aware-embeddedValueResolverAware/pom.xml b/spring-aware/spring-aware-embeddedValueResolverAware/pom.xml similarity index 89% rename from spring-aware-embeddedValueResolverAware/pom.xml rename to spring-aware/spring-aware-embeddedValueResolverAware/pom.xml index 17751c9..9071e68 100644 --- a/spring-aware-embeddedValueResolverAware/pom.xml +++ b/spring-aware/spring-aware-embeddedValueResolverAware/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-aware com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/EmbeddedValueResolverAwareApplication.java b/spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/EmbeddedValueResolverAwareApplication.java similarity index 100% rename from spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/EmbeddedValueResolverAwareApplication.java rename to spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/EmbeddedValueResolverAwareApplication.java diff --git a/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyEmbeddedValueResolverAware.java b/spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyEmbeddedValueResolverAware.java similarity index 97% rename from spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyEmbeddedValueResolverAware.java rename to spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyEmbeddedValueResolverAware.java index 3f7df9d..a9e5e66 100644 --- a/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyEmbeddedValueResolverAware.java +++ b/spring-aware/spring-aware-embeddedValueResolverAware/src/main/java/com/xcs/spring/config/MyEmbeddedValueResolverAware.java @@ -1,19 +1,19 @@ -package com.xcs.spring.config; - -import org.springframework.context.EmbeddedValueResolverAware; -import org.springframework.util.StringValueResolver; - -public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { - - private StringValueResolver stringValueResolver; - - @Override - public void setEmbeddedValueResolver(StringValueResolver resolver) { - this.stringValueResolver = resolver; - } - - public void resolve() { - String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); - System.out.println(resolvedValue); - } -} +package com.xcs.spring.config; + +import org.springframework.context.EmbeddedValueResolverAware; +import org.springframework.util.StringValueResolver; + +public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware { + + private StringValueResolver stringValueResolver; + + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.stringValueResolver = resolver; + } + + public void resolve() { + String resolvedValue = stringValueResolver.resolveStringValue("Hello, ${user.name:xcs}! Today is #{T(java.time.LocalDate).now().toString()}"); + System.out.println(resolvedValue); + } +} diff --git a/spring-aware-environmentAware/README.md b/spring-aware/spring-aware-environmentAware/README.md similarity index 97% rename from spring-aware-environmentAware/README.md rename to spring-aware/spring-aware-environmentAware/README.md index 0ffca4c..ba57171 100644 --- a/spring-aware-environmentAware/README.md +++ b/spring-aware/spring-aware-environmentAware/README.md @@ -1,464 +1,464 @@ -## EnvironmentAware - -- [EnvironmentAware](#environmentaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`EnvironmentAware` 接口,允许Beans访问`Environment`对象。这是一个回调接口,当实现该接口的Bean被Spring容器管理时,Spring容器会为该Bean设置`Environment`对象。 - -### 二、接口源码 - -`EnvironmentAware` 是 Spring 框架自 3.1 开始引入的一个核心接口。主要目的是允许bean在运行时获取与其运行环境有关的信息。这包括环境属性、配置文件、激活的profile等。 - -```java -/** - * 任何希望被通知其运行的Environment的bean应该实现的接口。 - * - * @author Chris Beams - * @since 3.1 - * @see org.springframework.core.env.EnvironmentCapable - */ -public interface EnvironmentAware extends Aware { - - /** - * 设置此组件运行所在的Environment。 - */ - void setEnvironment(Environment environment); -} -``` - -### 三、主要功能 - -**访问环境属性**:通过实现 `EnvironmentAware`,beans 可以直接访问应用上下文的`Environment`对象。这意味着它们可以读取环境属性,这些属性可能来自多个来源,例如系统属性、JVM参数、操作系统环境变量、属性文件等。 - -**识别运行时环境**:beans可以通过`Environment`对象来检查和确定当前激活的Spring profiles。这使得bean可以根据不同的运行环境(例如开发、测试、生产等)进行特定的操作或配置。 - -**自动回调**:当Spring容器识别到一个bean实现了`EnvironmentAware`接口时,容器会自动调用 `setEnvironment` 方法并传递当前的 `Environment` 对象。这意味着开发者不需要特意去手动设置或获取它。 - -**框架级别的集成**:此接口提供了一个标准机制,允许框架级别的代码(如其他Spring组件和第三方库)访问和集成`Environment`对象,而不必依赖特定的注入策略或其他机制。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEnvironmentAware`类型的bean,最后调用`getAppProperty`方法并打印。 - -```java -public class EnvironmentAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyEnvironmentAware environmentAware = context.getBean(MyEnvironmentAware.class); - System.out.println("AppProperty = " + environmentAware.getAppProperty()); - } -} -``` - -这里使用`@Bean`注解,定义了以个Bean,是为了确保 `MyEnvironmentAware` 被 Spring 容器执行,另外使用`@PropertySource`注解从类路径下的`application.properties`文件中加载属性。这意味着我们可以在这个文件中定义属性,然后在应用中使用`Environment`对象来访问它们。 - -```java -@Configuration -@PropertySource("classpath:application.properties") -public class MyConfiguration { - - @Bean - public MyEnvironmentAware myEnvironmentAware(){ - return new MyEnvironmentAware(); - } - -} -``` - -`MyEnvironmentAware`类实现了`EnvironmentAware`接口,并重写了`setEnvironment`方法,以便在Spring容器初始化它时获取`Environment`对象。之后,我们可以使用`getPropertyValue`方法来查询`application.properties`中的任何属性。 - -```java -public class MyEnvironmentAware implements EnvironmentAware { - - private String appProperty; - - @Override - public void setEnvironment(Environment environment) { - this.appProperty = environment.getProperty("app.xcs.property"); - } - - public String getAppProperty() { - return appProperty; - } -} -``` - -运行结果发现,这个输出证明了`EnvironmentAware`接口及其与`application.properties`文件的整合成功工作,我们已经成功地使用Spring环境获取了配置属性。 - -```java -AppProperty = Hello from EnvironmentAware! -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: EnvironmentAware时序图 - participant EnvironmentAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant ApplicationContextAwareProcessor - participant MyEnvironmentAware - - EnvironmentAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 - AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 - ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 - ApplicationContextAwareProcessor->>MyEnvironmentAware:setEnvironment(environment)
设置运行环境 - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>EnvironmentAwareApplication:初始化完成 -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEnvironmentAware`类型的bean,最后调用`getAppProperty`方法并打印。 - -```java -public class EnvironmentAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyEnvironmentAware environmentAware = context.getBean(MyEnvironmentAware.class); - System.out.println("AppProperty = " + environmentAware.getAppProperty()); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - Object wrappedBean = bean; - if (mbd == null || !mbd.isSynthetic()) { - wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); - } - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 - -```java -@Override -public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) - throws BeansException { - - Object result = existingBean; - for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; - } - result = current; - } - return result; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 - -```java -@Override -@Nullable -public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || - bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || - bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || - bean instanceof ApplicationStartupAware)) { - return bean; - } - - // ... [代码部分省略以简化] - - invokeAwareInterfaces(bean); - - return bean; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 - -```java -private void invokeAwareInterfaces(Object bean) { - if (bean instanceof EnvironmentAware) { - ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); - } - // ... [代码部分省略以简化] -} -``` - -最后执行到我们自定义的逻辑中,`MyEnvironmentAware`类实现了`EnvironmentAware`接口,并重写了`setEnvironment`方法,以便在Spring容器初始化它时获取`Environment`对象。之后,我们可以使用`getPropertyValue`方法来查询`application.properties`中的任何属性。 - -```java -public class MyEnvironmentAware implements EnvironmentAware { - - private String appProperty; - - @Override - public void setEnvironment(Environment environment) { - this.appProperty = environment.getProperty("app.xcs.property"); - } - - public String getAppProperty() { - return appProperty; - } -} -``` - -### 七、注意事项 - -**不要过度使用**:虽然`EnvironmentAware`为Bean提供了一个直接访问`Environment`的方法,但这并不意味着所有的Bean都应该使用它。在可能的情况下,首先考虑使用Spring的属性注入功能,例如`@Value`。 - -**避免使用硬编码的属性键**:当从`Environment`对象中获取属性时,尽量避免在代码中硬编码属性键。最好是将这些键作为常量或在外部配置中定义。 - -**处理不存在的属性**:当使用`Environment`获取属性时,如果该属性不存在,`Environment`可能会返回`null`。确保在代码中正确处理这种情况,或使用`Environment`提供的默认值方法。 - -**记住激活的配置文件**:`Environment`允许我们查询当前激活的配置文件(profiles)。确保我们知道哪些profiles是激活的,尤其是在使用特定于profile的属性时。 - -**了解Environment的层次结构**:`Environment`对象可能会从多个来源获取属性(例如系统属性、环境变量、配置文件等)。了解这些来源的优先级和加载顺序,以便正确地理解在存在冲突时哪个属性值会被使用。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**启动过程**:通过`EnvironmentAwareApplication`作为主入口,我们使用了`AnnotationConfigApplicationContext`来启动Spring上下文,并加载了`MyConfiguration`作为配置类。 - -**加载属性**:在`MyConfiguration`类中,我们使用了`@PropertySource`注解指定了从类路径下的`application.properties`文件加载属性到Spring的环境中。 - -**注册Bean**:在配置类`MyConfiguration`中,我们定义了一个bean `MyEnvironmentAware`。这保证了当Spring容器启动时,`MyEnvironmentAware`对象会被创建并由Spring管理。 - -**访问环境属性**:`MyEnvironmentAware`类实现了`EnvironmentAware`接口,这使得当Spring容器初始化该bean时,它会自动调用`setEnvironment`方法,注入当前的`Environment`对象。我们使用这个方法来读取`app.xcs.property`属性,并将其值存储在`appProperty`私有变量中。 - -**显示属性**:最后,在`EnvironmentAwareApplication`主程序中,我们从Spring上下文中获取了`MyEnvironmentAware` bean,并调用了`getAppProperty`方法来获取属性值,然后将其打印到控制台。 - -**输出**:结果显示为“AppProperty = Hello from EnvironmentAware!”,这证明了`EnvironmentAware`接口和`application.properties`文件成功地结合起来,并且我们已经成功地使用Spring环境获取了配置属性。 - -#### 8.2、源码分析总结 - -**应用启动**: 通过`EnvironmentAwareApplication`作为入口,使用`AnnotationConfigApplicationContext`来初始化Spring的上下文,并加载`MyConfiguration`作为配置类。 - -**属性加载**: 在`MyConfiguration`类中,利用`@PropertySource`注解,指定从`application.properties`文件加载属性到Spring环境中。 - -**Bean注册与初始化**: 在上下文的`refresh()`方法中,调用`finishBeanFactoryInitialization()`确保所有非懒加载的单例bean都被实例化。这个过程在`preInstantiateSingletons()`中通过循环调用`getBean()`完成,该方法将触发bean的创建、初始化及其依赖的注入。 - -**Bean后处理与"感知"**: 在bean的初始化过程中,`ApplicationContextAwareProcessor`负责检查并调用那些实现了Aware接口的bean的特定方法。对于实现了`EnvironmentAware`接口的beans,它会调用`setEnvironment()`方法并传入当前的`Environment`对象。 - -**自定义Bean的处理**: `MyEnvironmentAware`在其`setEnvironment()`方法中,从传入的`Environment`对象中获取了`app.xcs.property`属性,并存储到了它的私有变量`appProperty`中。 - +## EnvironmentAware + +- [EnvironmentAware](#environmentaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`EnvironmentAware` 接口,允许Beans访问`Environment`对象。这是一个回调接口,当实现该接口的Bean被Spring容器管理时,Spring容器会为该Bean设置`Environment`对象。 + +### 二、接口源码 + +`EnvironmentAware` 是 Spring 框架自 3.1 开始引入的一个核心接口。主要目的是允许bean在运行时获取与其运行环境有关的信息。这包括环境属性、配置文件、激活的profile等。 + +```java +/** + * 任何希望被通知其运行的Environment的bean应该实现的接口。 + * + * @author Chris Beams + * @since 3.1 + * @see org.springframework.core.env.EnvironmentCapable + */ +public interface EnvironmentAware extends Aware { + + /** + * 设置此组件运行所在的Environment。 + */ + void setEnvironment(Environment environment); +} +``` + +### 三、主要功能 + +**访问环境属性**:通过实现 `EnvironmentAware`,beans 可以直接访问应用上下文的`Environment`对象。这意味着它们可以读取环境属性,这些属性可能来自多个来源,例如系统属性、JVM参数、操作系统环境变量、属性文件等。 + +**识别运行时环境**:beans可以通过`Environment`对象来检查和确定当前激活的Spring profiles。这使得bean可以根据不同的运行环境(例如开发、测试、生产等)进行特定的操作或配置。 + +**自动回调**:当Spring容器识别到一个bean实现了`EnvironmentAware`接口时,容器会自动调用 `setEnvironment` 方法并传递当前的 `Environment` 对象。这意味着开发者不需要特意去手动设置或获取它。 + +**框架级别的集成**:此接口提供了一个标准机制,允许框架级别的代码(如其他Spring组件和第三方库)访问和集成`Environment`对象,而不必依赖特定的注入策略或其他机制。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEnvironmentAware`类型的bean,最后调用`getAppProperty`方法并打印。 + +```java +public class EnvironmentAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyEnvironmentAware environmentAware = context.getBean(MyEnvironmentAware.class); + System.out.println("AppProperty = " + environmentAware.getAppProperty()); + } +} +``` + +这里使用`@Bean`注解,定义了以个Bean,是为了确保 `MyEnvironmentAware` 被 Spring 容器执行,另外使用`@PropertySource`注解从类路径下的`application.properties`文件中加载属性。这意味着我们可以在这个文件中定义属性,然后在应用中使用`Environment`对象来访问它们。 + +```java +@Configuration +@PropertySource("classpath:application.properties") +public class MyConfiguration { + + @Bean + public MyEnvironmentAware myEnvironmentAware(){ + return new MyEnvironmentAware(); + } + +} +``` + +`MyEnvironmentAware`类实现了`EnvironmentAware`接口,并重写了`setEnvironment`方法,以便在Spring容器初始化它时获取`Environment`对象。之后,我们可以使用`getPropertyValue`方法来查询`application.properties`中的任何属性。 + +```java +public class MyEnvironmentAware implements EnvironmentAware { + + private String appProperty; + + @Override + public void setEnvironment(Environment environment) { + this.appProperty = environment.getProperty("app.xcs.property"); + } + + public String getAppProperty() { + return appProperty; + } +} +``` + +运行结果发现,这个输出证明了`EnvironmentAware`接口及其与`application.properties`文件的整合成功工作,我们已经成功地使用Spring环境获取了配置属性。 + +```java +AppProperty = Hello from EnvironmentAware! +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: EnvironmentAware时序图 + participant EnvironmentAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant ApplicationContextAwareProcessor + participant MyEnvironmentAware + + EnvironmentAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 + AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 + ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 + ApplicationContextAwareProcessor->>MyEnvironmentAware:setEnvironment(environment)
设置运行环境 + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>EnvironmentAwareApplication:初始化完成 +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyEnvironmentAware`类型的bean,最后调用`getAppProperty`方法并打印。 + +```java +public class EnvironmentAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyEnvironmentAware environmentAware = context.getBean(MyEnvironmentAware.class); + System.out.println("AppProperty = " + environmentAware.getAppProperty()); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 + +```java +@Override +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 + +```java +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || + bean instanceof ApplicationStartupAware)) { + return bean; + } + + // ... [代码部分省略以简化] + + invokeAwareInterfaces(bean); + + return bean; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 + +```java +private void invokeAwareInterfaces(Object bean) { + if (bean instanceof EnvironmentAware) { + ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); + } + // ... [代码部分省略以简化] +} +``` + +最后执行到我们自定义的逻辑中,`MyEnvironmentAware`类实现了`EnvironmentAware`接口,并重写了`setEnvironment`方法,以便在Spring容器初始化它时获取`Environment`对象。之后,我们可以使用`getPropertyValue`方法来查询`application.properties`中的任何属性。 + +```java +public class MyEnvironmentAware implements EnvironmentAware { + + private String appProperty; + + @Override + public void setEnvironment(Environment environment) { + this.appProperty = environment.getProperty("app.xcs.property"); + } + + public String getAppProperty() { + return appProperty; + } +} +``` + +### 七、注意事项 + +**不要过度使用**:虽然`EnvironmentAware`为Bean提供了一个直接访问`Environment`的方法,但这并不意味着所有的Bean都应该使用它。在可能的情况下,首先考虑使用Spring的属性注入功能,例如`@Value`。 + +**避免使用硬编码的属性键**:当从`Environment`对象中获取属性时,尽量避免在代码中硬编码属性键。最好是将这些键作为常量或在外部配置中定义。 + +**处理不存在的属性**:当使用`Environment`获取属性时,如果该属性不存在,`Environment`可能会返回`null`。确保在代码中正确处理这种情况,或使用`Environment`提供的默认值方法。 + +**记住激活的配置文件**:`Environment`允许我们查询当前激活的配置文件(profiles)。确保我们知道哪些profiles是激活的,尤其是在使用特定于profile的属性时。 + +**了解Environment的层次结构**:`Environment`对象可能会从多个来源获取属性(例如系统属性、环境变量、配置文件等)。了解这些来源的优先级和加载顺序,以便正确地理解在存在冲突时哪个属性值会被使用。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**启动过程**:通过`EnvironmentAwareApplication`作为主入口,我们使用了`AnnotationConfigApplicationContext`来启动Spring上下文,并加载了`MyConfiguration`作为配置类。 + +**加载属性**:在`MyConfiguration`类中,我们使用了`@PropertySource`注解指定了从类路径下的`application.properties`文件加载属性到Spring的环境中。 + +**注册Bean**:在配置类`MyConfiguration`中,我们定义了一个bean `MyEnvironmentAware`。这保证了当Spring容器启动时,`MyEnvironmentAware`对象会被创建并由Spring管理。 + +**访问环境属性**:`MyEnvironmentAware`类实现了`EnvironmentAware`接口,这使得当Spring容器初始化该bean时,它会自动调用`setEnvironment`方法,注入当前的`Environment`对象。我们使用这个方法来读取`app.xcs.property`属性,并将其值存储在`appProperty`私有变量中。 + +**显示属性**:最后,在`EnvironmentAwareApplication`主程序中,我们从Spring上下文中获取了`MyEnvironmentAware` bean,并调用了`getAppProperty`方法来获取属性值,然后将其打印到控制台。 + +**输出**:结果显示为“AppProperty = Hello from EnvironmentAware!”,这证明了`EnvironmentAware`接口和`application.properties`文件成功地结合起来,并且我们已经成功地使用Spring环境获取了配置属性。 + +#### 8.2、源码分析总结 + +**应用启动**: 通过`EnvironmentAwareApplication`作为入口,使用`AnnotationConfigApplicationContext`来初始化Spring的上下文,并加载`MyConfiguration`作为配置类。 + +**属性加载**: 在`MyConfiguration`类中,利用`@PropertySource`注解,指定从`application.properties`文件加载属性到Spring环境中。 + +**Bean注册与初始化**: 在上下文的`refresh()`方法中,调用`finishBeanFactoryInitialization()`确保所有非懒加载的单例bean都被实例化。这个过程在`preInstantiateSingletons()`中通过循环调用`getBean()`完成,该方法将触发bean的创建、初始化及其依赖的注入。 + +**Bean后处理与"感知"**: 在bean的初始化过程中,`ApplicationContextAwareProcessor`负责检查并调用那些实现了Aware接口的bean的特定方法。对于实现了`EnvironmentAware`接口的beans,它会调用`setEnvironment()`方法并传入当前的`Environment`对象。 + +**自定义Bean的处理**: `MyEnvironmentAware`在其`setEnvironment()`方法中,从传入的`Environment`对象中获取了`app.xcs.property`属性,并存储到了它的私有变量`appProperty`中。 + **应用结果输出**: 在`EnvironmentAwareApplication`的主方法中,从Spring上下文获取了`MyEnvironmentAware` bean并调用其`getAppProperty()`方法,然后将获得的属性值输出到控制台。 \ No newline at end of file diff --git a/spring-aware-environmentAware/pom.xml b/spring-aware/spring-aware-environmentAware/pom.xml similarity index 89% rename from spring-aware-environmentAware/pom.xml rename to spring-aware/spring-aware-environmentAware/pom.xml index aefb98e..ef1c511 100644 --- a/spring-aware-environmentAware/pom.xml +++ b/spring-aware/spring-aware-environmentAware/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-aware com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-aware-environmentAware/src/main/java/com/xcs/spring/EnvironmentAwareApplication.java b/spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/EnvironmentAwareApplication.java similarity index 100% rename from spring-aware-environmentAware/src/main/java/com/xcs/spring/EnvironmentAwareApplication.java rename to spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/EnvironmentAwareApplication.java diff --git a/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyEnvironmentAware.java b/spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyEnvironmentAware.java similarity index 96% rename from spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyEnvironmentAware.java rename to spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyEnvironmentAware.java index 8102f24..2980fb8 100644 --- a/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyEnvironmentAware.java +++ b/spring-aware/spring-aware-environmentAware/src/main/java/com/xcs/spring/config/MyEnvironmentAware.java @@ -1,18 +1,18 @@ -package com.xcs.spring.config; - -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; - -public class MyEnvironmentAware implements EnvironmentAware { - - private String appProperty; - - @Override - public void setEnvironment(Environment environment) { - this.appProperty = environment.getProperty("app.xcs.property"); - } - - public String getAppProperty() { - return appProperty; - } -} +package com.xcs.spring.config; + +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; + +public class MyEnvironmentAware implements EnvironmentAware { + + private String appProperty; + + @Override + public void setEnvironment(Environment environment) { + this.appProperty = environment.getProperty("app.xcs.property"); + } + + public String getAppProperty() { + return appProperty; + } +} diff --git a/spring-aware-environmentAware/src/main/resources/application.properties b/spring-aware/spring-aware-environmentAware/src/main/resources/application.properties similarity index 100% rename from spring-aware-environmentAware/src/main/resources/application.properties rename to spring-aware/spring-aware-environmentAware/src/main/resources/application.properties diff --git a/spring-aware-importAware/README.md b/spring-aware/spring-aware-importAware/README.md similarity index 97% rename from spring-aware-importAware/README.md rename to spring-aware/spring-aware-importAware/README.md index ef68d1a..e994a33 100644 --- a/spring-aware-importAware/README.md +++ b/spring-aware/spring-aware-importAware/README.md @@ -1,493 +1,493 @@ -## ImportAware - -- [ImportAware](#importaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`ImportAware` 接口,提供被导入类的访问功能。当一个类实现了 `ImportAware` 接口,并且被通过 @Import 注解导入到其他配置类中,该类可以获得对导入它的 `AnnotationMetadata` 的访问权。 - -### 二、接口源码 - -`ApplicationStartupAware` 是 Spring 框架自 3.1 开始引入的一个核心接口。实现`ImportAware`接口的对象会在Spring容器中被自动注入一个`AnnotationMetadata`实例。 - -```java -/** - * 任何希望被注入其导入它的Configuration类的AnnotationMetadata的Configuration类都应实现此接口。 - * 与使用Import作为元注解的注解结合使用时特别有用。 - * - * @author Chris Beams - * @since 3.1 - */ -public interface ImportAware extends Aware { - - /** - * 设置导入的@Configuration类的注解元数据。 - */ - void setImportMetadata(AnnotationMetadata importMetadata); - -} -``` - -### 三、主要功能 - -**访问导入类的注解元数据**:当一个类实现了 `ImportAware` 接口,并且它是通过 `@Import` 或其他特定方式被导入的,Spring 容器会自动调用它的 `setImportMetadata` 方法,并传入与导入该类的注解相关的 `AnnotationMetadata`。 - -**条件性的行为**:通过访问导入类的注解元数据,可以实现基于特定条件的行为。例如,根据导入类上的注解属性,决定是否注册某个 bean,或者为 bean 设置特定的属性值。 - -**框架和库的开发**:`ImportAware` 在 Spring 框架内部和某些第三方库中被用于执行特定的初始化和配置任务。例如,某些特性的自动配置可能会根据导入它们的配置类上的注解属性进行调整。 - -**增强诊断和调试信息**:可以基于导入类的元数据为我们提供更多的上下文信息,这在诊断复杂的配置问题时可能会很有用。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`String`类型的bean并打印。 - -```java -public class ImportAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - String customBean = context.getBean(String.class); - System.out.println(customBean); - } -} -``` - - `MyConfiguration` 是一个 Spring 配置类,然后通过这个类启用了通过 `@EnableXcs` 注解提供`ImportAware`功能。 - -```java -@Configuration -@EnableXcs -public class MyConfiguration { - -} -``` - -`EnableXcs`是一个注解类,`@Import(MyImportAware.class)`会对 Spring 上下文进行某种配置或修改 - -```java -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Documented -@Import(MyImportAware.class) -public @interface EnableXcs { - -} -``` - -由于 `MyImportAware` 实现了 `ImportAware`,它会检查导入它的配置类上是否存在 `@EnableXcs` 注解。如果存在,则继续处理并注册 `customBean` 这个 String 类型的 bean 到 Spring 上下文中。如果不存在 `@EnableXcs` 注解,则抛出异常。 - -```java -public class MyImportAware implements ImportAware { - - private AnnotationAttributes enableXcs; - - @Override - public void setImportMetadata(AnnotationMetadata importMetadata) { - this.enableXcs = AnnotationAttributes.fromMap( - importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); - if (this.enableXcs == null) { - throw new IllegalArgumentException( - "@EnableXcs is not present on importing class " + importMetadata.getClassName()); - } - } - - @Bean - public String customBean() { - return "This is a custom bean!"; - } -} -``` - -运行结果发现,当我们在 Spring 上下文中使用 `@EnableXcs` 注解并运行程序时,`MyImportAware` 类会被导入并处理,最后在 Spring 容器中注册一个 String 类型的 bean,其值为 "This is a custom bean!"。 - -```java -This is a custom bean! -``` - -当我们不通过`@EnableXcs` 注解方式去导入`MyImportAware`类,而是直接在MyConfiguration类中导入`@Import(MyImportAware.class)`来看看另外一种情况。 - -```java -@Configuration -@Import(MyImportAware.class) -public class MyConfiguration { - -} -``` - -运行结果发现,当我们直接使用`@Import(MyImportAware.class)`导入`MyImportAware`类而不使用`@EnableXcs`注解时,由于`MyConfiguration`上没有`@EnableXcs`注解,所以`enableXcs`的值为null,由于此时`enableXcs`是null,`MyImportAware`抛出了一个`IllegalArgumentException`异常。 - -```java -Caused by: java.lang.IllegalArgumentException: @EnableXcs is not present on importing class com.xcs.spring.config.MyConfiguration - at com.xcs.spring.config.MyImportAware.setImportMetadata(MyImportAware.java:19) - at org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor.postProcessBeforeInitialization(ConfigurationClassPostProcessor.java:484) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) - ... 10 more -``` - - - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: ImportAware时序图 - participant ImportAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant ImportAwareBeanPostProcessor - participant MyImportAware - - ImportAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses) - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh() - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory) - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons() - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name) - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly) - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory) - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject() - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args) - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args) - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd) - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean,beanName) - AbstractAutowireCapableBeanFactory->>ImportAwareBeanPostProcessor: postProcessBeforeInitialization(bean,beanName) - ImportAwareBeanPostProcessor->>MyImportAware:setImportMetadata(importMetadata)设置importMetadata - ImportAwareBeanPostProcessor-->>AbstractAutowireCapableBeanFactory: 返回Bean对象 - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext->>ImportAwareApplication: 初始化完成 -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`String`类型的bean并打印。 - -```java -public class ImportAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - String customBean = context.getBean(String.class); - System.out.println(customBean); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,主要负责bean初始化。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean()`方法中,核心逻辑是对`BeanPostProcessors`接口中的`postProcessBeforeInitialization`进行回调。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - Object wrappedBean = bean; - if (mbd == null || !mbd.isSynthetic()) { - wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); - } - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强。 - -```java -@Override -public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) - throws BeansException { - - Object result = existingBean; - for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; - } - result = current; - } - return result; -} -``` - -在`org.springframework.context.annotation.ConfigurationClassPostProcessor.ImportAwareBeanPostProcessor#postProcessBeforeInitialization`方法中,主要作用是为实现了 `ImportAware` 接口的 beans 设置导入它们的类的 `AnnotationMetadata`。这样,任何实现了 `ImportAware` 接口的 bean 都可以知道它是由哪个类导入的,以及这个导入类上的所有注解信息。 - -```java -@Override -public Object postProcessBeforeInitialization(Object bean, String beanName) { - if (bean instanceof ImportAware) { - ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class); - AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName()); - if (importingClass != null) { - ((ImportAware) bean).setImportMetadata(importingClass); - } - } - return bean; -} -``` - -最后执行到我们自定义的逻辑中,由于 `MyImportAware` 实现了 `ImportAware`,它会检查导入它的配置类上是否存在 `@EnableXcs` 注解。如果存在,则继续处理并注册 `customBean` 这个 String 类型的 bean 到 Spring 上下文中。如果不存在 `@EnableXcs` 注解,则抛出异常。 - -```java -public class MyImportAware implements ImportAware { - - private AnnotationAttributes enableXcs; - - @Override - public void setImportMetadata(AnnotationMetadata importMetadata) { - this.enableXcs = AnnotationAttributes.fromMap( - importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); - if (this.enableXcs == null) { - throw new IllegalArgumentException( - "@EnableXcs is not present on importing class " + importMetadata.getClassName()); - } - } - - @Bean - public String customBean() { - return "This is a custom bean!"; - } -} -``` - -### 七、注意事项 - -**明确需求**:在决定实现 `ImportAware` 之前,请确保您确实需要知道是哪个类导入了您的组件,并且需要访问其注解元数据。避免不必要的复杂性。 - -**正确的上下文**:`ImportAware` 只对通过 `@Import` 导入的类有意义。对于其他方式注册的 beans(例如,通过 component scanning 或 XML 配置),`setImportMetadata` 方法可能不会被调用。 - -**小心处理元数据**:当访问 `AnnotationMetadata` 时,确保处理不存在的注解或属性的情况,以避免空指针异常。 - -**注意与其他 `BeanPostProcessor` 的交互**:`ImportAware` 的功能部分是通过 `BeanPostProcessor` 机制实现的。如果您在应用中使用其他 `BeanPostProcessor`,请确保您了解它们之间的交互和执行顺序。 - -**不要过度使用**:虽然 `ImportAware` 可以带来一些灵活性,但不应在不需要的地方使用它。过度使用可能会导致配置变得复杂且难以追踪。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**初始化与运行**:使用 `AnnotationConfigApplicationContext` 初始化了一个 Spring 上下文,加载了 `MyConfiguration` 配置类,并从上下文中获取了一个类型为 `String` 的 bean。 - -**`@EnableXcs` 注解的作用**:`@EnableXcs` 是一个自定义注解,其主要作用是通过 `@Import` 注解导入 `MyImportAware` 类,从而启动 `ImportAware` 功能。 - -**`MyImportAware` 类与 `ImportAware`**:`MyImportAware` 实现了 `ImportAware` 接口,允许它获取关于导入它的类的注解信息。在 `setImportMetadata` 方法中,`MyImportAware` 会检查导入它的类是否有 `@EnableXcs` 注解。如果存在 `@EnableXcs` 注解,它会继续并注册一个 `String` 类型的 bean,值为 "This is a custom bean!"。如果不存在,它会抛出异常,提示 `@EnableXcs` 注解不存在于导入它的类上。 - -**正常使用**:当 `MyConfiguration` 使用 `@EnableXcs` 注解时,程序可以正常运行,从上下文中获取到的 String 类型的 bean 值为 "This is a custom bean!"。 - -**异常情况**:但如果 `MyConfiguration` 直接使用 `@Import(MyImportAware.class)` 导入 `MyImportAware` 类,而不使用 `@EnableXcs` 注解,会导致 `MyImportAware` 在查找 `@EnableXcs` 注解时发现它不存在,从而抛出异常。 - -#### 8.2、源码分析总结 - -**应用程序启动**: 使用 `AnnotationConfigApplicationContext` 初始化 Spring 上下文,加载 `MyConfiguration` 配置类。程序试图从 Spring 上下文中获取一个类型为 `String` 的 bean。 - -**上下文刷新**: 在构造 `AnnotationConfigApplicationContext` 时,会调用 `refresh()` 方法,这是 Spring 上下文的初始化和刷新过程的入口点。 - -**实例化Beans**: 执行 `finishBeanFactoryInitialization`,该方法负责预实例化上下文中的所有非懒加载单例bean。对于每个bean,它都会调用 `getBean` 方法。 - -**处理 `ImportAware` Beans**: 如果bean实现了 `ImportAware` 接口,`postProcessBeforeInitialization` 方法会为该 bean 设置导入它的类的注解元数据。在我们的例子中,`MyImportAware` 就是这样一个bean。 - -**检查 `@EnableXcs`**: 在 `MyImportAware` 的 `setImportMetadata` 方法中,它会检查导入它的类是否有 `@EnableXcs` 注解。如果存在该注解,则继续处理;如果不存在,则抛出异常。 - -**Bean创建**: 如果导入类上存在 `@EnableXcs` 注解,`MyImportAware` 继续并定义了一个 `String` 类型的 bean。这就是我们从上下文中检索并在控制台上打印的bean。 - +## ImportAware + +- [ImportAware](#importaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`ImportAware` 接口,提供被导入类的访问功能。当一个类实现了 `ImportAware` 接口,并且被通过 @Import 注解导入到其他配置类中,该类可以获得对导入它的 `AnnotationMetadata` 的访问权。 + +### 二、接口源码 + +`ApplicationStartupAware` 是 Spring 框架自 3.1 开始引入的一个核心接口。实现`ImportAware`接口的对象会在Spring容器中被自动注入一个`AnnotationMetadata`实例。 + +```java +/** + * 任何希望被注入其导入它的Configuration类的AnnotationMetadata的Configuration类都应实现此接口。 + * 与使用Import作为元注解的注解结合使用时特别有用。 + * + * @author Chris Beams + * @since 3.1 + */ +public interface ImportAware extends Aware { + + /** + * 设置导入的@Configuration类的注解元数据。 + */ + void setImportMetadata(AnnotationMetadata importMetadata); + +} +``` + +### 三、主要功能 + +**访问导入类的注解元数据**:当一个类实现了 `ImportAware` 接口,并且它是通过 `@Import` 或其他特定方式被导入的,Spring 容器会自动调用它的 `setImportMetadata` 方法,并传入与导入该类的注解相关的 `AnnotationMetadata`。 + +**条件性的行为**:通过访问导入类的注解元数据,可以实现基于特定条件的行为。例如,根据导入类上的注解属性,决定是否注册某个 bean,或者为 bean 设置特定的属性值。 + +**框架和库的开发**:`ImportAware` 在 Spring 框架内部和某些第三方库中被用于执行特定的初始化和配置任务。例如,某些特性的自动配置可能会根据导入它们的配置类上的注解属性进行调整。 + +**增强诊断和调试信息**:可以基于导入类的元数据为我们提供更多的上下文信息,这在诊断复杂的配置问题时可能会很有用。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`String`类型的bean并打印。 + +```java +public class ImportAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + String customBean = context.getBean(String.class); + System.out.println(customBean); + } +} +``` + + `MyConfiguration` 是一个 Spring 配置类,然后通过这个类启用了通过 `@EnableXcs` 注解提供`ImportAware`功能。 + +```java +@Configuration +@EnableXcs +public class MyConfiguration { + +} +``` + +`EnableXcs`是一个注解类,`@Import(MyImportAware.class)`会对 Spring 上下文进行某种配置或修改 + +```java +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Import(MyImportAware.class) +public @interface EnableXcs { + +} +``` + +由于 `MyImportAware` 实现了 `ImportAware`,它会检查导入它的配置类上是否存在 `@EnableXcs` 注解。如果存在,则继续处理并注册 `customBean` 这个 String 类型的 bean 到 Spring 上下文中。如果不存在 `@EnableXcs` 注解,则抛出异常。 + +```java +public class MyImportAware implements ImportAware { + + private AnnotationAttributes enableXcs; + + @Override + public void setImportMetadata(AnnotationMetadata importMetadata) { + this.enableXcs = AnnotationAttributes.fromMap( + importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); + if (this.enableXcs == null) { + throw new IllegalArgumentException( + "@EnableXcs is not present on importing class " + importMetadata.getClassName()); + } + } + + @Bean + public String customBean() { + return "This is a custom bean!"; + } +} +``` + +运行结果发现,当我们在 Spring 上下文中使用 `@EnableXcs` 注解并运行程序时,`MyImportAware` 类会被导入并处理,最后在 Spring 容器中注册一个 String 类型的 bean,其值为 "This is a custom bean!"。 + +```java +This is a custom bean! +``` + +当我们不通过`@EnableXcs` 注解方式去导入`MyImportAware`类,而是直接在MyConfiguration类中导入`@Import(MyImportAware.class)`来看看另外一种情况。 + +```java +@Configuration +@Import(MyImportAware.class) +public class MyConfiguration { + +} +``` + +运行结果发现,当我们直接使用`@Import(MyImportAware.class)`导入`MyImportAware`类而不使用`@EnableXcs`注解时,由于`MyConfiguration`上没有`@EnableXcs`注解,所以`enableXcs`的值为null,由于此时`enableXcs`是null,`MyImportAware`抛出了一个`IllegalArgumentException`异常。 + +```java +Caused by: java.lang.IllegalArgumentException: @EnableXcs is not present on importing class com.xcs.spring.config.MyConfiguration + at com.xcs.spring.config.MyImportAware.setImportMetadata(MyImportAware.java:19) + at org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor.postProcessBeforeInitialization(ConfigurationClassPostProcessor.java:484) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) + ... 10 more +``` + + + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: ImportAware时序图 + participant ImportAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant ImportAwareBeanPostProcessor + participant MyImportAware + + ImportAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses) + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh() + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory) + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons() + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name) + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly) + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory) + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject() + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args) + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args) + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd) + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean,beanName) + AbstractAutowireCapableBeanFactory->>ImportAwareBeanPostProcessor: postProcessBeforeInitialization(bean,beanName) + ImportAwareBeanPostProcessor->>MyImportAware:setImportMetadata(importMetadata)设置importMetadata + ImportAwareBeanPostProcessor-->>AbstractAutowireCapableBeanFactory: 返回Bean对象 + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext->>ImportAwareApplication: 初始化完成 +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`String`类型的bean并打印。 + +```java +public class ImportAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + String customBean = context.getBean(String.class); + System.out.println(customBean); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法。 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,主要负责bean初始化。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean()`方法中,核心逻辑是对`BeanPostProcessors`接口中的`postProcessBeforeInitialization`进行回调。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强。 + +```java +@Override +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} +``` + +在`org.springframework.context.annotation.ConfigurationClassPostProcessor.ImportAwareBeanPostProcessor#postProcessBeforeInitialization`方法中,主要作用是为实现了 `ImportAware` 接口的 beans 设置导入它们的类的 `AnnotationMetadata`。这样,任何实现了 `ImportAware` 接口的 bean 都可以知道它是由哪个类导入的,以及这个导入类上的所有注解信息。 + +```java +@Override +public Object postProcessBeforeInitialization(Object bean, String beanName) { + if (bean instanceof ImportAware) { + ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class); + AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName()); + if (importingClass != null) { + ((ImportAware) bean).setImportMetadata(importingClass); + } + } + return bean; +} +``` + +最后执行到我们自定义的逻辑中,由于 `MyImportAware` 实现了 `ImportAware`,它会检查导入它的配置类上是否存在 `@EnableXcs` 注解。如果存在,则继续处理并注册 `customBean` 这个 String 类型的 bean 到 Spring 上下文中。如果不存在 `@EnableXcs` 注解,则抛出异常。 + +```java +public class MyImportAware implements ImportAware { + + private AnnotationAttributes enableXcs; + + @Override + public void setImportMetadata(AnnotationMetadata importMetadata) { + this.enableXcs = AnnotationAttributes.fromMap( + importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); + if (this.enableXcs == null) { + throw new IllegalArgumentException( + "@EnableXcs is not present on importing class " + importMetadata.getClassName()); + } + } + + @Bean + public String customBean() { + return "This is a custom bean!"; + } +} +``` + +### 七、注意事项 + +**明确需求**:在决定实现 `ImportAware` 之前,请确保您确实需要知道是哪个类导入了您的组件,并且需要访问其注解元数据。避免不必要的复杂性。 + +**正确的上下文**:`ImportAware` 只对通过 `@Import` 导入的类有意义。对于其他方式注册的 beans(例如,通过 component scanning 或 XML 配置),`setImportMetadata` 方法可能不会被调用。 + +**小心处理元数据**:当访问 `AnnotationMetadata` 时,确保处理不存在的注解或属性的情况,以避免空指针异常。 + +**注意与其他 `BeanPostProcessor` 的交互**:`ImportAware` 的功能部分是通过 `BeanPostProcessor` 机制实现的。如果您在应用中使用其他 `BeanPostProcessor`,请确保您了解它们之间的交互和执行顺序。 + +**不要过度使用**:虽然 `ImportAware` 可以带来一些灵活性,但不应在不需要的地方使用它。过度使用可能会导致配置变得复杂且难以追踪。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**初始化与运行**:使用 `AnnotationConfigApplicationContext` 初始化了一个 Spring 上下文,加载了 `MyConfiguration` 配置类,并从上下文中获取了一个类型为 `String` 的 bean。 + +**`@EnableXcs` 注解的作用**:`@EnableXcs` 是一个自定义注解,其主要作用是通过 `@Import` 注解导入 `MyImportAware` 类,从而启动 `ImportAware` 功能。 + +**`MyImportAware` 类与 `ImportAware`**:`MyImportAware` 实现了 `ImportAware` 接口,允许它获取关于导入它的类的注解信息。在 `setImportMetadata` 方法中,`MyImportAware` 会检查导入它的类是否有 `@EnableXcs` 注解。如果存在 `@EnableXcs` 注解,它会继续并注册一个 `String` 类型的 bean,值为 "This is a custom bean!"。如果不存在,它会抛出异常,提示 `@EnableXcs` 注解不存在于导入它的类上。 + +**正常使用**:当 `MyConfiguration` 使用 `@EnableXcs` 注解时,程序可以正常运行,从上下文中获取到的 String 类型的 bean 值为 "This is a custom bean!"。 + +**异常情况**:但如果 `MyConfiguration` 直接使用 `@Import(MyImportAware.class)` 导入 `MyImportAware` 类,而不使用 `@EnableXcs` 注解,会导致 `MyImportAware` 在查找 `@EnableXcs` 注解时发现它不存在,从而抛出异常。 + +#### 8.2、源码分析总结 + +**应用程序启动**: 使用 `AnnotationConfigApplicationContext` 初始化 Spring 上下文,加载 `MyConfiguration` 配置类。程序试图从 Spring 上下文中获取一个类型为 `String` 的 bean。 + +**上下文刷新**: 在构造 `AnnotationConfigApplicationContext` 时,会调用 `refresh()` 方法,这是 Spring 上下文的初始化和刷新过程的入口点。 + +**实例化Beans**: 执行 `finishBeanFactoryInitialization`,该方法负责预实例化上下文中的所有非懒加载单例bean。对于每个bean,它都会调用 `getBean` 方法。 + +**处理 `ImportAware` Beans**: 如果bean实现了 `ImportAware` 接口,`postProcessBeforeInitialization` 方法会为该 bean 设置导入它的类的注解元数据。在我们的例子中,`MyImportAware` 就是这样一个bean。 + +**检查 `@EnableXcs`**: 在 `MyImportAware` 的 `setImportMetadata` 方法中,它会检查导入它的类是否有 `@EnableXcs` 注解。如果存在该注解,则继续处理;如果不存在,则抛出异常。 + +**Bean创建**: 如果导入类上存在 `@EnableXcs` 注解,`MyImportAware` 继续并定义了一个 `String` 类型的 bean。这就是我们从上下文中检索并在控制台上打印的bean。 + **异常处理**: 如果直接使用 `@Import` 导入 `MyImportAware` 而不使用 `@EnableXcs` 注解,会发生异常,因为 `MyImportAware` 期望导入它的类上有 `@EnableXcs` 注解。 \ No newline at end of file diff --git a/spring-aware-importAware/pom.xml b/spring-aware/spring-aware-importAware/pom.xml similarity index 88% rename from spring-aware-importAware/pom.xml rename to spring-aware/spring-aware-importAware/pom.xml index 0d24a82..94dd588 100644 --- a/spring-aware-importAware/pom.xml +++ b/spring-aware/spring-aware-importAware/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-aware com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-aware-importAware/src/main/java/com/xcs/spring/ImportAwareApplication.java b/spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/ImportAwareApplication.java similarity index 97% rename from spring-aware-importAware/src/main/java/com/xcs/spring/ImportAwareApplication.java rename to spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/ImportAwareApplication.java index 1923876..96eca57 100644 --- a/spring-aware-importAware/src/main/java/com/xcs/spring/ImportAwareApplication.java +++ b/spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/ImportAwareApplication.java @@ -1,13 +1,13 @@ -package com.xcs.spring; - -import com.xcs.spring.config.MyConfiguration; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; - -public class ImportAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - String customBean = context.getBean(String.class); - System.out.println(customBean); - } -} +package com.xcs.spring; + +import com.xcs.spring.config.MyConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class ImportAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + String customBean = context.getBean(String.class); + System.out.println(customBean); + } +} diff --git a/spring-aware-importAware/src/main/java/com/xcs/spring/annotation/EnableXcs.java b/spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/annotation/EnableXcs.java similarity index 95% rename from spring-aware-importAware/src/main/java/com/xcs/spring/annotation/EnableXcs.java rename to spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/annotation/EnableXcs.java index c23ed9e..b486a61 100644 --- a/spring-aware-importAware/src/main/java/com/xcs/spring/annotation/EnableXcs.java +++ b/spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/annotation/EnableXcs.java @@ -1,13 +1,13 @@ -package com.xcs.spring.annotation; - -import com.xcs.spring.config.MyImportAware; -import org.springframework.context.annotation.Import; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Documented -@Import(MyImportAware.class) -public @interface EnableXcs { -} +package com.xcs.spring.annotation; + +import com.xcs.spring.config.MyImportAware; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Import(MyImportAware.class) +public @interface EnableXcs { +} diff --git a/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 95% rename from spring-aware-importAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyConfiguration.java index af43047..8a43663 100644 --- a/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyConfiguration.java +++ b/spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyConfiguration.java @@ -1,10 +1,10 @@ -package com.xcs.spring.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; - -@Configuration -@Import(MyImportAware.class) -public class MyConfiguration { - -} +package com.xcs.spring.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import(MyImportAware.class) +public class MyConfiguration { + +} diff --git a/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyImportAware.java b/spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyImportAware.java similarity index 97% rename from spring-aware-importAware/src/main/java/com/xcs/spring/config/MyImportAware.java rename to spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyImportAware.java index 3acf67d..65acf14 100644 --- a/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyImportAware.java +++ b/spring-aware/spring-aware-importAware/src/main/java/com/xcs/spring/config/MyImportAware.java @@ -1,27 +1,27 @@ -package com.xcs.spring.config; - -import com.xcs.spring.annotation.EnableXcs; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ImportAware; -import org.springframework.core.annotation.AnnotationAttributes; -import org.springframework.core.type.AnnotationMetadata; - -public class MyImportAware implements ImportAware { - - private AnnotationAttributes enableXcs; - - @Override - public void setImportMetadata(AnnotationMetadata importMetadata) { - this.enableXcs = AnnotationAttributes.fromMap( - importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); - if (this.enableXcs == null) { - throw new IllegalArgumentException( - "@EnableXcs is not present on importing class " + importMetadata.getClassName()); - } - } - - @Bean - public String customBean() { - return "This is a custom bean!"; - } -} +package com.xcs.spring.config; + +import com.xcs.spring.annotation.EnableXcs; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ImportAware; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; + +public class MyImportAware implements ImportAware { + + private AnnotationAttributes enableXcs; + + @Override + public void setImportMetadata(AnnotationMetadata importMetadata) { + this.enableXcs = AnnotationAttributes.fromMap( + importMetadata.getAnnotationAttributes(EnableXcs.class.getName(), false)); + if (this.enableXcs == null) { + throw new IllegalArgumentException( + "@EnableXcs is not present on importing class " + importMetadata.getClassName()); + } + } + + @Bean + public String customBean() { + return "This is a custom bean!"; + } +} diff --git a/spring-aware-messageSourceAware/README.md b/spring-aware/spring-aware-messageSourceAware/README.md similarity index 98% rename from spring-aware-messageSourceAware/README.md rename to spring-aware/spring-aware-messageSourceAware/README.md index c54528b..89a2a79 100644 --- a/spring-aware-messageSourceAware/README.md +++ b/spring-aware/spring-aware-messageSourceAware/README.md @@ -1,519 +1,519 @@ -## MessageSourceAware - -- [MessageSourceAware](#messagesourceaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`MessageSourceAware` 接口,主要用于对象希望被注入`MessageSource`。`MessageSource`是Spring中用于国际化(i18n)的接口,它提供了从不同的消息资源(例如:属性文件)获取消息的方法。使用`MessageSource`,你可以为应用程序提供国际化的消息支持。 - -### 二、接口源码 - -`MessageSourceAware` 是 Spring 框架自 1.1.1 开始引入的一个核心接口。实现`MessageSourceAware`接口的对象会在Spring容器中被自动注入一个`MessageSource`实例。 - -```java -/** - * 任何希望被通知运行其中的MessageSource(通常是ApplicationContext)的对象需要实现的接口。 - * - * 注意,MessageSource通常也可以作为bean引用传递 - * (到任意的bean属性或构造函数参数),因为它在应用上下文中通常是以"name"为messageSource的bean定义的。 - * - * 作者: Juergen Hoeller, Chris Beams - * 版本: 1.1.1 - * 参见: ApplicationContextAware - */ -public interface MessageSourceAware extends Aware { - - /** - * 设置此对象运行的MessageSource。 - * 此方法在常规bean属性被填充之后调用,但在初始化回调(如InitializingBean的afterPropertiesSet或自定义的init-method)之前调用。 - * 此方法在ApplicationContextAware的setApplicationContext方法之前被调用。 - * - * @param messageSource 此对象要使用的消息源 - */ - void setMessageSource(MessageSource messageSource); - -} -``` - -### 三、主要功能 - -**自动注入**:当一个bean实现了`MessageSourceAware`接口,并且被Spring容器管理时,Spring将会自动调用该bean的`setMessageSource`方法,传入当前应用上下文的`MessageSource`实例。 - -**国际化支持**:通过`MessageSourceAware`,beans可以获得对`MessageSource`的访问权,从而可以根据不同的地区和语言获取相应的消息。这对于需要显示不同语言的错误消息、UI标签或其他用户面向的文本的beans特别有用。 - -**简化配置**:虽然我们可以通过常规的依赖注入方法将`MessageSource`注入到beans中,但`MessageSourceAware`提供了一种更加自动化和明确的方法,特别是当我们的bean需要在初始化过程的特定阶段获得`MessageSource`时。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyMessageSourceAware`类型的bean,最后调用`getMessage`方法。 - -```java -public class MessageSourceAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class); - messageSourceAware.getMessage(); - } -} -``` - -这里使用`@Bean`注解,定义了两个Bean,是为了确保 `MyMessageSourceAware` ,`MessageSource`被 Spring 容器执行。其中`ResourceBundleMessageSource` 是 Spring 框架中用于国际化(i18n)的一个具体实现。它为应用程序提供了从属性文件中读取国际化消息的能力。 - -```java -@Configuration -public class MyConfiguration { - - @Bean - public MyMessageSourceAware myMessageSourceAware(){ - return new MyMessageSourceAware(); - } - - @Bean - public MessageSource messageSource() { - ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); - messageSource.setBasename("i18n/messages"); - return messageSource; - } -} -``` - -`MyMessageSourceAware`类使用`MessageSourceAware`接口来自动获得对`MessageSource`的引用。这个引用可以用来根据不同的语言或地区检索国际化的消息。然后利用注入的`MessageSource`,从属性文件中检索并打印两个国际化的消息,一个是英文的,另一个是简体中文的。 - -```java -public class MyMessageSourceAware implements MessageSourceAware { - - private MessageSource messageSource; - - @Override - public void setMessageSource(MessageSource messageSource) { - this.messageSource = messageSource; - } - - public void getMessage() { - System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); - System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); - } -} -``` - -运行结果发现,`MyMessageSourceAware`类已成功从属性文件中获取了国际化消息。 - -```java -English:Hello! -中文:你好 -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: EnvironmentAware时序图 - participant MessageSourceAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant ApplicationContextAwareProcessor - participant MyMessageSourceAware - - MessageSourceAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 - AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 - ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 - ApplicationContextAwareProcessor->>MyMessageSourceAware:setMessageSource(messageSource)
设置messageSource - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>MessageSourceAwareApplication:初始化完成 -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyMessageSourceAware`类型的bean,最后调用`getMessage`方法。 - -```java -public class MessageSourceAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class); - messageSourceAware.getMessage(); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - - // 步骤1. Initialize message source for this context. - initMessageSource(); - - // 步骤2. Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -我们来到`org.springframework.context.support.AbstractApplicationContext#refresh`方法中的步骤1,在`org.springframework.context.support.AbstractApplicationContext#initMessageSource`方法中,这个方法确保Spring应用上下文总是有一个`MessageSource` bean可用,无论是否明确定义了它。如果用户没有定义,它会提供一个默认实现。这意味着在Spring上下文中,你总是可以安全地调用`getMessage()`,因为总会有一个`MessageSource`可用。 - -```java -protected void initMessageSource() { - // 获取Bean工厂 - ConfigurableListableBeanFactory beanFactory = getBeanFactory(); - // 检查是否已经存在名为MESSAGE_SOURCE_BEAN_NAME的bean - if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { - // 如果存在,则获取该bean - this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); - // 如果当前MessageSource具有层次结构并且没有设置父MessageSource - if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) { - HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource; - if (hms.getParentMessageSource() == null) { - // 设置父上下文作为父MessageSource(如果之前没有注册过父MessageSource) - hms.setParentMessageSource(getInternalParentMessageSource()); - } - } - // ... [代码部分省略以简化] - } - else { - // 如果不存在MESSAGE_SOURCE_BEAN_NAME的bean,则创建一个DelegatingMessageSource并注册到上下文 - // 使用DelegatingMessageSource以便能够处理getMessage调用 - DelegatingMessageSource dms = new DelegatingMessageSource(); - dms.setParentMessageSource(getInternalParentMessageSource()); - this.messageSource = dms; - beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource); - // ... [代码部分省略以简化] - } -} -``` - -我们来到`org.springframework.context.support.AbstractApplicationContext#refresh`方法中的步骤2,在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - Object wrappedBean = bean; - if (mbd == null || !mbd.isSynthetic()) { - wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); - } - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 - -```java -@Override -public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) - throws BeansException { - - Object result = existingBean; - for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; - } - result = current; - } - return result; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 - -```java -@Override -@Nullable -public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || - bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || - bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || - bean instanceof ApplicationStartupAware)) { - return bean; - } - - // ... [代码部分省略以简化] - - invokeAwareInterfaces(bean); - - return bean; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 - -```java -private void invokeAwareInterfaces(Object bean) { - // ... [代码部分省略以简化] - if (bean instanceof MessageSourceAware) { - ((MessageSourceAware) bean).setMessageSource(this.applicationContext); - } - // ... [代码部分省略以简化] -} -``` - -最后执行到我们自定义的逻辑中,`MyMessageSourceAware`类使用`MessageSourceAware`接口来自动获得对`MessageSource`的引用。这个引用可以用来根据不同的语言或地区检索国际化的消息。然后利用注入的`MessageSource`,从属性文件中检索并打印两个国际化的消息,一个是英文的,另一个是简体中文的。 - -```java -public class MyMessageSourceAware implements MessageSourceAware { - - private MessageSource messageSource; - - @Override - public void setMessageSource(MessageSource messageSource) { - this.messageSource = messageSource; - } - - public void getMessage() { - System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); - System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); - } -} -``` - -### 七、注意事项 - -**明确的配置**: 确保你的Spring上下文中有一个`MessageSource` bean,通常命名为“messageSource”。虽然Spring提供了一个默认的,但为了满足自定义需求,你可能需要明确地配置它。 - -**生命周期时机**: `MessageSourceAware`的`setMessageSource`方法在常规属性设置之后和初始化方法(如`InitializingBean`的`afterPropertiesSet`或任何自定义的init方法)之前被调用。确保你的bean不在其生命周期的早期阶段(例如,在构造函数中)期望使用`MessageSource`。 - -**文件位置和命名**: 如果你使用`ResourceBundleMessageSource`或类似的机制,确保你的属性文件位于类路径上,并且与你在`MessageSource`配置中指定的basename匹配。 - -**编码问题**: 属性文件默认使用ISO-8859-1编码。如果你的消息包含非此编码的字符(例如中文、俄文等),确保使用Unicode转义或正确设置文件的编码。 - -**父子上下文**: 在使用Spring的父子上下文(例如,在Web应用中)时,子上下文可以访问父上下文中的`MessageSource`,但反之则不行。确保你在正确的上下文中配置了`MessageSource`。 - -**避免硬编码**: 尽量不要在代码中硬编码消息键或默认消息。最好在属性文件中管理它们,这样在未来需要更改或添加新的语言支持时,你不需要修改代码。 - -**默认消息**: 当使用`MessageSource`检索消息时,考虑提供一个默认消息。这可以在未找到特定消息时提供一个后备,避免抛出异常。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**启动类**: 在`MessageSourceAwareApplication`类中,使用了`AnnotationConfigApplicationContext`来启动Spring应用。这个上下文是专为基于Java注解的配置而设计的。启动时,它加载了`MyConfiguration`配置类,并从上下文中获取了`MyMessageSourceAware`bean,随后调用了`getMessage`方法显示消息。 - -**配置类**: `MyConfiguration`是一个基于Java的Spring配置类,其中定义了两个bean:`MyMessageSourceAware`和`messageSource`。`messageSource` bean是一个`ResourceBundleMessageSource`实例,用于从`i18n/messages`基本名称的属性文件中读取国际化消息。 - -**实现MessageSourceAware接口**: `MyMessageSourceAware`类实现了`MessageSourceAware`接口,这意味着Spring容器会自动注入一个`MessageSource`实例到这个bean中。这是通过`setMessageSource`方法完成的。 - -**消息检索**: 在`MyMessageSourceAware`的`getMessage`方法中,使用了注入的`MessageSource`来检索和打印两种语言的国际化消息:英文和简体中文。 - -**运行结果**: 当应用程序执行时,它成功地从对应的属性文件中获取并显示了英文和简体中文的国际化消息。 - -#### 8.2、源码分析总结 - -**应用启动**:你从`MessageSourceAwareApplication`启动应用,使用`AnnotationConfigApplicationContext`初始化Spring容器,并加载`MyConfiguration`配置。 - -**容器初始化**:在`AnnotationConfigApplicationContext`的构造函数中,执行了`register`和`refresh`方法,其中`refresh`是最重要的,它触发了容器的初始化和bean的创建过程。 - -**消息源初始化**:在容器刷新的`refresh`方法中,首先确保了一个`MessageSource` bean存在,这是通过`initMessageSource`方法完成的。如果没有明确定义`MessageSource` bean,Spring会提供一个默认实现,确保应用上下文总是有一个可用。 - -**bean实例化**:随后,在`refresh`方法中,通过调用`finishBeanFactoryInitialization`方法,容器开始实例化所有非延迟加载的单例bean。 - -**Bean的生命周期**:在bean的创建过程中,Spring容器会确保所有的生命周期回调都被正确地执行,其中最重要的是`BeanPostProcessors`。这些处理器提供了一个插件机制,允许我们在bean的初始化前后执行自定义的逻辑。 - -**处理Aware接口**:`ApplicationContextAwareProcessor`是一个特殊的`BeanPostProcessor`,它关心那些实现了"Aware"接口的beans。对于实现了`MessageSourceAware`的beans,该处理器会自动注入应用上下文的`MessageSource`。 - +## MessageSourceAware + +- [MessageSourceAware](#messagesourceaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`MessageSourceAware` 接口,主要用于对象希望被注入`MessageSource`。`MessageSource`是Spring中用于国际化(i18n)的接口,它提供了从不同的消息资源(例如:属性文件)获取消息的方法。使用`MessageSource`,你可以为应用程序提供国际化的消息支持。 + +### 二、接口源码 + +`MessageSourceAware` 是 Spring 框架自 1.1.1 开始引入的一个核心接口。实现`MessageSourceAware`接口的对象会在Spring容器中被自动注入一个`MessageSource`实例。 + +```java +/** + * 任何希望被通知运行其中的MessageSource(通常是ApplicationContext)的对象需要实现的接口。 + * + * 注意,MessageSource通常也可以作为bean引用传递 + * (到任意的bean属性或构造函数参数),因为它在应用上下文中通常是以"name"为messageSource的bean定义的。 + * + * 作者: Juergen Hoeller, Chris Beams + * 版本: 1.1.1 + * 参见: ApplicationContextAware + */ +public interface MessageSourceAware extends Aware { + + /** + * 设置此对象运行的MessageSource。 + * 此方法在常规bean属性被填充之后调用,但在初始化回调(如InitializingBean的afterPropertiesSet或自定义的init-method)之前调用。 + * 此方法在ApplicationContextAware的setApplicationContext方法之前被调用。 + * + * @param messageSource 此对象要使用的消息源 + */ + void setMessageSource(MessageSource messageSource); + +} +``` + +### 三、主要功能 + +**自动注入**:当一个bean实现了`MessageSourceAware`接口,并且被Spring容器管理时,Spring将会自动调用该bean的`setMessageSource`方法,传入当前应用上下文的`MessageSource`实例。 + +**国际化支持**:通过`MessageSourceAware`,beans可以获得对`MessageSource`的访问权,从而可以根据不同的地区和语言获取相应的消息。这对于需要显示不同语言的错误消息、UI标签或其他用户面向的文本的beans特别有用。 + +**简化配置**:虽然我们可以通过常规的依赖注入方法将`MessageSource`注入到beans中,但`MessageSourceAware`提供了一种更加自动化和明确的方法,特别是当我们的bean需要在初始化过程的特定阶段获得`MessageSource`时。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyMessageSourceAware`类型的bean,最后调用`getMessage`方法。 + +```java +public class MessageSourceAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class); + messageSourceAware.getMessage(); + } +} +``` + +这里使用`@Bean`注解,定义了两个Bean,是为了确保 `MyMessageSourceAware` ,`MessageSource`被 Spring 容器执行。其中`ResourceBundleMessageSource` 是 Spring 框架中用于国际化(i18n)的一个具体实现。它为应用程序提供了从属性文件中读取国际化消息的能力。 + +```java +@Configuration +public class MyConfiguration { + + @Bean + public MyMessageSourceAware myMessageSourceAware(){ + return new MyMessageSourceAware(); + } + + @Bean + public MessageSource messageSource() { + ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); + messageSource.setBasename("i18n/messages"); + return messageSource; + } +} +``` + +`MyMessageSourceAware`类使用`MessageSourceAware`接口来自动获得对`MessageSource`的引用。这个引用可以用来根据不同的语言或地区检索国际化的消息。然后利用注入的`MessageSource`,从属性文件中检索并打印两个国际化的消息,一个是英文的,另一个是简体中文的。 + +```java +public class MyMessageSourceAware implements MessageSourceAware { + + private MessageSource messageSource; + + @Override + public void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource; + } + + public void getMessage() { + System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); + System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); + } +} +``` + +运行结果发现,`MyMessageSourceAware`类已成功从属性文件中获取了国际化消息。 + +```java +English:Hello! +中文:你好 +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: EnvironmentAware时序图 + participant MessageSourceAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant ApplicationContextAwareProcessor + participant MyMessageSourceAware + + MessageSourceAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 + AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 + ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 + ApplicationContextAwareProcessor->>MyMessageSourceAware:setMessageSource(messageSource)
设置messageSource + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>MessageSourceAwareApplication:初始化完成 +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyMessageSourceAware`类型的bean,最后调用`getMessage`方法。 + +```java +public class MessageSourceAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyMessageSourceAware messageSourceAware = context.getBean(MyMessageSourceAware.class); + messageSourceAware.getMessage(); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + + // 步骤1. Initialize message source for this context. + initMessageSource(); + + // 步骤2. Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +我们来到`org.springframework.context.support.AbstractApplicationContext#refresh`方法中的步骤1,在`org.springframework.context.support.AbstractApplicationContext#initMessageSource`方法中,这个方法确保Spring应用上下文总是有一个`MessageSource` bean可用,无论是否明确定义了它。如果用户没有定义,它会提供一个默认实现。这意味着在Spring上下文中,你总是可以安全地调用`getMessage()`,因为总会有一个`MessageSource`可用。 + +```java +protected void initMessageSource() { + // 获取Bean工厂 + ConfigurableListableBeanFactory beanFactory = getBeanFactory(); + // 检查是否已经存在名为MESSAGE_SOURCE_BEAN_NAME的bean + if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { + // 如果存在,则获取该bean + this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); + // 如果当前MessageSource具有层次结构并且没有设置父MessageSource + if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) { + HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource; + if (hms.getParentMessageSource() == null) { + // 设置父上下文作为父MessageSource(如果之前没有注册过父MessageSource) + hms.setParentMessageSource(getInternalParentMessageSource()); + } + } + // ... [代码部分省略以简化] + } + else { + // 如果不存在MESSAGE_SOURCE_BEAN_NAME的bean,则创建一个DelegatingMessageSource并注册到上下文 + // 使用DelegatingMessageSource以便能够处理getMessage调用 + DelegatingMessageSource dms = new DelegatingMessageSource(); + dms.setParentMessageSource(getInternalParentMessageSource()); + this.messageSource = dms; + beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource); + // ... [代码部分省略以简化] + } +} +``` + +我们来到`org.springframework.context.support.AbstractApplicationContext#refresh`方法中的步骤2,在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 + +```java +@Override +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 + +```java +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || + bean instanceof ApplicationStartupAware)) { + return bean; + } + + // ... [代码部分省略以简化] + + invokeAwareInterfaces(bean); + + return bean; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 + +```java +private void invokeAwareInterfaces(Object bean) { + // ... [代码部分省略以简化] + if (bean instanceof MessageSourceAware) { + ((MessageSourceAware) bean).setMessageSource(this.applicationContext); + } + // ... [代码部分省略以简化] +} +``` + +最后执行到我们自定义的逻辑中,`MyMessageSourceAware`类使用`MessageSourceAware`接口来自动获得对`MessageSource`的引用。这个引用可以用来根据不同的语言或地区检索国际化的消息。然后利用注入的`MessageSource`,从属性文件中检索并打印两个国际化的消息,一个是英文的,另一个是简体中文的。 + +```java +public class MyMessageSourceAware implements MessageSourceAware { + + private MessageSource messageSource; + + @Override + public void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource; + } + + public void getMessage() { + System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); + System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); + } +} +``` + +### 七、注意事项 + +**明确的配置**: 确保你的Spring上下文中有一个`MessageSource` bean,通常命名为“messageSource”。虽然Spring提供了一个默认的,但为了满足自定义需求,你可能需要明确地配置它。 + +**生命周期时机**: `MessageSourceAware`的`setMessageSource`方法在常规属性设置之后和初始化方法(如`InitializingBean`的`afterPropertiesSet`或任何自定义的init方法)之前被调用。确保你的bean不在其生命周期的早期阶段(例如,在构造函数中)期望使用`MessageSource`。 + +**文件位置和命名**: 如果你使用`ResourceBundleMessageSource`或类似的机制,确保你的属性文件位于类路径上,并且与你在`MessageSource`配置中指定的basename匹配。 + +**编码问题**: 属性文件默认使用ISO-8859-1编码。如果你的消息包含非此编码的字符(例如中文、俄文等),确保使用Unicode转义或正确设置文件的编码。 + +**父子上下文**: 在使用Spring的父子上下文(例如,在Web应用中)时,子上下文可以访问父上下文中的`MessageSource`,但反之则不行。确保你在正确的上下文中配置了`MessageSource`。 + +**避免硬编码**: 尽量不要在代码中硬编码消息键或默认消息。最好在属性文件中管理它们,这样在未来需要更改或添加新的语言支持时,你不需要修改代码。 + +**默认消息**: 当使用`MessageSource`检索消息时,考虑提供一个默认消息。这可以在未找到特定消息时提供一个后备,避免抛出异常。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**启动类**: 在`MessageSourceAwareApplication`类中,使用了`AnnotationConfigApplicationContext`来启动Spring应用。这个上下文是专为基于Java注解的配置而设计的。启动时,它加载了`MyConfiguration`配置类,并从上下文中获取了`MyMessageSourceAware`bean,随后调用了`getMessage`方法显示消息。 + +**配置类**: `MyConfiguration`是一个基于Java的Spring配置类,其中定义了两个bean:`MyMessageSourceAware`和`messageSource`。`messageSource` bean是一个`ResourceBundleMessageSource`实例,用于从`i18n/messages`基本名称的属性文件中读取国际化消息。 + +**实现MessageSourceAware接口**: `MyMessageSourceAware`类实现了`MessageSourceAware`接口,这意味着Spring容器会自动注入一个`MessageSource`实例到这个bean中。这是通过`setMessageSource`方法完成的。 + +**消息检索**: 在`MyMessageSourceAware`的`getMessage`方法中,使用了注入的`MessageSource`来检索和打印两种语言的国际化消息:英文和简体中文。 + +**运行结果**: 当应用程序执行时,它成功地从对应的属性文件中获取并显示了英文和简体中文的国际化消息。 + +#### 8.2、源码分析总结 + +**应用启动**:你从`MessageSourceAwareApplication`启动应用,使用`AnnotationConfigApplicationContext`初始化Spring容器,并加载`MyConfiguration`配置。 + +**容器初始化**:在`AnnotationConfigApplicationContext`的构造函数中,执行了`register`和`refresh`方法,其中`refresh`是最重要的,它触发了容器的初始化和bean的创建过程。 + +**消息源初始化**:在容器刷新的`refresh`方法中,首先确保了一个`MessageSource` bean存在,这是通过`initMessageSource`方法完成的。如果没有明确定义`MessageSource` bean,Spring会提供一个默认实现,确保应用上下文总是有一个可用。 + +**bean实例化**:随后,在`refresh`方法中,通过调用`finishBeanFactoryInitialization`方法,容器开始实例化所有非延迟加载的单例bean。 + +**Bean的生命周期**:在bean的创建过程中,Spring容器会确保所有的生命周期回调都被正确地执行,其中最重要的是`BeanPostProcessors`。这些处理器提供了一个插件机制,允许我们在bean的初始化前后执行自定义的逻辑。 + +**处理Aware接口**:`ApplicationContextAwareProcessor`是一个特殊的`BeanPostProcessor`,它关心那些实现了"Aware"接口的beans。对于实现了`MessageSourceAware`的beans,该处理器会自动注入应用上下文的`MessageSource`。 + **消息检索**:在你的`MyMessageSourceAware`类中,已经成功地获取了`MessageSource`的引用。然后,你调用其`getMessage`方法,从属性文件中检索并打印两个国际化的消息。 \ No newline at end of file diff --git a/spring-aware-messageSourceAware/pom.xml b/spring-aware/spring-aware-messageSourceAware/pom.xml similarity index 89% rename from spring-aware-messageSourceAware/pom.xml rename to spring-aware/spring-aware-messageSourceAware/pom.xml index 938e1a3..49a57bb 100644 --- a/spring-aware-messageSourceAware/pom.xml +++ b/spring-aware/spring-aware-messageSourceAware/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-aware com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/MessageSourceAwareApplication.java b/spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/MessageSourceAwareApplication.java similarity index 100% rename from spring-aware-messageSourceAware/src/main/java/com/xcs/spring/MessageSourceAwareApplication.java rename to spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/MessageSourceAwareApplication.java diff --git a/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyMessageSourceAware.java b/spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyMessageSourceAware.java similarity index 96% rename from spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyMessageSourceAware.java rename to spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyMessageSourceAware.java index 7778281..81c6cc4 100644 --- a/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyMessageSourceAware.java +++ b/spring-aware/spring-aware-messageSourceAware/src/main/java/com/xcs/spring/config/MyMessageSourceAware.java @@ -1,21 +1,21 @@ -package com.xcs.spring.config; - -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceAware; - -import java.util.Locale; - -public class MyMessageSourceAware implements MessageSourceAware { - - private MessageSource messageSource; - - @Override - public void setMessageSource(MessageSource messageSource) { - this.messageSource = messageSource; - } - - public void getMessage() { - System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); - System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); - } -} +package com.xcs.spring.config; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; + +import java.util.Locale; + +public class MyMessageSourceAware implements MessageSourceAware { + + private MessageSource messageSource; + + @Override + public void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource; + } + + public void getMessage() { + System.out.println("English:"+messageSource.getMessage("greeting", null, Locale.ENGLISH)); + System.out.println("中文:"+messageSource.getMessage("greeting", null, Locale.SIMPLIFIED_CHINESE)); + } +} diff --git a/spring-aware-messageSourceAware/src/main/resources/i18n/messages_en.properties b/spring-aware/spring-aware-messageSourceAware/src/main/resources/i18n/messages_en.properties similarity index 100% rename from spring-aware-messageSourceAware/src/main/resources/i18n/messages_en.properties rename to spring-aware/spring-aware-messageSourceAware/src/main/resources/i18n/messages_en.properties diff --git a/spring-aware-messageSourceAware/src/main/resources/i18n/messages_zh_CN.properties b/spring-aware/spring-aware-messageSourceAware/src/main/resources/i18n/messages_zh_CN.properties similarity index 100% rename from spring-aware-messageSourceAware/src/main/resources/i18n/messages_zh_CN.properties rename to spring-aware/spring-aware-messageSourceAware/src/main/resources/i18n/messages_zh_CN.properties diff --git a/spring-aware-resourceLoaderAware/README.md b/spring-aware/spring-aware-resourceLoaderAware/README.md similarity index 97% rename from spring-aware-resourceLoaderAware/README.md rename to spring-aware/spring-aware-resourceLoaderAware/README.md index f78f59b..c57f2c2 100644 --- a/spring-aware-resourceLoaderAware/README.md +++ b/spring-aware/spring-aware-resourceLoaderAware/README.md @@ -1,505 +1,505 @@ -## ResourceLoaderAware - -- [ResourceLoaderAware](#resourceloaderaware) - - [一、接口描述](#一接口描述) - - [二、接口源码](#二接口源码) - - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、时序图](#五时序图) - - [六、源码分析](#六源码分析) - - [七、注意事项](#七注意事项) - - [八、总结](#八总结) - - [8.1、最佳实践总结](#81最佳实践总结) - - [8.2、源码分析总结](#82源码分析总结) - -### 一、接口描述 - -`ResourceLoaderAware` 接口,它用于为需要访问 `ResourceLoader` 的 bean 提供一个回调。`ResourceLoader` 是一个简单的策略接口,定义了如何加载底层资源(如类路径或文件系统资源)的方法。 - -### 二、接口源码 - -`ResourceLoaderAware` 是 Spring 框架自 10.03.2004 开始引入的一个核心接口。主要是希望被通知并访问 `ResourceLoader` 的对象提供了一个机制。实现此接口的对象可以获取到 `ResourceLoader`,从而加载资源。 - -```java -/** - * 任何希望被通知 ResourceLoader(通常是 ApplicationContext)的对象都应实现此接口。 - * 这是通过 org.springframework.context.ApplicationContextAware 接口完全依赖 ApplicationContext 的另一种方式。 - * - * 请注意,org.springframework.core.io.Resource 依赖也可以暴露为类型为 Resource 或 Resource[] 的bean属性, - * 通过字符串在bean工厂中进行自动类型转换进行填充。这样就消除了为了访问特定文件资源而实现任何回调接口的需求。 - * - * 当您的应用对象需要访问其名称经过计算的各种文件资源时,通常需要一个 ResourceLoader。 - * 一个好策略是使对象使用 org.springframework.core.io.DefaultResourceLoader,但仍然实现 ResourceLoaderAware, - * 以允许在 ApplicationContext 中运行时覆盖。参考 org.springframework.context.support.ReloadableResourceBundleMessageSource 为例。 - * - * 传入的 ResourceLoader 也可以检查是否为 org.springframework.core.io.support.ResourcePatternResolver 接口 - * 并相应地进行类型转换,以将资源模式解析为 Resource 对象的数组。在 ApplicationContext 中运行时,这总是可行的 - * (因为 context 接口扩展了 ResourcePatternResolver 接口)。默认情况下,使用 org.springframework.core.io.support.PathMatchingResourcePatternResolver; - * 也可以查看 ResourcePatternUtils.getResourcePatternResolver 方法。 - * - * 作为 ResourcePatternResolver 依赖的替代,考虑暴露类型为 Resource[] 的 bean 属性,通过模式字符串 - * 在bean工厂的绑定时间进行自动类型转换进行填充。 - * - * @author Juergen Hoeller - * @author Chris Beams - * @since 10.03.2004 - * @see ApplicationContextAware - * @see org.springframework.core.io.Resource - * @see org.springframework.core.io.ResourceLoader - * @see org.springframework.core.io.support.ResourcePatternResolver - */ -public interface ResourceLoaderAware extends Aware { - - /** - * 设置此对象运行的 ResourceLoader。 - * 可能是一个 ResourcePatternResolver,可以通过 instanceof ResourcePatternResolver 检查。 - * 也可以查看 ResourcePatternUtils.getResourcePatternResolver 方法。 - * 在填充正常的bean属性之后但在像 InitializingBean 的 afterPropertiesSet 这样的初始化回调或自定义初始化方法之前被调用。 - * 在 ApplicationContextAware 的 setApplicationContext 之前调用。 - * @param resourceLoader 此对象要使用的 ResourceLoader - * @see org.springframework.core.io.support.ResourcePatternResolver - * @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver - */ - void setResourceLoader(ResourceLoader resourceLoader); - -} -``` - -### 三、主要功能 - -**资源加载回调**:当 bean 实现了 `ResourceLoaderAware` 接口,Spring 容器会在该 bean 初始化时,自动将一个 `ResourceLoader` 注入到该 bean 中,从而使得 bean 可以加载资源。 - -**提供资源加载策略**:通过 `ResourceLoader`, bean 可以加载各种类型的资源,如类路径资源、文件系统资源、URL 资源等。它为资源访问提供了一个统一的策略。 - -**减少对 ApplicationContext 的直接依赖**:虽然 `ApplicationContext` 也扩展了 `ResourceLoader` 的功能,但有时候 bean 只需要资源加载功能,而不需要其他的 ApplicationContext 功能。通过实现 `ResourceLoaderAware`,bean 可以只获得资源加载功能,从而降低与完整的 `ApplicationContext` 的耦合。 - -### 四、最佳实践 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyResourceLoaderAware`类型的bean,最后调用`getResource`方法并传递了一个路径。 - -```java -public class ResourceLoaderAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyResourceLoaderAware resourceLoaderAware = context.getBean(MyResourceLoaderAware.class); - resourceLoaderAware.getResource("classpath:xcs.txt"); - } -} -``` - -这里使用`@Bean`注解,定义了以个Bean,是为了确保 `MyResourceLoaderAware` 被 Spring 容器执行。 - -```java -@Configuration -public class MyConfiguration { - - @Bean - public MyResourceLoaderAware myResourceLoaderAware(){ - return new MyResourceLoaderAware(); - } -} -``` - -`MyResourceLoaderAware` 类是一个简单的实用工具,我们利用 Spring 的 `ResourceLoader` 机制,可以用于加载和打印资源内容。 - -```java -public class MyResourceLoaderAware implements ResourceLoaderAware { - - private ResourceLoader resourceLoader; - - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - this.resourceLoader = resourceLoader; - } - - public void getResource(String location){ - try { - Resource resource = resourceLoader.getResource(location); - System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); - } catch (IOException e) { - e.printStackTrace(); - } - } -} -``` - -运行结果发现, `MyResourceLoaderAware` 类成功地读取了资源文件的内容并将其打印到了控制台。 - -```java -Resource content: hello world -``` - -### 五、时序图 - -~~~mermaid -sequenceDiagram - Title: EnvironmentAware时序图 - participant ResourceLoaderAwareApplication - participant AnnotationConfigApplicationContext - participant AbstractApplicationContext - participant DefaultListableBeanFactory - participant AbstractBeanFactory - participant DefaultSingletonBeanRegistry - participant AbstractAutowireCapableBeanFactory - participant ApplicationContextAwareProcessor - participant MyResourceLoaderAware - - ResourceLoaderAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 - AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 - AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 - AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 - DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean - AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean - AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean - DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 - AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 - AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 - AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 - ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 - ApplicationContextAwareProcessor->>MyResourceLoaderAware:setResourceLoader(resourceLoader)
设置resourceLoader - AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 - AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 - AnnotationConfigApplicationContext-->>ResourceLoaderAwareApplication:初始化完成 -~~~ - -### 六、源码分析 - -首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyResourceLoaderAware`类型的bean,最后调用`getResource`方法并传递了一个路径。 - -```java -public class ResourceLoaderAwareApplication { - - public static void main(String[] args) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); - MyResourceLoaderAware resourceLoaderAware = context.getBean(MyResourceLoaderAware.class); - resourceLoaderAware.getResource("classpath:xcs.txt"); - } -} -``` - -在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 - -```java -public AnnotationConfigApplicationContext(Class... componentClasses) { - this(); - register(componentClasses); - refresh(); -``` - -在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 - -```java -@Override -public void refresh() throws BeansException, IllegalStateException { - // ... [代码部分省略以简化] - // Instantiate all remaining (non-lazy-init) singletons. - finishBeanFactoryInitialization(beanFactory); - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 - -```java -/** - * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 - * - * @param beanFactory 要初始化的bean工厂 - */ -protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - // ... [代码部分省略以简化] - // 完成所有剩余非懒加载的单列Bean对象。 - beanFactory.preInstantiateSingletons(); -} -``` - -在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 - -```java -public void preInstantiateSingletons() throws BeansException { - // ... [代码部分省略以简化] - // 循环遍历所有bean的名称 - for (String beanName : beanNames) { - getBean(beanName); - } - // ... [代码部分省略以简化] -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 - -```java -@Override -public Object getBean(String name) throws BeansException { - return doGetBean(name, null, null, false); -} -``` - -在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 - -```java -protected T doGetBean( - String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) - throws BeansException { - // ... [代码部分省略以简化] - - // 开始创建bean实例 - if (mbd.isSingleton()) { - // 如果bean是单例的,我们会尝试从单例缓存中获取它 - // 如果不存在,则使用lambda创建一个新的实例 - sharedInstance = getSingleton(beanName, () -> { - try { - // 尝试创建bean实例 - return createBean(beanName, mbd, args); - } - catch (BeansException ex) { - // ... [代码部分省略以简化] - } - }); - // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 - beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); - } - // ... [代码部分省略以简化] - - // 确保返回的bean实例与请求的类型匹配 - return adaptBeanInstance(name, beanInstance, requiredType); -} -``` - -在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 - -```java -public Object getSingleton(String beanName, ObjectFactory singletonFactory) { - // 断言bean名称不能为空 - Assert.notNull(beanName, "Bean name must not be null"); - - // 同步访问单例对象缓存,确保线程安全 - synchronized (this.singletonObjects) { - // 从缓存中获取单例对象 - Object singletonObject = this.singletonObjects.get(beanName); - - // 如果缓存中没有找到 - if (singletonObject == null) { - // ... [代码部分省略以简化] - - try { - // 使用工厂创建新的单例实例 - singletonObject = singletonFactory.getObject(); - newSingleton = true; - } - catch (IllegalStateException ex) { - // ... [代码部分省略以简化] - } - catch (BeanCreationException ex) { - // ... [代码部分省略以简化] - } - finally { - // ... [代码部分省略以简化] - } - - // ... [代码部分省略以简化] - } - - // 返回单例对象 - return singletonObject; - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 - -```java -@Override -protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // ... [代码部分省略以简化] - - try { - // 正常的bean实例化、属性注入和初始化。 - // 这里是真正进行bean创建的部分。 - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - // 记录bean成功创建的日志 - if (logger.isTraceEnabled()) { - logger.trace("Finished creating instance of bean '" + beanName + "'"); - } - return beanInstance; - } - catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { - // ... [代码部分省略以简化] - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 - -```java -protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) - throws BeanCreationException { - - // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 - Object exposedObject = bean; - - // ... [代码部分省略以简化] - - try { - // ... [代码部分省略以简化] - - // bean初始化 - exposedObject = initializeBean(beanName, exposedObject, mbd); - } - catch (Throwable ex) { - // ... [代码部分省略以简化] - } - - // 返回创建和初始化后的bean - return exposedObject; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 - -```java -protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { - - // ... [代码部分省略以简化] - - Object wrappedBean = bean; - if (mbd == null || !mbd.isSynthetic()) { - wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); - } - - // ... [代码部分省略以简化] - - return wrappedBean; -} -``` - -在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 - -```java -@Override -public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) - throws BeansException { - - Object result = existingBean; - for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; - } - result = current; - } - return result; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 - -```java -@Override -@Nullable -public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || - bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || - bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || - bean instanceof ApplicationStartupAware)) { - return bean; - } - - // ... [代码部分省略以简化] - - invokeAwareInterfaces(bean); - - return bean; -} -``` - -在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 - -```java -private void invokeAwareInterfaces(Object bean) { - // ... [代码部分省略以简化] - if (bean instanceof ResourceLoaderAware) { - ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); - } - // ... [代码部分省略以简化] -} -``` - -最后执行到我们自定义的逻辑中,`MyResourceLoaderAware` 类是一个简单的实用工具,我们利用 Spring 的 `ResourceLoader` 机制,可以用于加载和打印资源内容。 - -```java -public class MyResourceLoaderAware implements ResourceLoaderAware { - - private ResourceLoader resourceLoader; - - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - this.resourceLoader = resourceLoader; - } - - public void getResource(String location){ - try { - Resource resource = resourceLoader.getResource(location); - System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); - } catch (IOException e) { - e.printStackTrace(); - } - } -} -``` - -### 七、注意事项 - -**资源路径**:当使用 `ResourceLoader` 获取资源时,需要提供完整的路径。例如,使用 "classpath:" 前缀来加载类路径上的资源。你应确保路径是正确的,否则 `ResourceLoader` 可能找不到资源。 - -**资源缓存**:`ResourceLoader` 本身不提供资源内容的缓存功能。每次调用 `getResource` 方法都可能返回一个新的 `Resource` 实例。如果需要缓存资源内容,你应该自己实现。 - -**资源存在性检查**:使用 `ResourceLoader` 获取的 `Resource` 不保证资源确实存在。在尝试访问资源内容之前,你应使用 `Resource.exists()` 方法检查资源是否存在。 - -**资源类型的多样性**:根据运行环境和 `ResourceLoader` 的具体实现,它可以加载多种类型的资源,如类路径资源、文件系统资源、URL资源等。你应当了解当前环境支持的资源类型,并正确使用。 - -**避免过度使用**:虽然 `ResourceLoaderAware` 提供了一种方便的方式来访问资源,但不是所有的 beans 都需要它。只有当 bean 真正需要动态地加载资源时,才应实现这个接口。否则,更简洁的方式是直接注入 `Resource` 类型的属性。 - -**生命周期时机**:当一个 bean 实现了 `ResourceLoaderAware` 接口,`setResourceLoader` 方法会在 bean 初始化的早期被调用,这确保了后续的 bean 初始化和业务逻辑可以使用到 `ResourceLoader`。但你应确保不在构造函数中访问 `ResourceLoader`,因为它此时尚未被设置。 - -**避免过度使用**:虽然 `ResourceLoaderAware` 提供了一种方便的方式来访问资源,但不是所有的 beans 都需要它。只有当 bean 真正需要动态地加载资源时,才应实现这个接口。否则,更简洁的方式是直接注入 `Resource` 类型的属性。 - -**与 `ApplicationContextAware` 的区别**:`ApplicationContext` 本身也是一个 `ResourceLoader`,因此实现 `ApplicationContextAware` 也可以获得类似的资源加载功能。但如果你的 bean 只需要资源加载功能,而不需要其他的 `ApplicationContext` 功能,那么最好只实现 `ResourceLoaderAware` 以减少耦合。 - -### 八、总结 - -#### 8.1、最佳实践总结 - -**启动类入口**:使用了 `AnnotationConfigApplicationContext` 类来启动Spring应用。这是一个使用基于Java的注解来配置Spring容器的方式。上下文初始化时使用了 `MyConfiguration` 类作为配置类。接着,从Spring上下文中获取了一个 `MyResourceLoaderAware` 类型的bean。最后,调用了 `getResource` 方法并传入了一个指定的路径。 - -**配置类**:`MyConfiguration` 是一个标注有 `@Configuration` 的配置类,表示它是一个Spring配置类。在这个配置类中,通过 `@Bean` 注解定义了一个 `MyResourceLoaderAware` 类型的bean。这确保 `MyResourceLoaderAware` 被Spring容器管理,并且 `ResourceLoader` 被正确注入。 - -**资源加载实现**:`MyResourceLoaderAware` 类实现了 `ResourceLoaderAware` 接口,从而允许Spring容器在bean初始化时自动注入 `ResourceLoader`。`getResource` 方法使用注入的 `ResourceLoader` 来加载给定路径的资源,然后读取并打印资源的内容。 - -**运行结果**:当运行应用程序时,`MyResourceLoaderAware` 成功地从指定的资源路径加载内容,并将 "hello world" 打印到控制台。 - -#### 8.2、源码分析总结 - -**启动与上下文初始化**:使用 `AnnotationConfigApplicationContext` 创建了一个基于Java注解的Spring容器,传入了 `MyConfiguration` 作为配置。从上下文中获取 `MyResourceLoaderAware` 类型的bean,并调用了其 `getResource` 方法。 - -**配置类与Bean注册**:在 `MyConfiguration` 配置类中,通过 `@Bean` 注解注册了 `MyResourceLoaderAware` 类型的bean。 - -**上下文刷新与Bean实例化**:在上下文的 `refresh` 方法中,调用了 `finishBeanFactoryInitialization` 方法以实例化所有剩余的非懒加载单例Bean。在此方法中,调用了 `preInstantiateSingletons` 方法预先实例化所有非懒加载的单例bean。 - -**Bean获取与创建流程**:使用 `getBean` 方法来实际获取Bean,这可能会触发Bean的创建。在 `doGetBean` 方法中,如果bean还未创建,会尝试创建新实例,处理依赖关系,并返回正确的bean实例。 - -**单例Bean的创建与缓存**:在 `getSingleton` 方法中,首先尝试从单例缓存中获取bean实例。如果尚未创建,则使用提供的 `ObjectFactory` 创建新实例,并存入缓存。 - -**Bean初始化**:在Bean创建完成后,进行初始化。在 `initializeBean` 方法中,会对特定的bean应用 `BeanPostProcessor` 逻辑。 - -**Aware接口的处理**:使用 `ApplicationContextAwareProcessor` 处理实现了 `Aware` 接口的beans。对于实现了 `ResourceLoaderAware` 的beans,会注入一个 `ResourceLoader` 实例。 - +## ResourceLoaderAware + +- [ResourceLoaderAware](#resourceloaderaware) + - [一、接口描述](#一接口描述) + - [二、接口源码](#二接口源码) + - [三、主要功能](#三主要功能) + - [四、最佳实践](#四最佳实践) + - [五、时序图](#五时序图) + - [六、源码分析](#六源码分析) + - [七、注意事项](#七注意事项) + - [八、总结](#八总结) + - [8.1、最佳实践总结](#81最佳实践总结) + - [8.2、源码分析总结](#82源码分析总结) + +### 一、接口描述 + +`ResourceLoaderAware` 接口,它用于为需要访问 `ResourceLoader` 的 bean 提供一个回调。`ResourceLoader` 是一个简单的策略接口,定义了如何加载底层资源(如类路径或文件系统资源)的方法。 + +### 二、接口源码 + +`ResourceLoaderAware` 是 Spring 框架自 10.03.2004 开始引入的一个核心接口。主要是希望被通知并访问 `ResourceLoader` 的对象提供了一个机制。实现此接口的对象可以获取到 `ResourceLoader`,从而加载资源。 + +```java +/** + * 任何希望被通知 ResourceLoader(通常是 ApplicationContext)的对象都应实现此接口。 + * 这是通过 org.springframework.context.ApplicationContextAware 接口完全依赖 ApplicationContext 的另一种方式。 + * + * 请注意,org.springframework.core.io.Resource 依赖也可以暴露为类型为 Resource 或 Resource[] 的bean属性, + * 通过字符串在bean工厂中进行自动类型转换进行填充。这样就消除了为了访问特定文件资源而实现任何回调接口的需求。 + * + * 当您的应用对象需要访问其名称经过计算的各种文件资源时,通常需要一个 ResourceLoader。 + * 一个好策略是使对象使用 org.springframework.core.io.DefaultResourceLoader,但仍然实现 ResourceLoaderAware, + * 以允许在 ApplicationContext 中运行时覆盖。参考 org.springframework.context.support.ReloadableResourceBundleMessageSource 为例。 + * + * 传入的 ResourceLoader 也可以检查是否为 org.springframework.core.io.support.ResourcePatternResolver 接口 + * 并相应地进行类型转换,以将资源模式解析为 Resource 对象的数组。在 ApplicationContext 中运行时,这总是可行的 + * (因为 context 接口扩展了 ResourcePatternResolver 接口)。默认情况下,使用 org.springframework.core.io.support.PathMatchingResourcePatternResolver; + * 也可以查看 ResourcePatternUtils.getResourcePatternResolver 方法。 + * + * 作为 ResourcePatternResolver 依赖的替代,考虑暴露类型为 Resource[] 的 bean 属性,通过模式字符串 + * 在bean工厂的绑定时间进行自动类型转换进行填充。 + * + * @author Juergen Hoeller + * @author Chris Beams + * @since 10.03.2004 + * @see ApplicationContextAware + * @see org.springframework.core.io.Resource + * @see org.springframework.core.io.ResourceLoader + * @see org.springframework.core.io.support.ResourcePatternResolver + */ +public interface ResourceLoaderAware extends Aware { + + /** + * 设置此对象运行的 ResourceLoader。 + * 可能是一个 ResourcePatternResolver,可以通过 instanceof ResourcePatternResolver 检查。 + * 也可以查看 ResourcePatternUtils.getResourcePatternResolver 方法。 + * 在填充正常的bean属性之后但在像 InitializingBean 的 afterPropertiesSet 这样的初始化回调或自定义初始化方法之前被调用。 + * 在 ApplicationContextAware 的 setApplicationContext 之前调用。 + * @param resourceLoader 此对象要使用的 ResourceLoader + * @see org.springframework.core.io.support.ResourcePatternResolver + * @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver + */ + void setResourceLoader(ResourceLoader resourceLoader); + +} +``` + +### 三、主要功能 + +**资源加载回调**:当 bean 实现了 `ResourceLoaderAware` 接口,Spring 容器会在该 bean 初始化时,自动将一个 `ResourceLoader` 注入到该 bean 中,从而使得 bean 可以加载资源。 + +**提供资源加载策略**:通过 `ResourceLoader`, bean 可以加载各种类型的资源,如类路径资源、文件系统资源、URL 资源等。它为资源访问提供了一个统一的策略。 + +**减少对 ApplicationContext 的直接依赖**:虽然 `ApplicationContext` 也扩展了 `ResourceLoader` 的功能,但有时候 bean 只需要资源加载功能,而不需要其他的 ApplicationContext 功能。通过实现 `ResourceLoaderAware`,bean 可以只获得资源加载功能,从而降低与完整的 `ApplicationContext` 的耦合。 + +### 四、最佳实践 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyResourceLoaderAware`类型的bean,最后调用`getResource`方法并传递了一个路径。 + +```java +public class ResourceLoaderAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyResourceLoaderAware resourceLoaderAware = context.getBean(MyResourceLoaderAware.class); + resourceLoaderAware.getResource("classpath:xcs.txt"); + } +} +``` + +这里使用`@Bean`注解,定义了以个Bean,是为了确保 `MyResourceLoaderAware` 被 Spring 容器执行。 + +```java +@Configuration +public class MyConfiguration { + + @Bean + public MyResourceLoaderAware myResourceLoaderAware(){ + return new MyResourceLoaderAware(); + } +} +``` + +`MyResourceLoaderAware` 类是一个简单的实用工具,我们利用 Spring 的 `ResourceLoader` 机制,可以用于加载和打印资源内容。 + +```java +public class MyResourceLoaderAware implements ResourceLoaderAware { + + private ResourceLoader resourceLoader; + + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + public void getResource(String location){ + try { + Resource resource = resourceLoader.getResource(location); + System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +运行结果发现, `MyResourceLoaderAware` 类成功地读取了资源文件的内容并将其打印到了控制台。 + +```java +Resource content: hello world +``` + +### 五、时序图 + +~~~mermaid +sequenceDiagram + Title: EnvironmentAware时序图 + participant ResourceLoaderAwareApplication + participant AnnotationConfigApplicationContext + participant AbstractApplicationContext + participant DefaultListableBeanFactory + participant AbstractBeanFactory + participant DefaultSingletonBeanRegistry + participant AbstractAutowireCapableBeanFactory + participant ApplicationContextAwareProcessor + participant MyResourceLoaderAware + + ResourceLoaderAwareApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建上下文 + AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新上下文 + AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
初始化Bean工厂 + AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
实例化单例 + DefaultListableBeanFactory->>AbstractBeanFactory:getBean(name)
获取Bean + AbstractBeanFactory->>AbstractBeanFactory:doGetBean(name,requiredType,args,typeCheckOnly)
执行获取Bean + AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton(beanName,singletonFactory)
获取单例Bean + DefaultSingletonBeanRegistry-->>AbstractBeanFactory:getObject()
获取Bean实例 + AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean(beanName,mbd,args)
创建Bean + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
执行Bean创建 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:initializeBean(beanName,bean,mbd)
负责bean的初始化 + AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyBeanPostProcessorsBeforeInitialization(existingBean, beanName)
调用前置处理器 + AbstractAutowireCapableBeanFactory->>ApplicationContextAwareProcessor:postProcessBeforeInitialization(bean,beanName)
触发Aware处理 + ApplicationContextAwareProcessor->>ApplicationContextAwareProcessor:invokeAwareInterfaces(bean)
执行Aware回调 + ApplicationContextAwareProcessor->>MyResourceLoaderAware:setResourceLoader(resourceLoader)
设置resourceLoader + AbstractAutowireCapableBeanFactory-->>AbstractBeanFactory:返回Bean对象 + AbstractBeanFactory-->>DefaultListableBeanFactory:返回Bean对象 + AnnotationConfigApplicationContext-->>ResourceLoaderAwareApplication:初始化完成 +~~~ + +### 六、源码分析 + +首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyResourceLoaderAware`类型的bean,最后调用`getResource`方法并传递了一个路径。 + +```java +public class ResourceLoaderAwareApplication { + + public static void main(String[] args) { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); + MyResourceLoaderAware resourceLoaderAware = context.getBean(MyResourceLoaderAware.class); + resourceLoaderAware.getResource("classpath:xcs.txt"); + } +} +``` + +在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们重点关注`refresh()`方法 + +```java +public AnnotationConfigApplicationContext(Class... componentClasses) { + this(); + register(componentClasses); + refresh(); +``` + +在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 + +```java +@Override +public void refresh() throws BeansException, IllegalStateException { + // ... [代码部分省略以简化] + // Instantiate all remaining (non-lazy-init) singletons. + finishBeanFactoryInitialization(beanFactory); + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 + +```java +/** + * 完成此工厂的bean初始化,实例化所有剩余的非延迟初始化单例bean。 + * + * @param beanFactory 要初始化的bean工厂 + */ +protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + // ... [代码部分省略以简化] + // 完成所有剩余非懒加载的单列Bean对象。 + beanFactory.preInstantiateSingletons(); +} +``` + +在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,主要的核心目的是预先实例化所有非懒加载的单例bean。在Spring的上下文初始化完成后,该方法会被触发,以确保所有单例bean都被正确地创建并初始化。其中`getBean(beanName)`是此方法的核心操作。对于容器中定义的每一个单例bean,它都会调用`getBean`方法,这将触发bean的实例化、初始化及其依赖的注入。如果bean之前没有被创建过,那么这个调用会导致其被实例化和初始化。 + +```java +public void preInstantiateSingletons() throws BeansException { + // ... [代码部分省略以简化] + // 循环遍历所有bean的名称 + for (String beanName : beanNames) { + getBean(beanName); + } + // ... [代码部分省略以简化] +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 + +```java +@Override +public Object getBean(String name) throws BeansException { + return doGetBean(name, null, null, false); +} +``` + +在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 + +```java +protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { + // ... [代码部分省略以简化] + + // 开始创建bean实例 + if (mbd.isSingleton()) { + // 如果bean是单例的,我们会尝试从单例缓存中获取它 + // 如果不存在,则使用lambda创建一个新的实例 + sharedInstance = getSingleton(beanName, () -> { + try { + // 尝试创建bean实例 + return createBean(beanName, mbd, args); + } + catch (BeansException ex) { + // ... [代码部分省略以简化] + } + }); + // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + } + // ... [代码部分省略以简化] + + // 确保返回的bean实例与请求的类型匹配 + return adaptBeanInstance(name, beanInstance, requiredType); +} +``` + +在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 + +```java +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + // 断言bean名称不能为空 + Assert.notNull(beanName, "Bean name must not be null"); + + // 同步访问单例对象缓存,确保线程安全 + synchronized (this.singletonObjects) { + // 从缓存中获取单例对象 + Object singletonObject = this.singletonObjects.get(beanName); + + // 如果缓存中没有找到 + if (singletonObject == null) { + // ... [代码部分省略以简化] + + try { + // 使用工厂创建新的单例实例 + singletonObject = singletonFactory.getObject(); + newSingleton = true; + } + catch (IllegalStateException ex) { + // ... [代码部分省略以简化] + } + catch (BeanCreationException ex) { + // ... [代码部分省略以简化] + } + finally { + // ... [代码部分省略以简化] + } + + // ... [代码部分省略以简化] + } + + // 返回单例对象 + return singletonObject; + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 + +```java +@Override +protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // ... [代码部分省略以简化] + + try { + // 正常的bean实例化、属性注入和初始化。 + // 这里是真正进行bean创建的部分。 + Object beanInstance = doCreateBean(beanName, mbdToUse, args); + // 记录bean成功创建的日志 + if (logger.isTraceEnabled()) { + logger.trace("Finished creating instance of bean '" + beanName + "'"); + } + return beanInstance; + } + catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { + // ... [代码部分省略以简化] + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean`方法中,`initializeBean`方法是bean初始化,确保bean是完全配置和准备好的。 + +```java +protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException { + + // 声明一个对象,后续可能用于存放初始化后的bean或它的代理对象 + Object exposedObject = bean; + + // ... [代码部分省略以简化] + + try { + // ... [代码部分省略以简化] + + // bean初始化 + exposedObject = initializeBean(beanName, exposedObject, mbd); + } + catch (Throwable ex) { + // ... [代码部分省略以简化] + } + + // 返回创建和初始化后的bean + return exposedObject; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean`方法中,如果条件满足(即 bean 不是合成的),那么它会调用 `applyBeanPostProcessorsBeforeInitialization` 方法。这个方法是 Spring 生命周期中的一个关键点,它会遍历所有已注册的 `BeanPostProcessor` 实现,并调用它们的 `postProcessBeforeInitialization` 方法。这允许我们和内部处理器在 bean 初始化之前对其进行修改或执行其他操作。 + +```java +protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { + + // ... [代码部分省略以简化] + + Object wrappedBean = bean; + if (mbd == null || !mbd.isSynthetic()) { + wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); + } + + // ... [代码部分省略以简化] + + return wrappedBean; +} +``` + +在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization`方法中,遍历每一个 `BeanPostProcessor` 的 `postProcessBeforeInitialization` 方法都有机会对bean进行修改或增强 + +```java +@Override +public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException { + + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; + } + return result; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization`方法中,在这个方法的实现特别关注那些实现了 "aware" 接口的 beans,并为它们提供所需的运行环境信息。 + +```java +@Override +@Nullable +public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || + bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || + bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware || + bean instanceof ApplicationStartupAware)) { + return bean; + } + + // ... [代码部分省略以简化] + + invokeAwareInterfaces(bean); + + return bean; +} +``` + +在`org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces`方法中,用于处理实现了"Aware"接口的beans。这些接口使得beans能够被自动"感知"并获得对其运行环境或特定依赖的引用,而不需要显式地进行查找或注入。 + +```java +private void invokeAwareInterfaces(Object bean) { + // ... [代码部分省略以简化] + if (bean instanceof ResourceLoaderAware) { + ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); + } + // ... [代码部分省略以简化] +} +``` + +最后执行到我们自定义的逻辑中,`MyResourceLoaderAware` 类是一个简单的实用工具,我们利用 Spring 的 `ResourceLoader` 机制,可以用于加载和打印资源内容。 + +```java +public class MyResourceLoaderAware implements ResourceLoaderAware { + + private ResourceLoader resourceLoader; + + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + public void getResource(String location){ + try { + Resource resource = resourceLoader.getResource(location); + System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +### 七、注意事项 + +**资源路径**:当使用 `ResourceLoader` 获取资源时,需要提供完整的路径。例如,使用 "classpath:" 前缀来加载类路径上的资源。你应确保路径是正确的,否则 `ResourceLoader` 可能找不到资源。 + +**资源缓存**:`ResourceLoader` 本身不提供资源内容的缓存功能。每次调用 `getResource` 方法都可能返回一个新的 `Resource` 实例。如果需要缓存资源内容,你应该自己实现。 + +**资源存在性检查**:使用 `ResourceLoader` 获取的 `Resource` 不保证资源确实存在。在尝试访问资源内容之前,你应使用 `Resource.exists()` 方法检查资源是否存在。 + +**资源类型的多样性**:根据运行环境和 `ResourceLoader` 的具体实现,它可以加载多种类型的资源,如类路径资源、文件系统资源、URL资源等。你应当了解当前环境支持的资源类型,并正确使用。 + +**避免过度使用**:虽然 `ResourceLoaderAware` 提供了一种方便的方式来访问资源,但不是所有的 beans 都需要它。只有当 bean 真正需要动态地加载资源时,才应实现这个接口。否则,更简洁的方式是直接注入 `Resource` 类型的属性。 + +**生命周期时机**:当一个 bean 实现了 `ResourceLoaderAware` 接口,`setResourceLoader` 方法会在 bean 初始化的早期被调用,这确保了后续的 bean 初始化和业务逻辑可以使用到 `ResourceLoader`。但你应确保不在构造函数中访问 `ResourceLoader`,因为它此时尚未被设置。 + +**避免过度使用**:虽然 `ResourceLoaderAware` 提供了一种方便的方式来访问资源,但不是所有的 beans 都需要它。只有当 bean 真正需要动态地加载资源时,才应实现这个接口。否则,更简洁的方式是直接注入 `Resource` 类型的属性。 + +**与 `ApplicationContextAware` 的区别**:`ApplicationContext` 本身也是一个 `ResourceLoader`,因此实现 `ApplicationContextAware` 也可以获得类似的资源加载功能。但如果你的 bean 只需要资源加载功能,而不需要其他的 `ApplicationContext` 功能,那么最好只实现 `ResourceLoaderAware` 以减少耦合。 + +### 八、总结 + +#### 8.1、最佳实践总结 + +**启动类入口**:使用了 `AnnotationConfigApplicationContext` 类来启动Spring应用。这是一个使用基于Java的注解来配置Spring容器的方式。上下文初始化时使用了 `MyConfiguration` 类作为配置类。接着,从Spring上下文中获取了一个 `MyResourceLoaderAware` 类型的bean。最后,调用了 `getResource` 方法并传入了一个指定的路径。 + +**配置类**:`MyConfiguration` 是一个标注有 `@Configuration` 的配置类,表示它是一个Spring配置类。在这个配置类中,通过 `@Bean` 注解定义了一个 `MyResourceLoaderAware` 类型的bean。这确保 `MyResourceLoaderAware` 被Spring容器管理,并且 `ResourceLoader` 被正确注入。 + +**资源加载实现**:`MyResourceLoaderAware` 类实现了 `ResourceLoaderAware` 接口,从而允许Spring容器在bean初始化时自动注入 `ResourceLoader`。`getResource` 方法使用注入的 `ResourceLoader` 来加载给定路径的资源,然后读取并打印资源的内容。 + +**运行结果**:当运行应用程序时,`MyResourceLoaderAware` 成功地从指定的资源路径加载内容,并将 "hello world" 打印到控制台。 + +#### 8.2、源码分析总结 + +**启动与上下文初始化**:使用 `AnnotationConfigApplicationContext` 创建了一个基于Java注解的Spring容器,传入了 `MyConfiguration` 作为配置。从上下文中获取 `MyResourceLoaderAware` 类型的bean,并调用了其 `getResource` 方法。 + +**配置类与Bean注册**:在 `MyConfiguration` 配置类中,通过 `@Bean` 注解注册了 `MyResourceLoaderAware` 类型的bean。 + +**上下文刷新与Bean实例化**:在上下文的 `refresh` 方法中,调用了 `finishBeanFactoryInitialization` 方法以实例化所有剩余的非懒加载单例Bean。在此方法中,调用了 `preInstantiateSingletons` 方法预先实例化所有非懒加载的单例bean。 + +**Bean获取与创建流程**:使用 `getBean` 方法来实际获取Bean,这可能会触发Bean的创建。在 `doGetBean` 方法中,如果bean还未创建,会尝试创建新实例,处理依赖关系,并返回正确的bean实例。 + +**单例Bean的创建与缓存**:在 `getSingleton` 方法中,首先尝试从单例缓存中获取bean实例。如果尚未创建,则使用提供的 `ObjectFactory` 创建新实例,并存入缓存。 + +**Bean初始化**:在Bean创建完成后,进行初始化。在 `initializeBean` 方法中,会对特定的bean应用 `BeanPostProcessor` 逻辑。 + +**Aware接口的处理**:使用 `ApplicationContextAwareProcessor` 处理实现了 `Aware` 接口的beans。对于实现了 `ResourceLoaderAware` 的beans,会注入一个 `ResourceLoader` 实例。 + **自定义逻辑**:在 `MyResourceLoaderAware` 类中,利用注入的 `ResourceLoader`,加载并打印资源内容。 \ No newline at end of file diff --git a/spring-aware/spring-aware-resourceLoaderAware/pom.xml b/spring-aware/spring-aware-resourceLoaderAware/pom.xml new file mode 100644 index 0000000..50e42b4 --- /dev/null +++ b/spring-aware/spring-aware-resourceLoaderAware/pom.xml @@ -0,0 +1,15 @@ + + + + spring-aware + com.xcs.spring + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aware-resourceLoaderAware + + + \ No newline at end of file diff --git a/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/ResourceLoaderAwareApplication.java b/spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/ResourceLoaderAwareApplication.java similarity index 100% rename from spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/ResourceLoaderAwareApplication.java rename to spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/ResourceLoaderAwareApplication.java diff --git a/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 85% rename from spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java index 17b50b3..a6c997b 100644 --- a/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java +++ b/spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyConfiguration.java @@ -2,7 +2,6 @@ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; /** * @author xcs diff --git a/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyResourceLoaderAware.java b/spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyResourceLoaderAware.java similarity index 96% rename from spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyResourceLoaderAware.java rename to spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyResourceLoaderAware.java index 9e13b2d..49a5269 100644 --- a/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyResourceLoaderAware.java +++ b/spring-aware/spring-aware-resourceLoaderAware/src/main/java/com/xcs/spring/config/MyResourceLoaderAware.java @@ -1,27 +1,27 @@ -package com.xcs.spring.config; - -import org.springframework.context.ResourceLoaderAware; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.util.FileCopyUtils; - -import java.io.IOException; - -public class MyResourceLoaderAware implements ResourceLoaderAware { - - private ResourceLoader resourceLoader; - - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - this.resourceLoader = resourceLoader; - } - - public void getResource(String location){ - try { - Resource resource = resourceLoader.getResource(location); - System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); - } catch (IOException e) { - e.printStackTrace(); - } - } -} +package com.xcs.spring.config; + +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.FileCopyUtils; + +import java.io.IOException; + +public class MyResourceLoaderAware implements ResourceLoaderAware { + + private ResourceLoader resourceLoader; + + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + public void getResource(String location){ + try { + Resource resource = resourceLoader.getResource(location); + System.out.println("Resource content: " + new String(FileCopyUtils.copyToByteArray(resource.getInputStream()))); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/spring-aware-resourceLoaderAware/src/main/resources/xcs.txt b/spring-aware/spring-aware-resourceLoaderAware/src/main/resources/xcs.txt similarity index 100% rename from spring-aware-resourceLoaderAware/src/main/resources/xcs.txt rename to spring-aware/spring-aware-resourceLoaderAware/src/main/resources/xcs.txt diff --git a/spring-interface-instantiationAwareBeanPostProcessor/pom.xml b/spring-interface-instantiationAwareBeanPostProcessor/pom.xml deleted file mode 100644 index 68deeef..0000000 --- a/spring-interface-instantiationAwareBeanPostProcessor/pom.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - spring-reading - com.xcs.spring - 0.0.1-SNAPSHOT - - 4.0.0 - - spring-interface-instantiationAwareBeanPostProcessor - - - 11 - 11 - - - \ No newline at end of file diff --git a/spring-interface/pom.xml b/spring-interface/pom.xml new file mode 100644 index 0000000..c71e50d --- /dev/null +++ b/spring-interface/pom.xml @@ -0,0 +1,35 @@ + + + + spring-reading + com.xcs.spring + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-interface + pom + + + spring-interface-beanFactoryPostProcessor + spring-interface-beanPostProcessor + spring-interface-environment + spring-interface-applicationListener + spring-interface-importSelector + spring-interface-importBeanDefinitionRegistrar + spring-interface-resource + spring-interface-embeddedValueResolver + spring-interface-factoryBean + spring-interface-beanDefinitionRegistryPostProcessor + spring-interface-instantiationAwareBeanPostProcessor + spring-interface-destructionAwareBeanPostProcessor + spring-interface-mergedBeanDefinitionPostProcessor + spring-interface-smartInstantiationAwareBeanPostProcessor + spring-interface-initializingBean + spring-interface-disposableBean + spring-interface-smartInitializingSingleton + + + \ No newline at end of file diff --git a/spring-interface-applicationListener/pom.xml b/spring-interface/spring-interface-applicationListener/pom.xml similarity index 88% rename from spring-interface-applicationListener/pom.xml rename to spring-interface/spring-interface-applicationListener/pom.xml index a7f95c5..a5b7d68 100644 --- a/spring-interface-applicationListener/pom.xml +++ b/spring-interface/spring-interface-applicationListener/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-beanDefinitionRegistryPostProcessor/README.md b/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/README.md similarity index 100% rename from spring-interface-beanDefinitionRegistryPostProcessor/README.md rename to spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/README.md diff --git a/spring-interface-beanDefinitionRegistryPostProcessor/pom.xml b/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/pom.xml similarity index 89% rename from spring-interface-beanDefinitionRegistryPostProcessor/pom.xml rename to spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/pom.xml index 292fbd5..37ede1e 100644 --- a/spring-interface-beanDefinitionRegistryPostProcessor/pom.xml +++ b/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/pom.xml @@ -3,12 +3,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT - 4.0.0 + 4.0.0 spring-interface-beanDefinitionRegistryPostProcessor \ No newline at end of file diff --git a/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/BeanDefinitionRegistryPostProcessorApplication.java b/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/BeanDefinitionRegistryPostProcessorApplication.java similarity index 100% rename from spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/BeanDefinitionRegistryPostProcessorApplication.java rename to spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/BeanDefinitionRegistryPostProcessorApplication.java diff --git a/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanDefinitionRegistryPostProcessor.java b/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanDefinitionRegistryPostProcessor.java similarity index 100% rename from spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanDefinitionRegistryPostProcessor.java rename to spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanDefinitionRegistryPostProcessor.java diff --git a/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java b/spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java similarity index 100% rename from spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java rename to spring-interface/spring-interface-beanDefinitionRegistryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java diff --git a/spring-interface-beanFactoryPostProcessor/README.md b/spring-interface/spring-interface-beanFactoryPostProcessor/README.md similarity index 100% rename from spring-interface-beanFactoryPostProcessor/README.md rename to spring-interface/spring-interface-beanFactoryPostProcessor/README.md diff --git a/spring-interface-beanFactoryPostProcessor/pom.xml b/spring-interface/spring-interface-beanFactoryPostProcessor/pom.xml similarity index 88% rename from spring-interface-beanFactoryPostProcessor/pom.xml rename to spring-interface/spring-interface-beanFactoryPostProcessor/pom.xml index bdfc349..ad745f1 100644 --- a/spring-interface-beanFactoryPostProcessor/pom.xml +++ b/spring-interface/spring-interface-beanFactoryPostProcessor/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/BeanFactoryPostProcessorApplication.java b/spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/BeanFactoryPostProcessorApplication.java similarity index 100% rename from spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/BeanFactoryPostProcessorApplication.java rename to spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/BeanFactoryPostProcessorApplication.java diff --git a/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanFactoryPostProcessor.java b/spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanFactoryPostProcessor.java similarity index 100% rename from spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanFactoryPostProcessor.java rename to spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyBeanFactoryPostProcessor.java diff --git a/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 88% rename from spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java index 33119f3..835939c 100644 --- a/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java +++ b/spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java @@ -2,7 +2,6 @@ package com.xcs.spring.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; /** * @author xcs diff --git a/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java b/spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java similarity index 100% rename from spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java rename to spring-interface/spring-interface-beanFactoryPostProcessor/src/main/java/com/xcs/spring/config/MySimpleBean.java diff --git a/spring-interface-beanPostProcessor/README.md b/spring-interface/spring-interface-beanPostProcessor/README.md similarity index 100% rename from spring-interface-beanPostProcessor/README.md rename to spring-interface/spring-interface-beanPostProcessor/README.md diff --git a/spring-interface-beanPostProcessor/pom.xml b/spring-interface/spring-interface-beanPostProcessor/pom.xml similarity index 88% rename from spring-interface-beanPostProcessor/pom.xml rename to spring-interface/spring-interface-beanPostProcessor/pom.xml index d618a64..c8fd344 100644 --- a/spring-interface-beanPostProcessor/pom.xml +++ b/spring-interface/spring-interface-beanPostProcessor/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/BeanPostProcessorApplication.java b/spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/BeanPostProcessorApplication.java similarity index 100% rename from spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/BeanPostProcessorApplication.java rename to spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/BeanPostProcessorApplication.java diff --git a/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyBeanPostProcessor.java b/spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyBeanPostProcessor.java similarity index 100% rename from spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyBeanPostProcessor.java rename to spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyBeanPostProcessor.java diff --git a/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java b/spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java similarity index 100% rename from spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java rename to spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java diff --git a/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceImpl.java b/spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceImpl.java similarity index 100% rename from spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceImpl.java rename to spring-interface/spring-interface-beanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceImpl.java diff --git a/spring-interface-destructionAwareBeanPostProcessor/README.md b/spring-interface/spring-interface-destructionAwareBeanPostProcessor/README.md similarity index 100% rename from spring-interface-destructionAwareBeanPostProcessor/README.md rename to spring-interface/spring-interface-destructionAwareBeanPostProcessor/README.md diff --git a/spring-interface-destructionAwareBeanPostProcessor/pom.xml b/spring-interface/spring-interface-destructionAwareBeanPostProcessor/pom.xml similarity index 89% rename from spring-interface-destructionAwareBeanPostProcessor/pom.xml rename to spring-interface/spring-interface-destructionAwareBeanPostProcessor/pom.xml index 74179f8..9933baf 100644 --- a/spring-interface-destructionAwareBeanPostProcessor/pom.xml +++ b/spring-interface/spring-interface-destructionAwareBeanPostProcessor/pom.xml @@ -3,12 +3,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT - 4.0.0 + 4.0.0 spring-interface-destructionAwareBeanPostProcessor \ No newline at end of file diff --git a/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/DestructionAwareBeanPostProcessorApplication.java b/spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/DestructionAwareBeanPostProcessorApplication.java similarity index 100% rename from spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/DestructionAwareBeanPostProcessorApplication.java rename to spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/DestructionAwareBeanPostProcessorApplication.java diff --git a/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyDestructionAwareBeanPostProcessor.java b/spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyDestructionAwareBeanPostProcessor.java similarity index 100% rename from spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyDestructionAwareBeanPostProcessor.java rename to spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyDestructionAwareBeanPostProcessor.java diff --git a/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionService.java b/spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionService.java similarity index 100% rename from spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionService.java rename to spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionService.java diff --git a/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionServiceImpl.java b/spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionServiceImpl.java similarity index 100% rename from spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionServiceImpl.java rename to spring-interface/spring-interface-destructionAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/ConnectionServiceImpl.java diff --git a/spring-interface-disposableBean/README.md b/spring-interface/spring-interface-disposableBean/README.md similarity index 100% rename from spring-interface-disposableBean/README.md rename to spring-interface/spring-interface-disposableBean/README.md diff --git a/spring-interface-disposableBean/pom.xml b/spring-interface/spring-interface-disposableBean/pom.xml similarity index 88% rename from spring-interface-disposableBean/pom.xml rename to spring-interface/spring-interface-disposableBean/pom.xml index b0f4c3b..7a94036 100644 --- a/spring-interface-disposableBean/pom.xml +++ b/spring-interface/spring-interface-disposableBean/pom.xml @@ -3,12 +3,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT - 4.0.0 + 4.0.0 spring-interface-disposableBean \ No newline at end of file diff --git a/spring-interface-disposableBean/src/main/java/com/xcs/spring/DisposableBeanApplication.java b/spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/DisposableBeanApplication.java similarity index 100% rename from spring-interface-disposableBean/src/main/java/com/xcs/spring/DisposableBeanApplication.java rename to spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/DisposableBeanApplication.java diff --git a/spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyDisposableBean.java b/spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyDisposableBean.java similarity index 100% rename from spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyDisposableBean.java rename to spring-interface/spring-interface-disposableBean/src/main/java/com/xcs/spring/config/MyDisposableBean.java diff --git a/spring-interface-embeddedValueResolver/pom.xml b/spring-interface/spring-interface-embeddedValueResolver/pom.xml similarity index 88% rename from spring-interface-embeddedValueResolver/pom.xml rename to spring-interface/spring-interface-embeddedValueResolver/pom.xml index 8c6fc14..2f4d177 100644 --- a/spring-interface-embeddedValueResolver/pom.xml +++ b/spring-interface/spring-interface-embeddedValueResolver/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-environment/pom.xml b/spring-interface/spring-interface-environment/pom.xml similarity index 88% rename from spring-interface-environment/pom.xml rename to spring-interface/spring-interface-environment/pom.xml index 3f74c8d..9b1a231 100644 --- a/spring-interface-environment/pom.xml +++ b/spring-interface/spring-interface-environment/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-factoryBean/pom.xml b/spring-interface/spring-interface-factoryBean/pom.xml similarity index 88% rename from spring-interface-factoryBean/pom.xml rename to spring-interface/spring-interface-factoryBean/pom.xml index ed85c9a..9214ec4 100644 --- a/spring-interface-factoryBean/pom.xml +++ b/spring-interface/spring-interface-factoryBean/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-importBeanDefinitionRegistrar/pom.xml b/spring-interface/spring-interface-importBeanDefinitionRegistrar/pom.xml similarity index 89% rename from spring-interface-importBeanDefinitionRegistrar/pom.xml rename to spring-interface/spring-interface-importBeanDefinitionRegistrar/pom.xml index 19b2268..502c13c 100644 --- a/spring-interface-importBeanDefinitionRegistrar/pom.xml +++ b/spring-interface/spring-interface-importBeanDefinitionRegistrar/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-importSelector/pom.xml b/spring-interface/spring-interface-importSelector/pom.xml similarity index 88% rename from spring-interface-importSelector/pom.xml rename to spring-interface/spring-interface-importSelector/pom.xml index f740bd3..b2d6a80 100644 --- a/spring-interface-importSelector/pom.xml +++ b/spring-interface/spring-interface-importSelector/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-initializingBean/README.md b/spring-interface/spring-interface-initializingBean/README.md similarity index 100% rename from spring-interface-initializingBean/README.md rename to spring-interface/spring-interface-initializingBean/README.md diff --git a/spring-interface-initializingBean/pom.xml b/spring-interface/spring-interface-initializingBean/pom.xml similarity index 88% rename from spring-interface-initializingBean/pom.xml rename to spring-interface/spring-interface-initializingBean/pom.xml index dabb1db..43f2fa0 100644 --- a/spring-interface-initializingBean/pom.xml +++ b/spring-interface/spring-interface-initializingBean/pom.xml @@ -3,12 +3,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT - 4.0.0 + 4.0.0 spring-interface-initializingBean \ No newline at end of file diff --git a/spring-interface-initializingBean/src/main/java/com/xcs/spring/InitializingBeanApplication.java b/spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/InitializingBeanApplication.java similarity index 100% rename from spring-interface-initializingBean/src/main/java/com/xcs/spring/InitializingBeanApplication.java rename to spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/InitializingBeanApplication.java diff --git a/spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyInitializingBean.java b/spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyInitializingBean.java similarity index 100% rename from spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyInitializingBean.java rename to spring-interface/spring-interface-initializingBean/src/main/java/com/xcs/spring/config/MyInitializingBean.java diff --git a/spring-interface-instantiationAwareBeanPostProcessor/README.md b/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/README.md similarity index 100% rename from spring-interface-instantiationAwareBeanPostProcessor/README.md rename to spring-interface/spring-interface-instantiationAwareBeanPostProcessor/README.md diff --git a/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/pom.xml b/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/pom.xml new file mode 100644 index 0000000..1fa765c --- /dev/null +++ b/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/pom.xml @@ -0,0 +1,14 @@ + + + + spring-interface + com.xcs.spring + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-interface-instantiationAwareBeanPostProcessor + + \ No newline at end of file diff --git a/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/InstantiationAwareBeanPostProcessorApplication.java b/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/InstantiationAwareBeanPostProcessorApplication.java similarity index 100% rename from spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/InstantiationAwareBeanPostProcessorApplication.java rename to spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/InstantiationAwareBeanPostProcessorApplication.java diff --git a/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyInstantiationAwareBeanPostProcessor.java b/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyInstantiationAwareBeanPostProcessor.java similarity index 100% rename from spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyInstantiationAwareBeanPostProcessor.java rename to spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyInstantiationAwareBeanPostProcessor.java diff --git a/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBase.java b/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBase.java similarity index 100% rename from spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBase.java rename to spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBase.java diff --git a/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBaseImpl.java b/spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBaseImpl.java similarity index 100% rename from spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBaseImpl.java rename to spring-interface/spring-interface-instantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/DataBaseImpl.java diff --git a/spring-interface-mergedBeanDefinitionPostProcessor/README.md b/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/README.md similarity index 100% rename from spring-interface-mergedBeanDefinitionPostProcessor/README.md rename to spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/README.md diff --git a/spring-interface-mergedBeanDefinitionPostProcessor/pom.xml b/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/pom.xml similarity index 89% rename from spring-interface-mergedBeanDefinitionPostProcessor/pom.xml rename to spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/pom.xml index 7869a8c..1416d18 100644 --- a/spring-interface-mergedBeanDefinitionPostProcessor/pom.xml +++ b/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/pom.xml @@ -3,12 +3,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT - 4.0.0 + 4.0.0 spring-interface-mergedBeanDefinitionPostProcessor \ No newline at end of file diff --git a/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/MergedBeanDefinitionPostProcessorApplication.java b/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/MergedBeanDefinitionPostProcessorApplication.java similarity index 100% rename from spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/MergedBeanDefinitionPostProcessorApplication.java rename to spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/MergedBeanDefinitionPostProcessorApplication.java diff --git a/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/annotation/MyValue.java b/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/annotation/MyValue.java similarity index 100% rename from spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/annotation/MyValue.java rename to spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/annotation/MyValue.java diff --git a/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/bean/MyBean.java b/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/bean/MyBean.java similarity index 100% rename from spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/bean/MyBean.java rename to spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/bean/MyBean.java diff --git a/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyMergedBeanDefinitionPostProcessor.java b/spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyMergedBeanDefinitionPostProcessor.java similarity index 100% rename from spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyMergedBeanDefinitionPostProcessor.java rename to spring-interface/spring-interface-mergedBeanDefinitionPostProcessor/src/main/java/com/xcs/spring/config/MyMergedBeanDefinitionPostProcessor.java diff --git a/spring-interface-resource/pom.xml b/spring-interface/spring-interface-resource/pom.xml similarity index 88% rename from spring-interface-resource/pom.xml rename to spring-interface/spring-interface-resource/pom.xml index a3ffaf6..358b7bc 100644 --- a/spring-interface-resource/pom.xml +++ b/spring-interface/spring-interface-resource/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT diff --git a/spring-interface-smartInitializingSingleton/README.md b/spring-interface/spring-interface-smartInitializingSingleton/README.md similarity index 100% rename from spring-interface-smartInitializingSingleton/README.md rename to spring-interface/spring-interface-smartInitializingSingleton/README.md diff --git a/spring-interface-smartInitializingSingleton/pom.xml b/spring-interface/spring-interface-smartInitializingSingleton/pom.xml similarity index 89% rename from spring-interface-smartInitializingSingleton/pom.xml rename to spring-interface/spring-interface-smartInitializingSingleton/pom.xml index a47bb9e..a1de82b 100644 --- a/spring-interface-smartInitializingSingleton/pom.xml +++ b/spring-interface/spring-interface-smartInitializingSingleton/pom.xml @@ -3,12 +3,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT - 4.0.0 + 4.0.0 spring-interface-smartInitializingSingleton \ No newline at end of file diff --git a/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/SmartInitializingSingletonApplication.java b/spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/SmartInitializingSingletonApplication.java similarity index 100% rename from spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/SmartInitializingSingletonApplication.java rename to spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/SmartInitializingSingletonApplication.java diff --git a/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MySmartInitializingSingleton.java b/spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MySmartInitializingSingleton.java similarity index 100% rename from spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MySmartInitializingSingleton.java rename to spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/config/MySmartInitializingSingleton.java diff --git a/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/service/MyService.java b/spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/service/MyService.java similarity index 100% rename from spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/service/MyService.java rename to spring-interface/spring-interface-smartInitializingSingleton/src/main/java/com/xcs/spring/service/MyService.java diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/README.md b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/README.md similarity index 100% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/README.md rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/README.md diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/pom.xml b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/pom.xml similarity index 89% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/pom.xml rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/pom.xml index cc07cea..e74dcae 100644 --- a/spring-interface-smartInstantiationAwareBeanPostProcessor/pom.xml +++ b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/pom.xml @@ -3,12 +3,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - spring-reading + spring-interface com.xcs.spring 0.0.1-SNAPSHOT - 4.0.0 + 4.0.0 spring-interface-smartInstantiationAwareBeanPostProcessor \ No newline at end of file diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/SmartInstantiationAwareBeanPostProcessorApplication.java b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/SmartInstantiationAwareBeanPostProcessorApplication.java similarity index 100% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/SmartInstantiationAwareBeanPostProcessorApplication.java rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/SmartInstantiationAwareBeanPostProcessorApplication.java diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/annotation/MyAutowired.java b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/annotation/MyAutowired.java similarity index 100% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/annotation/MyAutowired.java rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/annotation/MyAutowired.java diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java similarity index 100% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MyConfiguration.java diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MySmartInstantiationAwareBeanPostProcessor.java b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MySmartInstantiationAwareBeanPostProcessor.java similarity index 97% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MySmartInstantiationAwareBeanPostProcessor.java rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MySmartInstantiationAwareBeanPostProcessor.java index 36f5a9d..334569d 100644 --- a/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MySmartInstantiationAwareBeanPostProcessor.java +++ b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/config/MySmartInstantiationAwareBeanPostProcessor.java @@ -1,7 +1,6 @@ package com.xcs.spring.config; import com.xcs.spring.annotation.MyAutowired; -import com.xcs.spring.service.MyService; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java similarity index 100% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyService.java diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceA.java b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceA.java similarity index 100% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceA.java rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceA.java diff --git a/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceB.java b/spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceB.java similarity index 100% rename from spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceB.java rename to spring-interface/spring-interface-smartInstantiationAwareBeanPostProcessor/src/main/java/com/xcs/spring/service/MyServiceB.java