2023-09-16  阅读(2)
原文作者:王伟王胖胖 原文地址: https://blog.csdn.net/wangwei19871103/article/details/104999560

ConfigurationClassParser的validate

为什么要讲这个,因为这里涉及到一个CGLIB动态代理的条件问题,GCLIB的代理原理是继承目标类,覆盖相应的方法,可能还有一些方法拦截器,所以对类和方法是有要求的。
所以他会进行配置类的遍历验证

    	public void validate() {
    		for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
    			configClass.validate(this.problemReporter);
    		}
    	}

ConfigurationClass的validate

具体验证是交给ConfigurationClass自身方法的。这里就是我们所说的Configuration注解的重要作用,可以进行类代理的增强。如果是Configuration注解的,默认proxyBeanMethods=true,后面就会进行GCLIB代理增强,所以这里要验证下元数据里有没有final修饰类,因为final修饰类是不可以被继承的,同时还要验证下所有的bean方法是可以被覆盖的。

    	//进行验证,如果是proxyBeanMethods=true,默认用GCLIB做代理,是继承的,所以不可以是final类
    	public void validate(ProblemReporter problemReporter) {
    		// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
    		Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
    		if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {//要GCLIB代理
    			if (this.metadata.isFinal()) {//配置类不可以是final的
    				problemReporter.error(new FinalConfigurationProblem());
    			}
    			for (BeanMethod beanMethod : this.beanMethods) {
    				beanMethod.validate(problemReporter);//验证方法可覆盖
    			}
    		}
    	}

BeanMethod的validate

这里就是验证bean注解的方法,如果是静态的,就没关系,立即返回,否则的话要判断是否可以覆盖。

    @Override
    	public void validate(ProblemReporter problemReporter) {
    		if (getMetadata().isStatic()) {//静态方法属于类的,不管
    			// static @Bean methods have no constraints to validate -> return immediately
    			return;
    		}
    
    		if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {
    			if (!getMetadata().isOverridable()) {//不能覆盖的话,也报错,因为不能GCLIB覆盖啦
    				// instance @Bean methods within @Configuration classes must be overridable to accommodate CGLIB
    				problemReporter.error(new NonOverridableMethodError());
    			}
    		}
    	}
    	//可以覆盖的条件,非静态且非final且不是私有的
    	@Override
    	public boolean isOverridable() {
    		return !isStatic() && !isFinal() && !isPrivate();
    	}

ConfigurationClassBeanDefinitionReader加载bean定义

每一个ConfigurationClass基本的加载流程

202309162311377771.png

前面我们把配置类解析好了,放进ConfigurationClass集合里,但是还没有进行bean定义,所以后面就是进行bean定义了,遍历所有的ConfigurationClass,加载bean定义。

    	public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    		TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    		for (ConfigurationClass configClass : configurationModel) {
    			loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    		}
    	}

loadBeanDefinitionsForConfigurationClass

ConfigurationClass中获取bean名字,如果发现是条件过滤的且注册器里有相关bean定义,就要删除。然后处理是否是被import进来的,处理bean注解方法的,处理ImportedResourcesImportBeanDefinitionRegistrar接口实现类的。

    	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()) {//是被import进来的
    			registerBeanDefinitionForImportedConfigurationClass(configClass);
    		}
    		for (BeanMethod beanMethod : configClass.getBeanMethods()) {//处理bean注解方法
    			loadBeanDefinitionsForBeanMethod(beanMethod);
    		}
    
    		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());//从ImportedResources加载bean定义
    		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());//从Registrars接口加载bean定义
    	}

registerBeanDefinitionForImportedConfigurationClass被import进来的

如果是被import进来的,直接将自己就封装成AnnotatedGenericBeanDefinition ,注册到注册器registry中。

    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.isTraceEnabled()) {
    			logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
    		}
    	}

loadBeanDefinitionsForBeanMethod

看起来这个方法好像很长,其实就是将bean注解的方法元数据取出来分析,分析bean注解,有没有别名啊,有没有跟xml配置冲突啊,封装成一个ConfigurationClassBeanDefinition,然后设置工厂方法名,获取bean注解的属性,设置初始化方法,销毁方法,是否自动装配,是否需要代理,是JDK动态代理,还是CGLIB动态代理,如果用了动态代理,除了目标bean定义会注册以外,代理bean也会被注册,具体源码暂时不深入。

    private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    		ConfigurationClass configClass = beanMethod.getConfigurationClass();
    		MethodMetadata metadata = beanMethod.getMetadata();//方法元数据
    		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;
    		}
    		//获取bean注解的属性
    		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);
    		}
    		//是否存在同名bean定义就不处理了,比如处理器扩展的,XML定义的
    		// Has this effectively been overridden before (e.g. via XML)?
    		if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
    			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;
    		}
    		//封装为ConfigurationClassBeanDefinition,表示是来自配置类里的bean定义
    		ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    		beanDef.setResource(configClass.getResource());//设置来源的类
    		beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));//设置元数据
    
    		if (metadata.isStatic()) {//静态的,设置BeanClass
    			// static @Bean method
    			if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
    				beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
    			}
    			else {
    				beanDef.setBeanClassName(configClass.getMetadata().getClassName());//设置配置类全限定名
    			}
    			beanDef.setUniqueFactoryMethodName(methodName);
    		}
    		else {
    			// instance @Bean method 实例的,设置工厂名,也就是配置类名
    			beanDef.setFactoryBeanName(configClass.getBeanName());
    			beanDef.setUniqueFactoryMethodName(methodName);//设置工厂方法的名字,也就是方法名
    		}
    		//如果方法元数据是标准方法元数据的话,就设置解析的工厂方法
    		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()) {//如果是自动装配,也就是BY_NAME 或者 BY_TYPE,再设置了一次自动装配模式
    			beanDef.setAutowireMode(autowire.value());
    		}
    		//自动装配候选,默认是true
    		boolean autowireCandidate = bean.getBoolean("autowireCandidate");
    		if (!autowireCandidate) {
    			beanDef.setAutowireCandidate(false);
    		}
    		//初始化方法 @PostConstruct 和 @PreDestroy  或者XML 或者 InitializingBean和 DisposableBean接口
    		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) {//如果范围不是no的话就要使用代理
    			BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
    					new BeanDefinitionHolder(beanDef, beanName), this.registry,
    					proxyMode == ScopedProxyMode.TARGET_CLASS);
    			beanDefToRegister = new ConfigurationClassBeanDefinition(
    					(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
    		}
    
    		if (logger.isTraceEnabled()) {
    			logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
    					configClass.getMetadata().getClassName(), beanName));
    		}
    		this.registry.registerBeanDefinition(beanName, beanDefToRegister);
    	}

loadBeanDefinitionsFromImportedResources

这个就是加载ImportedResourcesxml的,有两种加载类GroovyBeanDefinitionReaderXmlBeanDefinitionReader,然后用他们去加载xml,这里面就是各种xml解析,就暂时不深入了。

    private void loadBeanDefinitionsFromImportedResources(
    			Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
    
    		Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
    
    		importedResources.forEach((resource, readerClass) -> {
    			// Default reader selection necessary?
    			if (BeanDefinitionReader.class == readerClass) {
    				if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
    					// When clearly asking for Groovy, that's what they'll get...
    					readerClass = GroovyBeanDefinitionReader.class;
    				}
    				else {
    					// Primarily ".xml" files but for any other extension as well
    					readerClass = XmlBeanDefinitionReader.class;
    				}
    			}
    
    			BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
    			if (reader == null) {
    				try {
    					// Instantiate the specified BeanDefinitionReader
    					reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
    					// Delegate the current ResourceLoader to it if possible
    					if (reader instanceof AbstractBeanDefinitionReader) {
    						AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
    						abdr.setResourceLoader(this.resourceLoader);
    						abdr.setEnvironment(this.environment);
    					}
    					readerInstanceCache.put(readerClass, reader);
    				}
    				catch (Throwable ex) {
    					throw new IllegalStateException(
    							"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
    				}
    			}
    
    			// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
    			reader.loadBeanDefinitions(resource);
    		});
    	}

loadBeanDefinitionsFromRegistrars扩展点

这个是处理ImportBeanDefinitionRegistrar类型的import注解。具体就是去遍历被import进来的ImportBeanDefinitionRegistrarregisterBeanDefinitions自定义方法啦,这里也是一个扩展点,可以自己注册bean定义。

    private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    		registrars.forEach((registrar, metadata) ->
    				registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
    	}

扩展实战

我们举个例子:

202309162311383682.png

202309162311388283.png

202309162311393184.png
运行后回调到这里:

202309162311397325.png
扩展后注册进去了。

202309162311403496.png
至此对配置类的bean定义加载完成。主要步骤就是如果是被import进来的,就把自己注册进去,如果有bean注解方法的,把bean注解的方法作为bean定义注册进去,然后处理xml里的bean定义和ImportBeanDefinitionRegistrar实现类的扩展bean定义。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文