20220704 Spring 相关
@Configuration(proxyBeanMethods = false)
的作用
默认 proxyBeanMethods = true
,会为配置类生成 CGLIB 代理类。注意,不是为定义的 @Bean
生成代理类。
如果在 @Bean
方法 1 中引用了其他 @Bean
方法 2,此时需要被引用的 @Bean
方法 1 所在的配置类是代理类,对 @Bean
方法 2 所在的配置类不做要求
javadoc
指定是否应该代理 @Bean
方法以强制执行 bean 生命周期行为,例如即使在用户代码中直接调用 @Bean
方法的情况下,也可以返回共享的单例 bean 实例。
此功能需要方法拦截,通过运行时生成的 CGLIB 子类实现,该子类具有配置类及其方法不允许声明 final
等限制。
默认值为 true,允许通过配置类中的直接方法调用以及对此配置的 @Bean
方法的外部调用进行“bean 间引用”,例如从另一个配置类。
如果不需要这样做,因为每个特定配置的 @Bean
方法都是自包含的,并且设计为容器使用的普通工厂方法,请将此标志切换为 false
以避免 CGLIB 子类处理。
关闭 bean 方法拦截有效地单独处理 @Bean
方法,就像在非 @Configuration
类上声明时一样,也就是“@Bean Lite 模式”(参见 @Bean
的 javadoc)。因此,它在行为上等同于删除 @Configuration
构造型。
引入 @Bean 的方式
@Import
@ImportResource
- 引入 XML 配置
@Bean
@Component
及其派生注解- 派生
@Configuration
- 相关
@ComponentScan
- 派生
ImportSelector
接口- 子接口
DeferredImportSelector
- 子接口
ImportBeanDefinitionRegistrar
接口
@AliasFor
在扫描 Bean 定义时被解析
AnnotationTypeMapping#resolveAliasTarget(java.lang.reflect.Method, org.springframework.core.annotation.AliasFor, boolean)
用法参考:
-
@SpringBootApplication
-
@Bean
DataBinder
参考资料
Javadoc
允许在目标对象上设置属性值的绑定器,包括对验证和绑定结果分析的支持。
可以通过指定允许的字段模式、必填字段、自定义编辑器等来自定义绑定过程。
警告:数据绑定可能会通过暴露不应被外部客户端访问或修改的部分对象图而导致安全问题。因此,数据绑定的设计和使用应考虑安全性。有关详细信息,请参阅参考手册中有关 Spring Web MVC 和 Spring WebFlux 的数据绑定的专门部分。
绑定结果可以通过 BindingResult
接口检查,扩展 Errors
接口:参见 getBindingResult()
方法。缺少的字段和属性访问异常将被转换为 FieldErrors
,收集在 Errors
实例中,使用以下错误代码:
-
Missing field error: "required"
-
Type mismatch error: "typeMismatch"
-
Method invocation error: "methodInvocation"
默认情况下,绑定错误通过 BindingErrorProcessor
策略解决,处理缺失字段和属性访问异常:请参阅 setBindingErrorProcessor
方法。如果需要,您可以覆盖默认策略,例如生成不同的错误代码。
之后可以添加自定义验证错误。您通常希望将此类错误代码解析为适当的用户可见错误消息;这可以通过 org.springframework.context.MessageSource
解析每个错误来实现,它能够通过 org.springframework.context.MessageSource.getMessage(org.springframework.context.MessageSourceResolvable, java.util.Locale
方法解析 ObjectError
/ FieldError
。消息代码列表可以通过 MessageCodesResolver
策略自定义:见 setMessageCodesResolver
方法。 DefaultMessageCodesResolver
的 javadoc 说明了有关默认解析规则的详细信息。
此通用数据绑定器可用于任何类型的环境。
源码说明
实现了 PropertyEditorRegistry
, TypeConverter
接口
通过委托模式实现接口方法
protected PropertyEditorRegistry getPropertyEditorRegistry() {
if (getTarget() != null) {
return getInternalBindingResult().getPropertyAccessor();
}
else {
return getSimpleTypeConverter();
}
}
protected TypeConverter getTypeConverter() {
if (getTarget() != null) {
return getInternalBindingResult().getPropertyAccessor();
}
else {
return getSimpleTypeConverter();
}
}
包含字段:
@Nullable
private AbstractPropertyBindingResult bindingResult;
@Nullable
private SimpleTypeConverter typeConverter;
与 Validator
相关,绑定时,会进行校验
AttributeAccessor
org.springframework.core.AttributeAccessor
public interface AttributeAccessor {
void setAttribute(String name, @Nullable Object value);
@Nullable
Object getAttribute(String name);
// 如果根据 name 可以获取到值,不再调用 computeFunction
default <T> T computeAttribute(String name, Function<String, T> computeFunction) {
Assert.notNull(name, "Name must not be null");
Assert.notNull(computeFunction, "Compute function must not be null");
Object value = getAttribute(name);
if (value == null) {
value = computeFunction.apply(name);
Assert.state(value != null,
() -> String.format("Compute function must not return null for attribute named '%s'", name));
setAttribute(name, value);
}
return (T) value;
}
@Nullable
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
}
PropertyValue
org.springframework.beans.PropertyValue
保存单个 bean 属性的信息和值的对象。在此处使用对象,而不是仅将所有属性存储在以属性名称为键的映射中,可以提供更大的灵活性,并能够以优化的方式处理索引属性等。
请注意,该值不需要是最终所需的类型: BeanWrapper
实现应该处理任何必要的转换,因为该对象不知道将应用到的对象的任何信息。
实现类
-
AttributeAccessorSupport
:内部存储是LinkedHashMap
private final Map<String, Object> attributes = new LinkedHashMap<>();
-
BeanMetadataAttributeAccessor
:继承AttributeAccessorSupport
-
增加字段
source
-
增加了自己的一些方法,使属性值都是
BeanMetadataAttribute
BeanMetadataAttribute
:包含字段name
、value
、source
-
-
PropertyValue
:继承BeanMetadataAttributeAccessor
-
增加字段
name
、value
、source
-
增加字段
converted
、convertedValue
、conversionNecessary
、resolvedTokens
-
PropertyValues
接口,表示可遍历的 PropertyValue
实现类 MutablePropertyValues
BeanWrapper
唯一实现类 BeanWrapperImpl
通过 BeanPropertyBindingResult
与 DataBinder
关联
DataBinder#getPropertyAccessor
返回的就是 BeanWrapperImpl
,在 DataBinder#applyPropertyValues
方法中通过 BeanWrapperImpl
设置属性值
MyBean1 myBean1 = new MyBean1(1, "n1", new Date());
Map<String, Object> map = new HashMap<>();
map.put("name", "update name");
MutablePropertyValues propertyValues = new MutablePropertyValues(map);
DataBinder dataBinder = new DataBinder(myBean1);
dataBinder.bind(propertyValues);
System.out.println(myBean1);
BindingResult bindingResult = dataBinder.getBindingResult();
BeanPropertyBindingResult beanPropertyBindingResult = (BeanPropertyBindingResult) bindingResult;
ConfigurablePropertyAccessor propertyAccessor = beanPropertyBindingResult.getPropertyAccessor();
BeanWrapper beanWrapper = (BeanWrapper) propertyAccessor;
Spring 怎么读取 Bean 定义的
注册方法:org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
示例
public class MyMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext(MyMain.class);
for (String beanDefinitionName : annotationConfigApplicationContext.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
@Bean
public MyBean1 myBean1() {
return new MyBean1(1, "m1", new Date());
}
}
输出结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myMain
myBean1
注册 internal
的代码路径:
AnnotationConfigServletWebServerApplicationContext()
this.reader = new AnnotatedBeanDefinitionReader(this);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
实例化 AnnotatedBeanDefinitionReader
时注册了 internal
注册 myMain
的代码路径:
AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(java.lang.Class<?>...)
register(componentClasses);
注册 MyBean1
的代码路径:
AbstractApplicationContext#refresh
invokeBeanFactoryPostProcessors(beanFactory);
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
postProcessor.postProcessBeanDefinitionRegistry(registry);
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor#processConfigBeanDefinitions
this.reader.loadBeanDefinitions(configClasses);
这里的 reader
是 ConfigurationClassBeanDefinitionReader
ConfigurationClassPostProcessor#processConfigBeanDefinitions
判断是否为配置类
ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)
ConfigurationClassUtils#isConfigurationCandidate
如果类上注解了 @Component
、@ComponentScan
、@Import
、@ImportResource
,或者类中存在 @Bean
注解的方法
@Configuration
也属于 @Component
的派生注解
对 ImportBeanDefinitionRegistrar
接口进行了处理
Spring Boot 怎么读取 Bean 定义的
和 Spring
类似,同样是实例化 AnnotatedBeanDefinitionReader
时,注册了 internal
,使用 reader
注册了 source
,然后在 ConfigurationClassPostProcessor
中进行扫描注册
使用 BeanDefinitionLoader
作为使用 AnnotatedBeanDefinitionReader
的中间类