Skip to content

「属性别名」 和 「属性覆盖」 理解有误 #61

@justmehyp

Description

@justmehyp

小马哥这本书写的很好,使我收益匪浅,同时也很钦佩小马哥技术造诣之高。

最近看到 第 7 章「走向注解驱动编程(Annotation-Driven)」,很多之前稀里糊涂的地方,在书的指引下,加上动手试验和阅读相关资料之后,豁然开朗。

好了,开始提 Bug 吧 :》
我手上的书版次是:2019年3月第1版,印次是:2019年4月第2次印刷。

第 211 页第 1 段,关于“显性覆盖”,文档中并没有确认指出属性A和B是否在同一个注解中 是理解有误的,官方原文如下:

Explicit Overrides: if attribute A is declared as an alias for attribute B in a meta-annotation via @AliasFor, then A is an explicit override for B.

可以看到,属性 B 在 meta-annotation 中,所以 A 和 B 不在同一个注解中。
假如 A 和 B 在同一个注解,那么不能单向 A @AliasFor B,还需要 B @AliasFor A,但此时, A 和 B 之间不是"显性覆盖"的关系,而是 显性属性别名(Explicit Aliases) 的关系。

第 215 页末尾 和 第 216 页开头的示例,无法重现,我试了 Spring Boot 版本从 1.0.0.RELEASE 到 2.1.7.RELEASE 的几个版本,都无法重现。

书上的运行结果是不会报错,会输出:

Bean 名称: txManager, 对象: thinking.in.spring.boot.samples.spring5.bean.TransactionalServiceBean@52e6fdee
保存...
txManager2: 事务提交...

但我却抛异常了:

beanName: transactionalServiceBean, bean: com.example.springboot.annotationattribute.TransactionalServiceBean@1672fe87  
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: txManager,txManager2

我的代码如下:

TransactionalServiceBootstrap.java

@Configuration
@ComponentScan
@EnableTransactionManagement
public class TransactionalServiceBootstrap {

    public static void main(String[] args) {

        ApplicationContext ac = new AnnotationConfigApplicationContext(TransactionalServiceBootstrap.class);
        ac.getBeansOfType(TransactionalServiceBean.class).forEach((beanName, bean) -> {
            System.out.println("beanName: " + beanName + ", bean: " + bean);
            bean.save();
        });
    }

    @Bean("txManager")
    public PlatformTransactionManager txManager() {
        return new PlatformTransactionManager() {
            @Override
            public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
                return new SimpleTransactionStatus();
            }

            @Override
            public void commit(TransactionStatus status) throws TransactionException {
                System.out.println("txManager commit.");
            }

            @Override
            public void rollback(TransactionStatus status) throws TransactionException {
                System.out.println("txManager rollback.");
            }
        };
    }

    @Bean("txManager2")
    public PlatformTransactionManager txManager2() {
        return new PlatformTransactionManager() {
            @Override
            public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
                return new SimpleTransactionStatus();
            }

            @Override
            public void commit(TransactionStatus status) throws TransactionException {
                System.out.println("txManager2 commit.");
            }

            @Override
            public void rollback(TransactionStatus status) throws TransactionException {
                System.out.println("txManager2 rollback.");
            }
        };
    }
}

@TransactionalService
class TransactionalServiceBean {
    public void save() {
        System.out.println("saving...");
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Transactional
@Service("transactionalService")
@interface TransactionalService {
    String name() default "";

    String value() default "txManager";
}

实际上,无法重现,是符合我的预期的,因为 Spring 对 属性 value 做了特殊对待,「隐性覆盖」对 value 不起作用, Spring 源码位置见org.springframework.core.annotation.AnnotatedElementUtils.MergedAnnotationAttributesProcessor#postProcess 方法,摘取如下:

// Implicit annotation attribute override based on convention
else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
    overrideAttribute(element, annotation, attributes, attributeName, attributeName);
}

第 220 页第 1 段 末尾 因此@TransactionalService.name()与@Service.value()之间的关系被"Attribute Aliases and Overrides"一节定义为"隐性别名" 理解有误,官方对于"隐性别名"的说明是:

Implicit Aliases: if two or more attributes in one annotation are declared as explicit overrides for the same attribute in a meta-annotation via @AliasFor, they are implicit aliases.

可以看到,in one annotation 才是属性别名,@TransactionalService.name()与@Service.value() 显然不在同一个注解中。

我对「属性别名」和「属性覆盖」的理解总结如下:
-「属性别名」只能发生在同一个注解内部
-「属性覆盖」只能发生在注解之间

另外,我还将这段时间的学习和理解记录在这篇文章中https://github.com/justmehyp/note-spring-boot/blob/master/note/Annotation-Programming-Model.md,不一定都正确,如有错误,还望帮忙提醒纠正,多谢多谢!

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions