发布于2021-06-12 14:08 阅读(214) 评论(0) 点赞(9) 收藏(2)
最开始的Spring系列我主要着重的是整个代码逻辑的实力,到现在有些都快一年了,或者也有几个月了,自己现在去看也有些看不下去了,毕竟代码逻辑有些复杂。并且自己以前也是刚开始了解Spring的源码,也是主要想了解Spring的代码到底是怎样的处理的。现在准备重新梳理下Spring我想了解的内容,这次我们不陷入代码的具体的实现,我们主要梳理Spring的一些结构、逻辑实现,同时也整理梳理过程中的一些Spring的组件用法。
我们这次主要是梳理下Spring中的一些引入直接:@Component
、ComponentScans
、ComponentScan
、@Bean
、@PropertySources
、@PropertySource
,以及与@Import
以及配套的ImportSelector
、ImportBeanDefinitionRegistrar
。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
这个接口是用来引入组件的(各种类型的组件),其的属性value
是用来指定这个组件的名称的。这个注解的功能也是注入一个Bean
,类似于@Bean
的功能,只是这个直接有更多的用法,并且我们一般并不自动使用这个注解,而是使用引用它的组件注解:Service
、Configuration
、Repository
、Controller
这些。我们可以看下有引用它的注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}
而这些注解的用法我们都已经知道了,比较简单,我们就不再看其的用法了,所以我们使用的@Service
、@Controller
这些注解,Spring
在底层扫描都是看有没有@Component
。
这两个都是用来扫描组件的,其中@Component
是用来扫描指定包下面的组件,@Import
是用来加载指定的类资源。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
这个是用来导入指定的类,可以为@Configuration
、ImportSelector
这样的配置加载组件,也可以为一般的组件例如注入简单的Bean
对象。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
这个是用来注入扫描包的,但其实这个注解还可以有一些其他的参数,这个就先不研究了,一般是使用这个简单的用法。
然后@ComponentScans
是用来指定多个@ComponentScan
的我们就不再额外说明了:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {
ComponentScan[] value();
}
这里我们使用官方的简单demo测试用例。
@Test
public void componentScanOverlapsWithImport() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config1.class);
ctx.register(Config2.class);
ctx.refresh(); // no conflicts found trying to register SimpleComponent
ctx.getBean(SimpleComponent.class); // succeeds -> there is only one bean of type SimpleComponent
}
@Test
public void componentScanViaImport() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config3.class);
ctx.refresh();
ctx.getBean(SimpleComponent.class);
}
@ComponentScan("org.springframework.context.annotation.componentscan.simple")
static final class Config1 {
}
@Import(org.springframework.context.annotation.componentscan.simple.SimpleComponent.class)
static final class Config2 {
}
@Import(ImportedConfig.class)
static final class Config3 {
}
@ComponentScan("org.springframework.context.annotation.componentscan.simple")
@ComponentScan("org.springframework.context.annotation.componentscan.importing")
public static final class ImportedConfig {
}
package org.springframework.context.annotation.componentscan.simple;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class SimpleComponent {
@Bean
public String exampleBean() {
return "example";
}
}
package org.springframework.context.annotation.componentscan.simple;
import org.springframework.stereotype.Component;
public class ClassWithNestedComponents {
@Component
public static class NestedComponent extends ClassWithNestedComponents {
}
@Component
public static class OtherNestedComponent extends ClassWithNestedComponents {
}
}
这个就是官方的简单测试用例,其中Config1
是使用的@ComponentScan
来扫描包的,Config2
是使用的@Import
扫描具体的Bean
资源,然后Config3
是通过@Import
再去引入其他配置组件,这里是@ComponentScan
。这上面3个都能加载到SimpleComponent
类。
同时在componentScanOverlapsWithImport()
方法中是有将SimpleComponent
加载两次的,同时并没有什么一次,这说明Spring
内部是有重复判断的。这里问题1
,到时候源码的时候我能看其是怎样处理的。
1)、结构
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ImportResource {
@AliasFor("locations")
String[] value() default {};
@AliasFor("value")
String[] locations() default {};
Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;
}
这个就是用来加载资源的,当我们有些不想用注解,或者注解不能满足,还是想用配置文件的时候我们就可以使用这个注解来加载资源。这里的reader()
是用来指定读取解析器的,就是使用指定的BeanDefinitionReader
来读取这个加载的资源。
@Configuration
@ImportResource("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
static class ImportXmlAutowiredConfig {
@Autowired TestBean xmlDeclaredBean;
public @Bean String xmlBeanName() {
return xmlDeclaredBean.getName();
}
}
@Test
public void importXmlWithAutowiredConfig() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlAutowiredConfig.class);
String name = ctx.getBean("xmlBeanName", String.class);
assertThat(name, equalTo("xml.declared"));
ctx.close();
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="xmlDeclaredBean" class="org.springframework.tests.sample.beans.TestBean">
<constructor-arg value="xml.declared"/>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<map>
<entry key="name" value="myName"/>
</map>
</property>
</bean>
<!-- should not cause infinite loop (SPR-11858) but rather simply be ignored -->
<bean class="org.springframework.context.annotation.ConfigurationClassPostProcessor"/>
</beans>
通过这个demo我们了解,当我们不想使用@Configuration
注解去加载内容的,时候就可以使用这个@ImportResource
注解去加载xml
配置。同时我们可以看到在ImportXmlAutowiredConfig
类上面是有一个@Configuration
注解的,但其实在这里这个注解更多的是一个象征其是配置类的意义,可以不加这个注解,Spring
也可以扫描到这个@ImportResource
。
我们上面是使用@ImportSource
来加载xml
,但其实我们还可以其来加载properties
文件。
@Configuration
@ImportResource(locations = "classpath:org/springframework/context/annotation/configuration/ImportNonXmlResourceConfig-context.properties", reader = PropertiesBeanDefinitionReader.class)
static class ImportNonXmlResourceConfig {
}
propertiesDeclaredBean.(class)=org.springframework.tests.sample.beans.TestBean
@Test
public void importNonXmlResource() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportNonXmlResourceConfig.class);
assertTrue(ctx.containsBean("propertiesDeclaredBean"));
ctx.close();
}
我们可以看这个demo其是用指定PropertiesBeanDefinitionReader
来读取加载的资源。不过使用这个PropertiesBeanDefinitionReader
我们可以看到其需要写一些特殊的标记,例如这里的.(class)
,我们再看这个类的内部定义
public static final String CLASS_KEY = "(class)";
public static final String PARENT_KEY = "(parent)";
public static final String SCOPE_KEY = "(scope)";
public static final String SINGLETON_KEY = "(singleton)";
public static final String ABSTRACT_KEY = "(abstract)";
public static final String LAZY_INIT_KEY = "(lazy-init)";
还可以指定这些去配置含义。不过这种方式一般应该很少用。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
String name() default "";
String[] value();
同样@PropertySources
也是用来加载多个@PropertySource
的,我们也不重复讲@PropertySources
了。
@Configuration
@PropertySource("classpath:org/springframework/context/annotation/p1.properties")
static class ConfigWithImplicitName {
@Inject Environment env;
@Bean
public TestBean testBean() {
return new TestBean(env.getProperty("testbean.name"));
}
}
testbean.name=p1TestBean
from.p1=p1Value
base.package=org/springframework/context/annotation
spring.profiles.active=test
@Test
public void withImplicitName() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConfigWithImplicitName.class);
ctx.refresh();
assertTrue("property source p1 was not added",
ctx.getEnvironment().getPropertySources().contains("class path resource [org/springframework/context/annotation/p1.properties]"));
assertThat(ctx.getBean(TestBean.class).getName(), equalTo("p1TestBean"));
}
public TestBean(String name) {
this.name = name;
}
这个demo我们看withImplicitName()
方法。这个就是将p1.properties
中的内容加载到Environment
环境变量中,然后这里还注入了一个Bean
对象即TestBean
,然后其初始化的时候将p1.properties
中设置的testbean.name
对应的值设置为其的name
。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
通过这个注释我们了解到,其实用来犹如@Configuration
,或配套的ImportSelector
、ImportBeanDefinitionRegistrar
的,又或者一般的组件。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
这个用来返回扫描筛选的类全路径名。
public interface ImportBeanDefinitionRegistrar {
void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
这个入参有BeanDefinitionRegistry
,就表示其可以注入BeanDefinition
。也就是我们可以通过importingClassMetadata
从这个入参获取到我们需要的信息,然后动态筛选注册到BeanDefinitionRegistry
中。
@Test
public void testWithImporter() {
ApplicationContext context = new AnnotationConfigApplicationContext(Wrapper.class);
assertEquals("foo", context.getBean("value"));
}
@Configuration
@Import(Selector.class)
protected static class Wrapper {
}
protected static class Selector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {Config.class.getName()};
}
}
@Configuration
protected static class Config {
@Bean
public FooFactoryBean foo() {
return new FooFactoryBean();
}
@Bean
public String value() throws Exception {
String name = foo().getObject().getName();
Assert.state(name != null, "Name cannot be null");
return name;
}
@Bean
@Conditional(NoBarCondition.class)
public String bar() throws Exception {
return "bar";
}
}
这个demo 就是能通过扫描Config
导入其中定义的Bean
,然后借由Selector
返回这个Config
全路径,再由@Import
导入到Spring容器中去解析。最后通过上面的方式我们就能获取到foo
(FooFactoryBean foo()
)这个Bean
实例了。
ImportBeanDefinitionRegistrar
@Test
public void importRegistrar() throws Exception {
ImportedRegistrar.called = false;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ImportingRegistrarConfig.class);
ctx.refresh();
assertNotNull(ctx.getBean("registrarImportedBean"));
assertNotNull(ctx.getBean("otherImportedConfigBean"));
}
@Configuration
@EnableImportRegistrar
static class ImportingRegistrarConfig {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ImportedRegistrar.class)
public @interface EnableImportRegistrar {
}
static class ImportedRegistrar implements ImportBeanDefinitionRegistrar {
static boolean called;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(String.class.getName());
registry.registerBeanDefinition("registrarImportedBean", beanDefinition);
GenericBeanDefinition beanDefinition2 = new GenericBeanDefinition();
beanDefinition2.setBeanClass(OtherImportedConfig.class);
registry.registerBeanDefinition("registrarImportedConfig", beanDefinition2);
Assert.state(!called, "ImportedRegistrar called twice");
called = true;
}
}
这个demo就是通过ImportedRegistrar
来注入一个GenericBeanDefinition
,这个GenericBeanDefinition
是直接new
的当然我们一般可以从AnnotationMetadata
获取我们信息来注入相应的内容。
6、@Bean的使用
@Configuration
static class StandardConfig {
@Bean
public TestBean testBean1() {
return new TestBean("interesting");
}
}
@Test
public void testStandard() {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(StandardConfig.class);
assertFalse(ctx.getBeanFactory().containsSingleton("testBean1"));
}
我们这样就能注入一个Bean
了。
这里我们使用前面的democomponentScanOverlapsWithImport
来debug。
@Test
public void componentScanOverlapsWithImport() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config1.class);
ctx.register(Config2.class);
ctx.refresh(); // no conflicts found trying to register SimpleComponent
ctx.getBean(SimpleComponent.class); // succeeds -> there is only one bean of type SimpleComponent
}
Spring
处理扫描上面的这些注解主要就是这个ConfigurationClassPostProcessor
类。
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
这里我们主要关注其是实现了BeanDefinitionRegistryPostProcessor
接口。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
然后主要是这个postProcessBeanDefinitionRegistry
方法来触发扫描加载的过程。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
这个方法主要就是从例如@Configuration
这样的配置中跟进一步的加载Bean
,也就是通过上面的@Component
、@Import
这些加载Bean
。
这里其主要逻辑处理是processConfigBeanDefinitions
方法,而入参BeanDefinitionRegistry
就是我们以前就梳理过的加载、保存、创建所有BeanDefinition
以及Bean
对象的类,一般实现是DefaultListableBeanFactory
。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
............
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// 这里会进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
............
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
........
}
while (!candidates.isEmpty());
............
}
}
上面我们简化了一些内容,下面我们就来分布解析
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
这里就是从所有加载的BeanDefinition
中,筛选是配置类(@Configuration)的BeanDefinition
。主要是使用ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)
,然后beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null
是用来判断这个BeanDefinition
是否已经解析为配置类了,如果是就不需要再次解析了,也就是会直接return;
了。如果还没有解析,就通过方法判断是不是配置类。
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
............... (不同情况对metadata的赋值),从BeanDefinition中获取metadata
}
if (isFullConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (isLiteConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
...........
return true;
}
这个方法主要就是检查BeanDefinition
是不是配置类。
通过metadata.getAnnotationAttributes(Configuration.class.getName())
看其是有没有@Configuration
注解,如果有,这里就加一个标记CONFIGURATION_CLASS_ATTRIBUTE
,表明其是一个Configuration
配置类,这里就与前面的判断关联上了。
上面isFullConfigurationCandidate
是对@Configuration
注解的判断,下面的isLiteConfigurationCandidate
又是另一个配置类型的判断了。
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
可以看到这里是另外注解的判断,如果是@Component
、@Import
、@ImportResource
、@Bean
这些的话,其也是会识别为配置类,返回true
,然后将其添加到configCandidates
中。
上面这些我们主要是从BeanDefinition*中筛选出配置类,下面我们就要来具体分析加载这些配置类要加载的Bean定义了
这里我们看到,我们自己定义的两个Config1
、Config2
都是被加载到BeanDefinition
定义中。
然后由于这两个都是配置类,所以我们可以看到其都被添加到了configCandidates
中了。
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
下面重要是使用ConfigurationClassParser
来解析,通过parser.parse(candidates)
,然后会将解析出来的具体Bean
形成ConfigurationClass
,添加到其的configurationClasses
属性中。这个parser.parse
方法里面会有些喜欢解析加载,例如你可以通过@ComponentScan
去加载@Import
,再在@Import
里面嵌套@ComponentScan
这些。
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();
我们可以看到现在configurationClasses
里面还没有内容。
最终解析后,其就有了所有的加载资源。
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
之后通过this.reader.loadBeanDefinitions(configClasses)
方法,再将这些ConfigurationClass
都加载转换为BeanDefinition
并加载到BeanDefinitionRegistry
(DefaultListableBeanFactory
),再借由BeanDefinition
去创建对应的Bean
实例。
上面我们梳理了通过配置类将一些要加载的Bean
转换为基础的ConfigurationClass
,然后再由ConfigurationClass
转换为BeanDefinition
。现在我们就来具体分析下parser.parse(candidates)
(ConfigurationClassParser
)方法。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
processDeferredImportSelectors();
}
这里主要是判断其是哪种类型的BeanDefinition
,再分别调用不同的parse
方法:
protected final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
但我们可以看到其最终都是调用到processConfigurationClass
方法,同时会通过入参构建为ConfigurationClass
。
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
这里也是先通过this.configurationClasses.get(configClass)
判断这个ConfigurationClass
是不是已经加载了,毕竟多个注解可能会重复扫描,这里也就解答了我们最前面的问题1
了。这里最前面的this.conditionEvaluator.shouldSkip
也是一个重要的内容,我们先跳过,之后文章我们来通过实例demo
来说明。记住这个processConfigurationClass
方法,之后会循环,再次调用这个方法来循环解析。
然后再通过doProcessConfigurationClass
方法来具体解析(会将configClass
先转换为SourceClass
)。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
...........
}
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
..............
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
...........
}
// No superclass -> processing is complete
return null;
}
这个方法就是解析的核心类了,以这个类为核心,来具体扫描这些配置类需要加载的Bean
。
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
这个方法主要就是来加载这个类的子类的,因为其子类可能也嵌套有这些注解,触发再次的循环解析。
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
processConfigurationClass(candidate.asConfigClass(configClass));
}
finally {
this.importStack.pop();
}
}
}
}
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}
//前面的两个判断是否是配置类的方法。
这个是通过sourceClass.getMemberClasses()
来获取其子类资源。然后循环判断,再通过ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata())
方法来判断其子类中是否有这些配置类,如果有就添加到candidates
。之后再是processConfigurationClass(candidate.asConfigClass(configClass))
,再次循环遍历,这个processConfigurationClass
方法我们前面提过,用来再次调用循环解析。
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
这个方法就是将@PropertySource
中的内容加载到environment
中。
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
...................
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
..................
}
}
}
private void addPropertySource(PropertySource<?> propertySource) {
String name = propertySource.getName();
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
............
if (this.propertySourceNames.isEmpty()) {
propertySources.addLast(propertySource);
}
else {
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
propertySources.addBefore(firstProcessed, propertySource);
}
this.propertySourceNames.add(name);
}
这个就是通过地址获取资源Resource resource = this.resourceLoader.getResource(resolvedLocation)
,再解析为propertySource
,然后将其添加到this.environment
中
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
ropertySources.addBefore(firstProcessed, propertySource);
我们以上面的加载@PropertySource
注解的demo为例,debug到这里:
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
这部分就是用来解析@ComponentScan
的,这里是通过this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName())
扫描出这个包下面的所有BeanDefinition
。
public class ClassWithNestedComponents {
@Component
public static class NestedComponent extends ClassWithNestedComponents {
}
@Component
public static class OtherNestedComponent extends ClassWithNestedComponents {
}
}
然后又会再次通过ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)
方法来看这些BeanDefinitionHolder
中又有没有配置类,如果有,就又会通过parse(bdCand.getBeanClassName(), holder.getBeanName())
方法来循环调用扫描批处理。
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
这里实现是获取如果有@Improt
使用的注解,然后获取其value
将其添加到imports
返回。就是下面的importCandidates
方法。注意这个processImports
方法名,后面也会进行自我调用。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
..........
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
............
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
............
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
...........
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
这个是解析@Import
相关内容的。我们看起步骤,实现是添加到importStack
栈中,再最后会出栈this.importStack.pop()
,然后就是这个@Improt
的三种情况了。
情况一
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
这个就是我们前面demo1中使用的ImportSelector
。现在我们用前面的demo1来看下这个类的处理过程。
现在已经将这个注入的Selector.class
实例化为对象了。同时我们还可以看到这个实例化出来的对象还可以实现Aware
这些接口,并且还会对其进行赋值。
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
public static void invokeAwareMethods(Object parserStrategyBean, Environment environment,
ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
if (parserStrategyBean instanceof Aware) {
if (parserStrategyBean instanceof BeanClassLoaderAware) {
ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
if (classLoader != null) {
((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader);
}
}
if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) {
((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry);
}
if (parserStrategyBean instanceof EnvironmentAware) {
((EnvironmentAware) parserStrategyBean).setEnvironment(environment);
}
if (parserStrategyBean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader);
}
}
在上面后,就会进入else
,因为我们现在是ImportSelector
。而DeferredImportSelector
这个我们以后再看。
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
然后是通过其的selectImports
方法返回importClassNames
,即类的全路径
然后再通过类的全路径名转换为SourceClass
。之后再调用processImports
方法进行处理。然后其实这个processImports
方法是自我调用,所以最后还是会到一般的组件交给最后的else
分支。
情况二
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
这部分是用来处理ImportBeanDefinitionRegistrar
的可以看到这里同样是先通过Class<?> candidateClass
去创建实例对象BeanUtils.instantiateClass
。然后再调用其configClass.addImportBeanDefinitionRegistrar
来完成注册BeanDefinition
。 情况三
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
这个就是当做为一般的配置类,然后又再循环调用了我们前面的开始方法processConfigurationClass
,发起新一轮的解析。
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
处理了上面的@Import
后,现在是处理ImportResource
了,我们这个这个注解一般是用来加载xml
、properties
这些文件的。
这里是先通过sourceClass.getMetadata()
,获取当前SourceClass
的所有@ImportSource
的,然后通过locations
获取其的文件地址,再获取对应的reader
,用于之后的解析读取,当然这个也可以自定义。然后再将其添加达到待解析的资源中configClass.addImportedResource(resolvedResource, readerClass)
。
public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
我们先记下这个addImportedResource
方法,之后我们会调用getImportedResources
来获取这里添加的资源地址。
public Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
然后会在parser.parse(candidates)
方法后去解析。我个我们后面再说。
现在我们就会来获取被@Bean
注解的方法了。我们也以前面使用的@Bean
demo来说明
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
我们可以看到,这里就获取解析到了被@Bean
注解的方法testBean1
。这里解析出来后就会将这个MethodMetadata
添加到configClass
中。
public void addBeanMethod(BeanMethod method) {
this.beanMethods.add(method);
}
这里我们同样注意之后的getBeanMethods()
方法的获取调用。
public Set<BeanMethod> getBeanMethods() {
return this.beanMethods;
}
7)、再然后又是超类的循环。
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
这里再配合入口方法。
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
..........
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
所以这里有很多的循环调用。
自此我们关于这些注解的解析&加载工作已经完成了,所以这里的整个过程都是将这些注解的相关内容解析加载到其对应的ConfigurationClass
:
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
例如以前面的parse
方法为起点的转换为ConfigurationClass
。
通过前面这个深入的过程,我们已经了解了其对这些注解的加载解析过程,现在我们可以来了解是怎样通过ConfigurationClass
来形成对应的BeanDefinition
的。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
............
}
while (!candidates.isEmpty());
}
前面是parser.parse(candidates);
方法的深入,现在我们可以通过this.reader.loadBeanDefinitions(configClasses)
来看是怎样形成解析为BeanDefiniton
的,注意这里Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses())
,已经将前面的所有ConfigurationClass
从parse
中提取到了Set<ConfigurationClass> configClasses
中。
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
这个就是解析configurationModel
的内容,将其加载注册为BeanDefinition
。
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
我们可以看到这里就是对不同注解的解析内容解析处理。我们这里简单的configClass.isImported()
这个来说明下,这个其实就是如果本身是是一个import
类型的注解,就注册其本身为一个BeanDefintion
。
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
configBeanDef.setScope(scopeMetadata.getScopeName());
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
configClass.setBeanName(configBeanName);
if (logger.isDebugEnabled()) {
logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
}
}
这里的注册逻辑就是this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
。
然后我们再简单看下,是怎样处理@Bean
()的:
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
..........
// Consider name and any aliases
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// Register aliases even when overridden
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
.............
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
...........
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
String destroyMethodName = bean.getString("destroyMethod");
beanDef.setDestroyMethodName(destroyMethodName);
...........
// Replace the original bean definition with the target one, if necessary
BeanDefinition beanDefToRegister = beanDef;
...........
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
我们可以看到是先注册了别名this.registry.registerAlias(beanName, alias);
,再是构建ConfigurationClassBeanDefinition(configClass, metadata)
。之后再说设置autowire
、initMethod
这些,最后就是调用this.registry.registerBeanDefinition(beanName, beanDefToRegister)
来注册。
自此我们就再次梳理了@Import
、@ImportSource
、@ComponentScan
等这些注解的使用以及简单的源码解析流程。
原文链接:https://blog.csdn.net/qq_25179481/article/details/117718432
作者:长这么胖
链接:http://www.javaheidong.com/blog/article/222346/1c70cf4963c3a62c0875/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!