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 :包含字段 namevaluesource
  • PropertyValue :继承 BeanMetadataAttributeAccessor

    • 增加字段 namevaluesource

    • 增加字段 convertedconvertedValueconversionNecessaryresolvedTokens

PropertyValues

接口,表示可遍历的 PropertyValue

实现类 MutablePropertyValues

BeanWrapper

唯一实现类 BeanWrapperImpl

通过 BeanPropertyBindingResultDataBinder 关联

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);

这里的 readerConfigurationClassBeanDefinitionReader

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 的中间类

posted @ 2022-11-28 09:48  流星<。)#)))≦  阅读(33)  评论(0编辑  收藏  举报