【问题标题】:@PostConstruct of abstract ancestors are not invoked不调用抽象祖先的@PostConstruct
【发布时间】:2015-06-29 11:47:35
【问题描述】:

我正在编写一个 JAX-RS 库(不是应用程序)。

我有:

abstract class A {

    @PostConstruct
    private void constructed_a() {} // not invoked

    @Inject
    private Some some;
}


public abstract class B extends A {

    @PostConstruct
    private void constructed_b() {} // not invoked
}

和测试类:

@Path("c")
public class C extends B {

    @PostConstrct
    private void constructed_c() {} // invoked
}

我正在使用 jersey 测试框架 v2.17 进行测试

我发现只有constructed_c()被调用,而那些在祖先中定义的方法没有被调用。请注意,在类A 中使用@Inject 声明的字段(some) 已正确注入。

这正常吗?我该怎么办?


结论

我使用嵌入式 glassfish 进行了测试,发现正如 Antonin Stefanutti 指出的那样,这些回调方法按预期顺序调用。

constructed_a()
constructed_b()
constructed_c()

【问题讨论】:

    标签: java unit-testing jersey jax-rs cdi


    【解决方案1】:

    根据JSR 318 - Interceptors 1.2 规范的在目标类上声明的拦截器的调用顺序部分:

    在目标类或其超类上声明的拦截器方法 按以下顺序调用:

    • 如果目标类具有超类,则会调用在这些超类上定义的任何拦截器方法,首先是最通用的超类。
    • 调用目标类本身的拦截器方法(如果有)。

    如果一个拦截器方法被另一个方法覆盖(不管 是否该方法本身是一个拦截器方法),它不会 被调用。

    这意味着在编写库/框架时,可以在父类和子类中使用@PostConstruct生命周期回调的同时实现可扩展性。

    该机制用于 Camel CDI 扩展,该扩展在 https://github.com/astefanutti/camel-cdi/blob/b6f52d91b247e36eefb6f3ecde61016d681d3535/impl/src/main/java/org/apache/camel/cdi/CdiCamelContext.java#L37 中使用 @PostConstruct 生命周期回调声明默认 Camel 上下文

    并且可以由用户扩展,例如在声明自己的 @PostConstruct 生命周期回调的 https://github.com/astefanutti/camel-cdi/blob/b6f52d91b247e36eefb6f3ecde61016d681d3535/envs/se/src/main/java/org/apache/camel/cdi/se/bean/CustomLifecycleCamelContext.java#L37 中。

    两者都被容器按照指定的顺序调用。

    这意味着从设计的角度来看,您的方法是正确的。但是,由于 Jersey 依赖注入是基于 HK2 而不是 CDI,并且依赖于像 jersey-gf-cdi 这样的桥接器,因此该级别可能存在问题。

    【讨论】:

    • 问题:我看到CustomLifecycleCamelContext 明确覆盖了postConstruct() 而没有super.postConstruct()?根据您引用的规范,不应执行CdiCamelContextpostConstruct() 中定义的代码。应该吗?
    • 父类中的postConstruct() 方法是packaged-private(默认可见性)。因此它不能从子类访问,也不被视为被覆盖。
    • 我的短途旅行结束了here。谢谢@AntoninStefanutti
    • 恐怕我没有关注。我承认,我没有运行 Jin Kwon 的示例,但根据他发布的内容,@PostConstruct 函数不会通过具有不同的方法名称来覆盖。我们是说没有调用@PostConstruct 父函数的原因与@PostConstruct 本身无关吗?没有被调用的函数与 Jersey 框架的不当使用有关?
    【解决方案2】:

    注解不会被继承。您必须为每个子类创建一个 @PostConstruct 注释方法。

    为了帮助/提醒开发者,您可能需要一个postConstruct() 方法,并希望开发者按照其名称的建议对其进行适当的注释:

    public abstract class A {
    
        @PostConstruct // annotation here has no value except as a reminder
        public abstract void postConstruct();  
    

    【讨论】:

    • 但是A#constructed_a()一定有事情发生。你建议让它可覆盖,子类必须调用super.postConstruct()?
    • @JinKwon 是的。容器只会调用被实例化类的@PostConstruct注解方法(不是超类的注解方法)
    • 还需要注意的是Only one method can be annotated with this annotation。所以我猜这也涉及到继承问题。根据我的测试,如果你去掉 C 的 PostConstruct,B 将被调用
    • @peeskillet 当我从 A 和 B 中删除这两个 abstract 时。并且仅调用 constructed_a()。老实说,我真的不明白。
    • @JinKwon 我想说的是,整个继承链只会调用一个带注释的方法。最接近创建类型的将被调用。如果 C 是创建的类型,并且它有一个带注释的方法,则该方法将被调用。如果您从 C 中删除注释,则将调用带有注释的链的下一个 B's。如果你从 B 和 C 中删除注释,那么 A 将被调用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 2012-06-25
    • 2014-02-14
    • 1970-01-01
    相关资源
    最近更新 更多