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基本的加载流程
前面我们把配置类解析好了,放进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
注解方法的,处理ImportedResources
和ImportBeanDefinitionRegistrar
接口实现类的。
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
这个就是加载ImportedResources
的xml
的,有两种加载类GroovyBeanDefinitionReader
和XmlBeanDefinitionReader
,然后用他们去加载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进来的ImportBeanDefinitionRegistrar
的registerBeanDefinitions
自定义方法啦,这里也是一个扩展点,可以自己注册bean
定义。
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
扩展实战
我们举个例子:
运行后回调到这里:
扩展后注册进去了。
至此对配置类的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] ,回复【面试题】 即可免费领取。