Spring源码解析之BeanFactoryPostProcessor(二)

上一章,我们介绍了在AnnotationConfigApplicationContext初始化的时候,会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner两个对象:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
……
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
……
}

 

我们已经知道AnnotatedBeanDefinitionReader对象创建的大致流程,AnnotatedBeanDefinitionReader会向BeanDefinitionRegistry注册一些基础组件的BeanDefinition,好让spring容器根据这些基础组件的BeanDefinition生成bean对象完成类的扫描、依赖注入。那么ClassPathBeanDefinitionScanner在初始化应用上下文对象又起到什么样的作用呢?首先从ClassPathBeanDefinitionScanner的名字我们大概可以知道,这个类是用来扫描BeanDefinition的类路径的,那么,我们要怎么使用这个类来扫描类路径呢?来看下面的测试用例:

    @Test
    public void test04() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        ac.scan("org.example.service");
        ac.refresh();
        System.out.println(ac.getBean("orderService"));
    }

    

运行结果:

org.example.service.OrderService@4ba2ca36

  

上面的测试用例,我们不再像之前那样在配置类上用@ComponentScan注解标记要扫描的类路径,并将配置类作为参数传给AnnotationConfigApplicationContext扫描bean对象。而是在调用AnnotationConfigApplicationContext无参构造方法创建对象后,再调用应用上下文(AnnotationConfigApplicationContext)ac.scan(String... basePackages)将类路径传入,由应用上下文对象根据传入的类路径进行扫描,而ac.scan(String... basePackages)方法也是调用scanner.scan(String... basePackages)来完成类路径的扫描。

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
……
	private final ClassPathBeanDefinitionScanner scanner;
……
	@Override
	public void scan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		this.scanner.scan(basePackages);
	}
……
}

    

<1>和<2>两块代码最终效果看起来一样,都能扫描我们设定的路径,根据类生成BeanDefinition再生成bean,可能有人会怀疑,扫描用@ComponentScan所标记的类路径,是否是AnnotationConfigApplicationContext的scanner对象? 这里笔者可以告诉大家:扫描@ComponentScan标记的类路径需要用到ClassPathBeanDefinitionScanner类,但并非用AnnotationConfigApplicationContext的scanner对象,而是在代码的某处创建了ClassPathBeanDefinitionScanner对象再调用scan(String... basePackages)方法扫描@ComponentScan标记的路径,至于是哪里创建新的ClassPathBeanDefinitionScanner对象再扫描@ComponentScan标记的路径后面会讲,只是这里我们要知道AnnotationConfigApplicationContext的scanner对象仅仅用来帮助我们添加扫描路径,而实际的开发中很少用到。

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);//<1>
————————————————————————————————————————————————————————————————————————————————————————————————
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();//<2>
ac.scan("org.example.service");
ac.refresh();

  

下面,我们来看看ClassPathBeanDefinitionScanner的scan(String... basePackages)方法都做了些什么:

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

	private final BeanDefinitionRegistry registry;
	……
	public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);//<1>

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);//<2>
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//<3>
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//<4>
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);//<5>
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//<6>
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);//<7>
				}
			}
		}
		return beanDefinitions;
	}
	……
}

  

  • 进入到scan(String... basePackages)方法后,会在<1>处将要扫描的类路径传递给doScan(String... basePackages),由doScan(String... basePackages)代替其完成扫描。
  • includeAnnotationConfig字段默认为true,会进入到AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)方法,这个方法在上一章有讲过,会预先注册一些基础组件的BeanDefinition到BeanDefinitionRegistry。
  • 进入到doScan(String... basePackages)后,在<3>处会调用父类ClassPathScanningCandidateComponentProvider的findCandidateComponents(String basePackage)方法,这个方法可以针对我们传入的一个类路径,扫描路径下所有类并返回其BeanDefinition,这里返回的是BeanDefinition的集合。
  • 在<3>处拿到BeanDefinition集合后会循环每一个BeanDefinition,在<4>处用beanName生成器根据BeanDefinition生成beanName。
  • 在代码<5>处会判断BeanDefinition能否转型成AnnotatedBeanDefinition(注解BeanDefinition),如果可以则会将BeanDefinition传入到AnnotationConfigUtils.processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd),在这个方法中会获取AnnotatedBeanDefinition的元信息并设置其属性,比如这个类是否标记了@Lazy、@Primary、@DependsOn、@Description……等。
  • 最后,在<6>处会根据BeanDefinition和beanName生成一个BeanDefinitionHolder对象,在<7>处将BeanDefinitionHolder和registry对象传入registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法,将BeanDefinition和beanName注册到registry。

从上面的代码我们得知,spring扫描类路径是调用ClassPathScanningCandidateComponentProvider.findCandidateComponents(String basePackage)方法,所以我们继续追踪到这个方法:

	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);//<1>
		}
	}
	
	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;//<2>
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);//<3>
			……
			for (Resource resource : resources) {
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);//<4>
						if (isCandidateComponent(metadataReader)) {//<5>
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);//<6>
							sbd.setSource(resource);//<7>
							if (isCandidateComponent(sbd)) {//<8>
								candidates.add(sbd);
							}
						}
						……
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}

  

  • 在findCandidateComponents(String basePackage)方法中,一般是调用<1>处的代码进行类扫描,即else分支,if分支一般是使用spring注解@Index进行扫描性能的提升,否则不会进入。
  • 在scanCandidateComponents(String basePackage)方法的<2>处会根据类路径生成一个spring自定义的表达式packageSearchPath,在<3>处spring可以解析这个表达式并返回一个Resource数组,每个Resource元素都代表类路径下的一个class文件的路径。
  • 之后会循环每个Resource元素,在<4>处获取其元数据,并在<5>处判断通过父类ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)方法判断Resource元素所对应的class是否是一个组件,比如标记了@Component、@Service、@Controller……等。
  • 通过<5>处的判断如果一个类是一个组件,在<6>处会根据元数据生成一个ScannedGenericBeanDefinition对象,这里我们又看到一个BeanDefinition的实现类,然后在<7>处设置BeanDefinition的元信息,即:sbd.setSource(resource),之前说过,一个对象的元信息是类,一个类的元信息是类文件路径,而BeanDefinition是用于描述类的,所以它的元信息也是类文件路径。
  • 最后,会在<8>处调用父类ClassPathScanningCandidateComponentProvider的重载方法isCandidateComponent(AnnotatedBeanDefinition beanDefinition),在这个方法决定描述类的BeanDefinition是否有资格加入candidates集合,那么什么样的类才可以加入到candidates集合呢?比如:这个类是一个顶级类或者静态嵌套内部类,这个类不需要借助其他类来构造实例;或者这个类并不是一个接口类,或者这个类是个抽象类,但内部方法有用@Lookup注解来标记。这里不理解的不要心急,下面还会讲到ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)和isCandidateComponent(AnnotatedBeanDefinition beanDefinition)这两个重载方法。

现在,我们来调试一下上面的代码,看看packageSearchPath和resources的内容,首先我们来看我们类路径下的文件:

 D:\F\java_space\spring-source\spring-beanFactoryPostProcessor\target\classes\org\example\service 的目录

2020/11/19  08:22    <DIR>          .
2020/11/19  08:22    <DIR>          ..
2020/11/19  08:22               490 HelloService$BarService.class
2020/11/19  08:22               609 HelloService$FooService.class
2020/11/19  08:22               318 HelloService$Hello.class
2020/11/19  08:22               524 HelloService.class
2020/11/19  08:22               559 OrderService.class
2020/11/19  08:22             1,783 Test1BeanFactoryPostProcessor.class
2020/11/19  08:22               555 UserService.class
               7 个文件          4,838 字节
               2 个目录 102,708,711,424 可用字节

 

然后调试进入上面的代码,可以看到packageSearchPath的内容为:classpath*:org/example/service/**/*.class,之前说过,这里spring会自定义表达式,通过表达式可以扫描这个路径下的类文件。

 

我们来看下ClassPathScanningCandidateComponentProvider的isCandidateComponent(MetadataReader metadataReader)和isCandidateComponent(AnnotatedBeanDefinition beanDefinition)两个重载方法的实现:

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
	……
	private final List<TypeFilter> includeFilters = new LinkedList<>();

	private final List<TypeFilter> excludeFilters = new LinkedList<>();
	……
	protected void registerDefaultFilters() {
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));//<1>
		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
			logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
		}
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
			logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}
	……
	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {//<2>
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {//<3>
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}
	
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));//<4>
	}
	……
}

  

  • 在创建ClassPathScanningCandidateComponentProvider对象时,一般会调用到registerDefaultFilters()方法,在这个方法中会往includeFilters字段加入需要扫描的注解,如:在<1>处加入对标记了@Component类的过滤。
  • isCandidateComponent(MetadataReader metadataReader)在<2>和<3>会按照我们在@CommponScan设定的excludeFilters和includeFilters来过滤要扫描的类。
  • <4>处的isCandidateComponent(AnnotatedBeanDefinition beanDefinition)会获取beanDefinition的元数据,根据元数据判断这个beanDefinition是否是一个候选组件,比如:metadata.isIndependent()要求一个类必须是顶级类或者是静态内部嵌套类,即这个类不需要依赖其他类来生成,而内部嵌套类必须依赖外部类来生成对象;metadata.isConcrete()判断一个类是否是抽象类或者接口;metadata.isAbstract()判断是否是一个抽象类,metadata.hasAnnotatedMethods(Lookup.class.getName())则判断这个类中是否有标记了Lookup的方法。如果一个抽象类中没有标记Lookup的方法,则不能成为候选组件。

我们来分析下下面的类哪些可以成为候选组件:

package org.example.service;

import org.springframework.stereotype.Component;


public class HelloService {
    @Component
    public class FooService {
    }

    @Component
    public static class BarService {
    }

    @Component
    public interface Hello {
        void sayHello();
    }
}

  

  • FooService是嵌套内部类,需要依赖HelloService来生成对象,所以它无法成为一个候选组件。
  • BarService是静态嵌套内部类,可以独立生成对象,所以它可以成为一个候选组件。
  • Hello是接口,无法独立生成对象,所以它无法成为一个候选组件。

我们已经大致了解了ClassPathBeanDefinitionScanner的作用,下面我们来总结下:ClassPathBeanDefinitionScanner会根据程序员指定的类路径扫描到路径下所有的类,并将其封装为ScannedGenericBeanDefinition对象返回,之后会判断这些ScannedGenericBeanDefinition对象是否具备成为BeanDefinition的条件,如果一个类没有用诸如:@Component、@Service……这样的注解标记,那一定无法成为BeanDefinition。即便一个类标记了@Component注解,也要看这个类是否是内部类或者接口,因为内部类和接口都不能生成bean,其BeanDefinition也没有存在的意义。如果一个抽象类的方法有使用Lookup注解,这个抽象类是可以成为一个bean的,也会将解析出来的抽象类BeanDefinition注册到spring容器。

我们已经了解了下面代码<1>处AnnotationConfigApplicationContext(Class<?>... componentClasses)最开始的步骤,会调用默认构造方法初始化reader和scanner。现在我们要来学习<2>处的配置类注册方法,如我们所见,<2>处会进而将配置类传给<3>处的reader对象。reader对象会根据配置类生成对应的BeanDefinition注册进spring容器。

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	private final AnnotatedBeanDefinitionReader reader;

	private final ClassPathBeanDefinitionScanner scanner;
	……
	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		this();//<1>
		register(componentClasses);//<2>
		refresh();
	}	
	……
	@Override
	public void register(Class<?>... componentClasses) {
		Assert.notEmpty(componentClasses, "At least one component class must be specified");
		this.reader.register(componentClasses);//<3>
	}
	……
}

  

AnnotatedBeanDefinitionReader.register(Class<?>... componentClasses)经过一系列的调用,会来到AnnotatedBeanDefinitionReader.doRegisterBean(...)方法,这里我们又看到一个BeanDefinition的实现——AnnotatedGenericBeanDefinition,这段代码在<1>处将为配置类生成一个对应的AnnotatedGenericBeanDefinition,在<2>处生成beanName,在<3>处将beanName和BeanDefinition包装成一个BeanDefinitionHolder对象,最后在<4>处将beanName和BeanDefinition注册进原先的AnnotationConfigApplicationContext对象。

public class AnnotatedBeanDefinitionReader {
	……
	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}
	……
	public void registerBean(Class<?> beanClass, @Nullable String name) {
		doRegisterBean(beanClass, name, null, null, null);
	}
	……
	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {

		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);//<1>
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}
		……
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		if (customizers != null) {
			for (BeanDefinitionCustomizer customizer : customizers) {
				customizer.customize(abd);
			}
		}

		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);//<2>
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);//<3>
	}
	……
}

    

在生成配置类对应的BeanDefinition并注册进spring容器后,AnnotationConfigApplicationContext就会调用父类AbstractApplicationContext的refresh()方法,我们先大致看一下refresh()方法: 

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	……
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			……
			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//<1>
			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);
			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);//<2>
				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				……
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);//<3>
				……
			}

			catch (BeansException ex) {
				……
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();
				……
			}
			……
		}
	}
	……
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		return getBeanFactory();
	}
	protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
		@Override
	public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
	……
}

     

在refresh()的<1>处会返回一个beanFactory对象,之后会把beanFactory对象作为参数传入到多个调用方法。从注释上来看,<2>处会执行bean工厂后置处理器(BeanFactoryPostProcessor)的回调接口,<3>处会初始化非懒加载的单例bean。其他方法我们先不去管完成了什么工作,但可以得出一个结论:beanFactory对象在整个refresh()方法中占着举足轻重的分量。

从AbstractApplicationContext的实现来看,obtainFreshBeanFactory()方法只是定义了两个抽象方法:refreshBeanFactory()、getBeanFactory()来刷新、获取BeanFactory对象,具体是如何刷新、返回是由子类来完成的。实现这两个抽象方法的类有:GenericApplicationContext、AbstractRefreshableApplicationContext。GenericApplicationContext是AnnotationConfigApplicationContext的父类,AbstractRefreshableApplicationContext是ClassPathXmlApplicationContext的父类,我们先来看GenericApplicationContext实现的两个抽象方法:

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
	private final DefaultListableBeanFactory beanFactory;
	……
	private final AtomicBoolean refreshed = new AtomicBoolean();
	……
	public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
	}
	……
	@Override
	protected final void refreshBeanFactory() throws IllegalStateException {
		if (!this.refreshed.compareAndSet(false, true)) {
			throw new IllegalStateException(
					"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
		}
		this.beanFactory.setSerializationId(getId());
	}
	……
	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		return this.beanFactory;
	}
}

  

可以看到,在初始化GenericApplicationContext对象时,会通过构造方法初始化beanFactory对象,执行refreshBeanFactory()就只是向beanFactory设置一个唯一标识,getBeanFactory()只是将已经初始化好的beanFactory对象返回。

AbstractRefreshableApplicationContext的实现相比GenericApplicationContext会复杂些,但不会太多,可以看到如果通过子类来初始化AbstractRefreshableApplicationContext对象时并不会初始化beanFactory对象,而是在执行refreshBeanFactory()方法的时候创建一个新的beanFactory对象。这里会先在<1>处判断下应用上下文是否已经生成了beanFactory,如果有则进入<1>处的分支销毁已有的bean并且关闭当前beanFactory。正常情况下是不会进入到<1>处的分支,因为refreshBeanFactory()方法通常只会在spring容器启动的时候在refresh()方法中被调用一次,直到整个进程被销毁。如果判断beanFactory为空,或者beanFactory不为空但已经销毁现有bean和关闭beanFactory之后,会调用createBeanFactory()创建一个新的beanFactory对象出来。

在初始化好新的beanFactory后,会执行customizeBeanFactory方法,这个方法可以针对beanFactory设置一些定制化的属性,比如在<2>处是否允许BeanDefinition重载,在<3>处设置spring容器是否支持循环依赖。如果不熟悉BeanDefinition重载可以看笔者之前写的Spring源码解析之BeanFactoryPostProcessor(一)。这里我们来了解下什么是设置循环依赖,假如笔者开发了两个bean:OrderService和UserService,又在这两个bean之中依赖注入彼此,这就是循环依赖,相信有不少人在日常开发中都有遇到这种循环依赖的场景。spring默认是支持循环依赖的,但我们也可以通过配置或者重载关闭循环依赖,比如笔者开发一个应用上下文类并继承了ClassPathXmlApplicationContext,通过重写customizeBeanFactory方法设置allowCircularReferences为false,直接关闭spring的循环依赖,那么spring在依赖注入的时候发现OrderService和UserService在各自的bean中循环依赖彼此就会报错。

在执行完beanFactory的属性定制后,会执行loadBeanDefinitions抽象方法,这个方法同样是由子类AbstractXmlApplicationContext来实现。子类可以重写此方法将一些基础组件的BeanDefinition注册进beanFactory,这样既可以让spring以bean的形式管理这些组件的生命周期,又不需要程序员在XML文件中配置这些spring自身需要的基础组件,减轻程序员的心智负担。同理,如果我们编写了一些配置较为复杂的组件,但又不想在XML进行配置增加开发者的理解成本,也可以重写此方法将自己编写的组件注册进beanFactory。只是有一点要注意,如果我们重写此方法,最好先调用父类实现的loadBeanDefinitions方法,以保证spring自身需要的基础组件都已经注册到beanFactory中,再将我们自己开发的组件注册到beanFactory。

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
	@Nullable
	private Boolean allowBeanDefinitionOverriding;

	@Nullable
	private Boolean allowCircularReferences;

	/** Bean factory for this context. */
	@Nullable
	private volatile DefaultListableBeanFactory beanFactory;
	……
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {//<1>
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
	……
	protected final boolean hasBeanFactory() {
		return (this.beanFactory != null);
	}
	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		DefaultListableBeanFactory beanFactory = this.beanFactory;
		if (beanFactory == null) {
			throw new IllegalStateException("BeanFactory not initialized or already closed - " +
					"call 'refresh' before accessing beans via the ApplicationContext");
		}
		return beanFactory;
	}
	protected DefaultListableBeanFactory createBeanFactory() {
		return new DefaultListableBeanFactory(getInternalParentBeanFactory());
	}
	……
	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {//<2>
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {//<3>
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}
	protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
			throws BeansException, IOException;
}

  

  

posted @ 2020-12-01 21:06  北洛  阅读(477)  评论(0编辑  收藏  举报