首页 技术 正文
技术 2022年11月18日
0 收藏 557 点赞 4,485 浏览 12943 个字

总结

  1. @Configuration注解的Bean,在BeanDefinition加载注册到IOC容器之后,进行postProcessBeanFactory处理时会进行CGLIB动态代理
  2. 将@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean等直接注解的类的BeanDefinition,是在ConfigurationClassParser#parse()中直接进行加载注册
  3. 通过ConfigurationClassBeanDefinitionReader#loadBeanDefinitions()开始将@Configuration注解类内部@Import、@Bean进行BeanDefinition的加载注册

简单例子

@Configuration
public class ConfigTest {
@Bean
public ConfigBean configBean() {
return new ConfigBean();
}
}
public class ConfigBean {......}

对@Configuration的注解类进行CGLIB动态代理

调用链:

AbstractApplicationContext#refresh() –> AbstractApplicationContext#invokeBeanFactoryPostProcessors() –> PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors() –> PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors() –> ConfigurationClassPostProcessor#postProcessBeanFactory() –> ConfigurationClassPostProcessor#enhanceConfigurationClasses()

在@Configuration注解的类attributes中有<org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass, <org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass, full>>值(具体怎么通过反射从class文件获取@Configuration attributes,详见前文《Spring源码之注解的原理》)

Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);的值为full

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// 执行到@Configuration注解类,configClassAttr的value为full
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
//true,执行下面put逻辑
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
enhanceConfigClasses.end();
return;
}
if (IN_NATIVE_IMAGE) {
throw new BeanDefinitionStoreException("@Configuration classes need to be marked as " +
"proxyBeanMethods=false. Found: " + configBeanDefs.keySet());
} // 进行CGLIB动态代理
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.getBeanClass();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}

ConfigurationClassParser#parse()扫描出configClasses

springboot启动时,在AbstractApplicationContext#refresh()中invokeBeanFactoryPostProcessors(beanFactory)会将@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean注解的类进行生成BeanDefinition,并加载注册。并将ConfigurationClass进行缓存

private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
this.configurationClasses.put(configClass, configClass);
}

详见前文《Spring源码之IOC容器创建、BeanDefinition加载和注册和IOC容器依赖注入》

loadBeanDefinitions处理

调用链:

AbstractApplicationContext#refresh() –> AbstractApplicationContext#invokeBeanFactoryPostProcessors() –> PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors() –> PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors() –> ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()–> ConfigurationClassPostProcessor#processConfigBeanDefinitions() –> ConfigurationClassBeanDefinitionReader#loadBeanDefinitions() –> ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass()

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;
} //@Configuration注解内部@Import注解方法处理
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//@Configuration注解内部@Bean注解方法处理
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

处理Bean的一系列属性后,向IOC容器中开始注册。this.registry.registerBeanDefinition(),注册逻辑可详见前文。

/**
* Read the given {@link BeanMethod}, registering bean definitions
* with the BeanDefinitionRegistry based on its contents.
*/
@SuppressWarnings("deprecation") // for RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE
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;
}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)?
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 beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));if (metadata.isStatic()) {
// 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()) {
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);
}

@Configuration CGLIB增强的功能

https://www.cnblogs.com/fnlingnzb-learner/p/10762905.html

https://blog.csdn.net/weixin_42997554/article/details/104578710

例子:
@Component
public class ConfigTest {
@Bean
public ConfigBean configBean() { ConfigBean configBean = new ConfigBean();
System.out.println(configBean + "-----------@Component");
return configBean;
} @Bean ConfigBean2 configBean2() {
return new ConfigBean2(configBean());
}
}
public class ConfigBean {}
public class ConfigBean2 {
public ConfigBean2(ConfigBean configBean) {
System.out.println(configBean + "-------configBean2");
}
}

打印:

com.java.study.spring.bean.configuration.ConfigBean@1d8e2eea-----------@Component
com.java.study.spring.bean.configuration.ConfigBean@240139e1-----------@Component
com.java.study.spring.bean.configuration.ConfigBean@240139e1-------configBean2

换成@Configuration注解时:

@Configuration
public class ConfigTest {
@Bean
public ConfigBean configBean() { ConfigBean configBean = new ConfigBean();
System.out.println(configBean + "-----------@Component");
return configBean;
} @Bean ConfigBean2 configBean2() {
return new ConfigBean2(configBean());
}
}

打印:

com.java.study.spring.bean.configuration.ConfigBean@1d81e101-----------@Component
com.java.study.spring.bean.configuration.ConfigBean@1d81e101-------configBean2

结论:

@Configuration通过CGLIB进行增强时,方法里面@Bean的对象都会和@Configuration注解的类scope一样,是单例的。@Component则会创建多个对象。

源码

在AbstractAutowireCapableBeanFactory#createBeanInstance时

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
}

会调用ConfigurationClassEnhancer的内部类BeanMethodInterceptor的intercept拦截

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);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.
// 第一次创建
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

第二次实例化ConfigBean时,isCurrentlyInvokedFactoryMethod(beanMethod)为false,走入resolveBeanReference方法

通过getBean直接从容器中获取

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,ConfigurableBeanFactory beanFactory, String beanName) {Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
beanFactory.getBean(beanName));
}
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,965
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,486
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,331
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,114
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,747
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,781