Spring之配置类解析
Spring的配置类解析过程
什么是配置类?配置类有什么作用?
从使用上来看,配置了可以用来配置Bean,将配置好的bean注册到容器中来进行使用。
Spring是怎么判断是配置类的?
在最开始的阶段org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
进入之后,来到
但是当前Spring容器中只有一个ConfigurationClassPostProcessor这个后置处理器才会发挥作用,所以直接看下ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法。
问题一:Spring会将什么类当成是配置类?
首先spring会将容器中的BeanDefinition全部拿出来,然后挨个进行遍历。
对于第一个if判断来说,因为没有对BeanDefinition做过设置,所以不成立,那么直接来看第二个。
判断哪些是配置类
首先获取得到类上添加了@Configuratoin注解的类信息
-
第一个判断的是Configuration注解中的proxyBeanMethods是否为true,如果为true,那么就会给BeanDefinitoin属性来设置一个标识
表示当前的配置类是一个full类型的配置类
-
第二个判断是如果没有@Configuration注解或者只要是存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
看一下candidateIndicators中的值:
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());
}
那么看hasBeanMethods的表示:
小结
总结到了这里,不禁感慨,原来之前用的@Component也是一个lite类型的配置类;甚至没有添加任何注释,只是在方法上添加了注释的@Bean的类也是一个lite类型的配置类(但是这里的前提是一定要存在某个metaData元数据描述器能够来描述这个类),如下所示:
如果仅仅只是这样的一个配置类,添加扫描器来进行扫描的时候,因为没有描述器来描述,这样的类不会成为一个lite配置类
public class MyConfig {
@Bean
public AnimalService animalService(){
return new AnimalService();
}
}
比如说,这里来添加一个元数据描述器
@Component
public class MyConfig {
@Bean
public AnimalService animalService(){
return new AnimalService();
}
}
就可以将其注入进来。
找到了配置类,开始解析配置类
首先来构建一个配置类的解析器,然后利用do....while来循环进行解析,这里就是利用了do..while的好处,解析完所有的配置类
因为对于配置类来说,可能解析完一个配置类之后,还会产生另外一个配置类,类似递归的这种思想。
如何解析配置类
根据每种不同的BeanDefinition的类型来进行解析,从第一个进去,因为大部分都是第一种类型。
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
// 把类的元信息和beanName封装为ConfigurationClass
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
将元信息和beanName封装成一个配置类,然后来解析配置类:
这里又看见了do...while循环,那么这里是用来干嘛的?因为这里考虑到了父类,如果父类中也有是一个配置类标志,那么这里也会来进行添加。
然后下面分别来解析@Component、@PropertySources、@ComponentScans和@ComponentScan注解、@ImportResource和@Bean
解析配置类是加了@Component注解
这里的解析是十分有意思的
举例如下所示:
@Component
public class MyConfig implements AppInterface {
class MyTest{
@Bean
public AnimalService animalService(){
return new AnimalService();
}
}
}
解析配置类是加了@PropertySources注解
可以加载外部的文件,如properties文件等,这里是先找到位置,添加到propertySourceNames中,但是不是先来进行解析。
在后面统一来做处理。
解析配置类是加了@ComponentScans和ComponentScan注解
spring正常来进行扫描的逻辑,如mybatis的框架在扫描mapper的时候,也会在这一步来做处理。
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// spring重新创建了一个扫描器,然后获取得到在ApplicationContext中的componentScan中的默认
// 扫描器中的默认规则获取得到
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
// 默认为AnnotationBeanNameGenerator
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
}
else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
// 自定义添加扫描器规则
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
// 排除规则
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
// 扫描那些包
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
// 开始来记性扫描逻辑。扫描逻辑是在这里来执行的
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
解析配置类是加了@Import注解
但是这里又分成了三种:实现了ImportSelector接口、实现了ImportBeanDefinitionRegistrar接口和没有实现ImportSelector接口以及ImportBeanDefinitionRegistrar接口的类。
如果是实现了ImportSelector接口的,则会将ImportSelector.selectImports返回的String[]中的类名取出来,然后重新执行processImports逻辑,直到找到所有的lite类型的配置类。
如果是实现了ImportBeanDefinitionRegistrar接口的,这里只会将类实例化出来,然后添加到importBeanDefinitionRegistrars结合中,在后面做统一处理。
最终是既没有实现ImportSelector接口,也没有实现ImportBeanDefinitionRegistrar的类,重新执行processConfigurationClass方法,获取得到所有的配置类。
解析配置类是加了@ImportResource注解
显示找到外部的配置文件来进行扫描,找到位置之后,这里先是来进行添加,不做处理。类似@PropertySources中的逻辑,在后面统一做处理。这里只是放入到importedResources中,做个缓存处理。
解析配置类中方法是加了@Bean注解
可以看到这里又是将其添加到beanMethods集合中来进行保存,而不是先来进行执行。
解析配置类实现接口中的默认方法
public interface AppInterface {
@Bean
default DogService dogService(){
return new DogService();
}
}
@Component
public class MyConfig implements AppInterface {
}
这种方式,也是可以将DogService注入到容器中来的。
小结
在整合解析配置类的过程中,只有@ComponentScan和@ComponentScans会来扫描并最终成为BeanDefinition,然后添加到容器中去。
而对于其他的来说,仅仅只是保存而已,还没有来得及解析。
而且在扫描配置类的时候会形成很多新的配置类,那么就需要递归来进行调用。
最终将需要进行解析的配置类添加到configurationClasses中来,但是这里始终还是没有来进行解析的。在解析的最后一步中:
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// ConfigurationClass重写了equals方法,只要两个ConfigurationClass对应的className相等就可以
this.configurationClasses.put(configClass, configClass);
扫描配置类完成,开始处理
添加解析完成之后的配置类之后,接着就是来对配置类来做处理
接下来就是利用读取器来对每种类型的配置类来做处理了。
那么看一下读取器开始来进行去取,读取的正是之前存储的内容。
比如:加了@Bean、读取xml的以及外部配置文件以及ImportBeanDefinitionRegistrar对象需要执行方法,开始来着手生成BeanDefinitoin的。
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
可能有多个,那么循环遍历,看看具体的执行逻辑。
看方法名称即可知道在这里来注册BeanDefinition。
但是从这里可以看到
this.reader.loadBeanDefinitions(configClasses);
这行代码执行之后,还会有BeanDefinitoin的出现,证明了还可能会有新的配置类产生,所以依然需要来做处理。
// 如果发现BeanDefinition增加了,则有可能增加了配置类
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
// 检查多出来的BeanDefinition是不是配置类,需不需要解析
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
所以这里就是外部的do..while来做循环的目的。
BeanDefinition的beanName重名
第一种是针对@Component重名,如果两个@Component直接报错;
@Component("a")
public class A{
}
@Component("a")
public class A{
}
第二种是@Bean类型,命名重命名,如果是重载
@Bean
public A a(){
return new A();
}
@Bean
public A a(B b){
return new A();
}
对应的源码在loadBeanDefinitionsForBeanMethod方法中:
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
// 如果beanName等于"appConfig",就会抛异常
if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
"' clashes with bean name for containing configuration class; please make those names unique!");
}
return;
}
如果是这样子,那么只会有一个a作为beanName而存在,并不存在覆盖问题。
第三种:@Component和@Bean共存
先扫描,然后再来解析@Bean共存的问题。这个是这里的顺序。
这里才是真正的覆盖。
对应的源码也还是在上面的方法中,先来检查是否已经存在,存在的类型是ConfigurationClassBeanDefinition的话,表示的是利用@Bean方式产生的BeanDefiniton,而扫描的BeanDefinition不是ConfigurationClassBeanDefinition类型,而是ScannedGenericBeanDefinition类型的
BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);
// Is the existing bean definition one that was created from a configuration class?
// -> allow the current bean method to override, since both are at second-pass level.
// However, if the bean method is an overloaded case on the same configuration class,
// preserve the existing bean definition.
if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
if (ccbd.getMetadata().getClassName().equals(
beanMethod.getConfigurationClass().getMetadata().getClassName())) {
// 如果@Bean对应的beanName已经存在BeanDefinition,那么则把此BeanDefinition的isFactoryMethodUnique设置为false
// 等到后续根据此BeanDefinition去创建Bean时,就知道不止一个对应方法了,要推断了
if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {
ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
}
return true;
}
else {
return false;
}
}
if (existingBeanDef instanceof ScannedGenericBeanDefinition) {
return false;
}
最终执行到下面这里,生成新的ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
......
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
然后判断是否能够覆盖?
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 默认是允许BeanDefinition覆盖的
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
.....
this.beanDefinitionMap.put(beanName, beanDefinition);
然后执行到这里,因为默认的isAllowBeanDefinitionOverriding,表示允许覆盖,那么就来进行覆盖。
就会来进行覆盖掉了,@Bean配置的代替了@Component的了。
所以最终@Bean和@Component生成的BeanDefinition以@Bean生成的Definition生成的为主。
测试一下这种情况:
@Component
public class UserService {}
@ComponentScan("com.gunag.ioc.demo3")
public class AppConfig3 {
@Bean
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public UserService userService(){
return new UserService();
}
}
测试多次获取得到bean:
com.gunag.ioc.demo3.service.UserService@5577140b
com.gunag.ioc.demo3.service.UserService@1c6b6478
com.gunag.ioc.demo3.service.UserService@67f89fa3
@Bean的一些细节
为什么会有@Bean这个注解的出现呢?在spring3.0就开始出现了这个。
那么看一下xml中的使用方式。
工厂非静态方法
工厂非静态方法实例化:
public class NoStaticMethodFactory{
public UserDao createUserDao(){
return new UserDaoImpl();
}
}
配置文件:
<!-- 先配置工厂 -->
<bean id="noStaticMethodFactory" class="com.guang.factory.NoStaticMethodFactory"></bean>
<!-- 再配置UserDao -->
<!-- factory-bean是工厂bean的名字,使用的是工厂bean中的非静态方法createUserDao来创建出来的userDao组件 -->
<bean id="userDao" factory-bean="instanceFactory" factory-method="createUserDao"></bean>
对于这种bean来说,factory-bean的名字是instanceFactory,factory-method的名字是createUserDao。
也就是说是利用了factory工厂来实现的方式。
工厂静态方法
public class StaticMethodFactory{
public static UserDao createUserDao(){
return new UserDaoImpl();
}
}
配置文件:
<bean id="userDao" class="com.guang.bean.StaticMethodFactory" factory-method="createUserDao"></bean>
配置文件中指明了使用StaticMethodFactory类中的createUserDao方法来进行实例化bean;
对于这种使用方式来说,factory-bean的名字是没有的,factory-method的名字是createUserDao,但是beanclass是StaticMethodFactory,将这个类记录下来了。
对于@Bean来说,这里是一对一的关系。
首先,Spring会把@Bean修饰的方法解析成BeanDefinition:
- 如果方法不是static的,也就是静态方法,那么解析出来的BeanDefinition中:
- factoryBeanName为AppConfig所对应的beanName,比如"appConfig"
- factoryMethodName为对应的方法名,比如"aService"
- factoryClass为AppConfig.class
- 如果方法是static的,那么解析出来的BeanDefinition中:
- factoryBeanName为null
- factoryMethodName为对应的方法名,比如"aService"
- factoryClass也为AppConfig.class
在由@Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示factoryMethod是不是唯一的,在普通情况下@Bean生成的BeanDefinition的isFactoryMethodUnique为true,但是如果出现了方法重载,那么就是特殊的情况,比如:
@Bean
public static AService aService(){
return new AService();
}
@Bean
public AService aService(BService bService){
return new AService();
}
虽然有两个@Bean,但是肯定只会生成一个aService的Bean,那么Spring在处理@Bean时,也只会生成一个aService的BeanDefinition,比如Spring先解析到第一个@Bean,会生成一个BeanDefinition,此时isFactoryMethodUnique为true,但是解析到第二个@Bean时,会判断出来beanDefinitionMap中已经存在一个aService的BeanDefinition了,那么会把之前的这个BeanDefinition的isFactoryMethodUnique修改为false,并且不会生成新的BeanDefinition了。
并且后续在根据BeanDefinition创建Bean时,会根据isFactoryMethodUnique来操作,如果为true,那就表示当前BeanDefinition只对应了一个方法,那也就是只能用这个方法来创建Bean了,但是如果isFactoryMethodUnique为false,那就表示当前BeanDefition对应了多个方法,需要和推断构造方法的逻辑一样,去选择用哪个方法来创建Bean。
@Bean生成BeanDefinition的过程
直接来到对应的逻辑中来:
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
// @Bean生成BeanDefinition并注册
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
因为之前已经缓存了BeanMethod,现在获取得到所有的BeanMethod,开始循环遍历
直接看源码:
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
// 获取得到配置类
ConfigurationClass configClass = beanMethod.getConfigurationClass();
// 获取得到@Bean注解的元信息
MethodMetadata metadata = beanMethod.getMetadata();
// 获取得到@Bean的方法名称
String methodName = metadata.getMethodName();
// Do we need to mark the bean as skipped by its condition?
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
// 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);
}
// Has this effectively been overridden before (e.g. via XML)?
// 如果出现了两个@Bean修改的方法名字一样(比如方法重载了),则直接return,并且会把已经存在的BeanDefinition的isFactoryMethodUnique为false
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
// 如果beanName等于"appConfig",就会抛异常
if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
"' clashes with bean name for containing configuration class; please make those names unique!");
}
return;
}
// 开始生成@Bean对应的ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
// 如果@Bean方法是static关键字修饰的
if (metadata.isStatic()) {
// static @Bean method
if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
}
else {
// 设置beanClassName为当前的配置类的名字,也就是appconfig
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
}
beanDef.setUniqueFactoryMethodName(methodName);
}
else {
// 如果不是static关键字,那么这里会将BeanDefinition中的一个变量isFactoryMethodUnique设置为true
// 设置FactoryBeanName为appconfig
// instance @Bean method
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setUniqueFactoryMethodName(methodName);
}
// 设置FactoryMethod为当前方法
if (metadata instanceof StandardMethodMetadata) {
beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
}
// 设置为根据构造方法来进行注入
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
// 设置属性,跳过检查
beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
// 在这里来判断注入类型
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
// 是否自动注入!这里也来做了判断
boolean autowireCandidate = bean.getBoolean("autowireCandidate");
if (!autowireCandidate) {
beanDef.setAutowireCandidate(false);
}
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
String destroyMethodName = bean.getString("destroyMethod");
beanDef.setDestroyMethodName(destroyMethodName);
// Consider scoping
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
if (attributes != null) {
beanDef.setScope(attributes.getString("value"));
proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
}
// Replace the original bean definition with the target one, if necessary
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry,
proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = new ConfigurationClassBeanDefinition(
(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName);
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
configClass.getMetadata().getClassName(), beanName));
}
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
然后将其注入到beandefinitionmap中去保存,等到后续实例化操作。
什么时候来实例化@Bean标注的BeanDefinition的
在推断构造方法中有两行代码表示:
// @Bean对应的BeanDefinition
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
在这里走的逻辑类似推断构造方法,来找到最匹配的来选择执行哪个方法。
上面几个过程中都没有提到@Configuration必须出现的场景
下面来考虑一个问题,如下所示:
@ComponentScan("com.gunag.ioc.demo3")
public class AppConfig3 {
@Bean
public A a() {
A a = new A();
System.out.println("容器中的a是:"+a);
return a;
}
@Bean
public B b(A a) {
A a1 = a();
System.out.println("容器中的b持有的a是:"+a);
System.out.println("容器中的b持有的a1是:"+a1);
return new B();
}
}
如果是正常的话,我们想让b中可以使用容器中的a这个bean,但是如果不加@Configuration注解,那么b中获取得到的a就不是容器中的。
测试一下:
容器中的a是:com.gunag.ioc.demo3.service.A@3b192d32
容器中的a是:com.gunag.ioc.demo3.service.A@14899482
容器中的b持有的a是:com.gunag.ioc.demo3.service.A@3b192d32
容器中的b持有的a1是:com.gunag.ioc.demo3.service.A@14899482
但是如果想保持唯一,那么只需要在配置类上添加@Configuration注解
容器中的a是:com.gunag.ioc.demo3.service.A@34ce8af7
容器中的b持有的a是:com.gunag.ioc.demo3.service.A@34ce8af7
容器中的b持有的a1是:com.gunag.ioc.demo3.service.A@34ce8af7
这个@Configuratoin注解是必须要存在的。上面只是执行完了BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法,而没有执行postProcessBeanFactory方法,所以来看下这个方法中执行的逻辑。
这个方法执行到这里的时候,说明容器中的BeanDefinition已经全部添加完成了,但是对应的Bean还没有生成。
终于来到了熟悉的方法,那么只只需要看下这里的代理逻辑:
设置AppConfig中的beanclass设置成了AppConfig的代理类的类型
在这里来将AppConfig对应的BeanDefinition中的beanclass属性设置成了AppConfig的代理类的类型,所以最终来执行的时候,执行的是代理类中的逻辑。
加入说:
@Configuration
public class AppConfig{
@Bean
public A a(){
return new A();
}
}
在使用AppConfig来调用a方法的时候,肯定执行的是代理类中的逻辑。
在ConfigurationClassEnhancer类中已经指定了需要回调的方法:
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
对于加了@Configuration且其中proxyBeanMethods=true的情况下
那么看一下创建方法
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
创建代理类对象,那么在@Configuration标注的类上,且@Configuration属性proxyBeanMethods=true的情况下,在执行方法的时候,一定会执行到拦截方法中来:BeanMethodInterceptor中的intercept方法中来。
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
// enhancedConfigInstance是代理对象
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// To handle the case of an inter-bean method reference, we must explicitly check the
// container for already cached instances.
// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// This ensures that the semantics of calling a FactoryBean from within @Bean methods
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
// 如果代理对象正在执行的方法就是正在创建Bean的工厂方法,那就直接执行对应的方法得到对象作为Bean
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
if (logger.isInfoEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
}
// 注意这里传入的是代理对象,相当于在执行父类的方法,注意和Spring事务做区分
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 如果代理对象正在执行的方法不是正在创建Bean的方法,那就直接根据方法的名字去Spring容器中获取
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
这里有一行关键性代码:
isCurrentlyInvokedFactoryMethod(beanMethod)
判断是否是当前正在调用的FactoryMethod方法。为什么要这样子来做判断呢?
如下代码:
@ComponentScan("com.gunag.ioc.demo3")
@Configuration
public class AppConfig3 {
@Bean
public A a() {
A a = new A();
System.out.println("容器中的a是:"+a);
return a;
}
@Bean
public B b(A a) {
A a1 = a();
System.out.println("容器中的b持有的a是:"+a);
System.out.println("容器中的b持有的a1是:"+a1);
return new B();
}
}
如果当前正在创建的方法是a(),这里会来做一个记录(用集合保存当前@Bean方法创建中),表示当前创建A是由a()方法引起的;创建完成之后将这个方法移除;
当代用b方法来创建b的时候,这里来做一个记录b,表示是有b方法引起的;而在b在调用中,发现会调用类中的a方法,正在执行的方法和记录到集合中的方法是不一样的,那么spring将会走cglib的代理逻辑。
明确@Bean方法调用时机
是在实例化阶段才会来创建,那么继续走到
// @Bean对应的BeanDefinition
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
走到这个方法中来,最终执行到
bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
中来,然后下面里面的设置,最终执行到org.springframework.beans.factory.support.SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory, java.lang.Object, java.lang.reflect.Method, java.lang.Object...)方法中来
private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<>();
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
currentlyInvokedFactoryMethod.set(factoryMethod);
// factoryBean就是AppConfig的代理对象(如果加了@Configuration)
// factoryMethod就是@Bean修饰的方法
Object result = factoryMethod.invoke(factoryBean, args);
if (result == null) {
result = new NullBean();
}
return result;
}
finally {
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
else {
currentlyInvokedFactoryMethod.remove();
}
}
保存当前线程执行的是@Bean对应的方法。这里只是来进行设置。然后会来执行
Object result = factoryMethod.invoke(factoryBean, args);
因为factoryBean是一个代理的bean,所以会走到对应的intercepter方法中来,也就是一个回调方法。
那么接着执行:
// 如果代理对象正在执行的方法就是正在创建Bean的工厂方法,那就直接执行对应的方法得到对象作为Bean
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
.........
// 注意这里传入的是代理对象,相当于在执行父类的方法,注意和Spring事务做区分
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 如果代理对象正在执行的方法不是正在创建Bean的方法,那就直接根据方法的名字去Spring容器中获取
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
那么直接来看对应的处理逻辑:
private boolean isCurrentlyInvokedFactoryMethod(Method method) {
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}
public static Method getCurrentlyInvokedFactoryMethod() {
return currentlyInvokedFactoryMethod.get();
}
如果说当前factorybean中正在执行的方法和当前线程中保存的方法是一致的。那么直接去执行父类中的方法;
如果说factorybean中正在执行的方法和当前线程中保存的方法是不一致的,那么表示的是当前@Bean方法正在调用其他@Bean加的方法,那么就需要不需要来执行父类中的方法,直接去从容器中来进行获取。
为什么说@Bean标注的方法的参数值会从容器中来进行查找
在@Bean标注的方法中,会找到@Bean中的方法来进行查找。 最终会去容器中查找。
而因为对于@Bean方法和推断构造方法来说,找值都是去容器中找
this.beanFactory.resolveDependency(
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
所以在构造方法和@Bean对应的方法中,在查找bean的时候,可以使用@Qulifired来标注使用哪个名字的bean。