【问题标题】:Micronaut: problem with getting bean from contextMicronaut:从上下文中获取 bean 的问题
【发布时间】:2019-11-22 09:14:01
【问题描述】:

我有一个包含注入和强制(最终)字段的类。通常,我可以使用 MicronautBeanFactory.getBean(type) 或 BeanContext.getBean(type) 从上下文中获取 bean,但在这种情况下,我必须传递 type 和 args。

我为此创建了简单的测试

@MicronautTest
public class ETLExecutorTest {

    @Inject
    private MicronautBeanFactory micronautBeanFactory;

    @Test
    void testGetBean() {
        Object[] args = new Object[] {"name", "spec", 1L};
        ObjectInstance instance = micronautBeanFactory.getBean(ObjectInstance.class, args);
    }
}

对象(bean)代码

@Prototype
public class ObjectInstance {

    @Inject
    private ObjectStorage objectStorage;

    private final String name;
    private final String spec;
    private final Long id;

    public ObjectInstance(String name, String spec, Long id) {
        this.name = name;
        this.spec = spec;
        this.id = id;
    }
}

当我运行它时,我收到异常

io.micronaut.context.exceptions.DependencyInjectionException:无法为类的参数 [name] 注入值:com.ObjectInstance 消息:找到多个可能的 bean 候选者:[java.lang.String, java.lang.String, java.lang.String] 采用的路径:new ObjectInstance([String name],String specName,Long accountId) 在 io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:1016) 在 com.$TableInstanceDefinition.build(未知来源) 在 io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1598) 在 io.micronaut.context.DefaultBeanContext.getScopedBeanForDefinition(DefaultBeanContext.java:2076) 在 io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1991) 在 io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1963) 在 io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:610) 在 io.micronaut.spring.context.factory.MicronautBeanFactory.getBean(MicronautBeanFactory.java:264) 原因:io.micronaut.context.exceptions.NonUniqueBeanException:找到多个可能的 bean 候选者:[java.lang.String, java.lang.String, java.lang.String] 在 io.micronaut.context.DefaultBeanContext.findConcreteCandidate(DefaultBeanContext.java:1701) 在 io.micronaut.context.DefaultApplicationContext.findConcreteCandidate(DefaultApplicationContext.java:395) 在 io.micronaut.context.DefaultBeanContext.lastChanceResolve(DefaultBeanContext.java:2289) 在 io.micronaut.context.DefaultBeanContext.findConcreteCandidateNoCache(DefaultBeanContext.java:2212) 在 io.micronaut.context.DefaultBeanContext.lambda$findConcreteCandidate$57(DefaultBeanContext.java:2155) 在 io.micronaut.core.util.clhm.ConcurrentLinkedHashMap.lambda$compute$0(ConcurrentLinkedHashMap.java:721) 在 java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) 在 io.micronaut.core.util.clhm.ConcurrentLinkedHashMap.compute(ConcurrentLinkedHashMap.java:733) 在 io.micronaut.core.util.clhm.ConcurrentLinkedHashMap.computeIfAbsent(ConcurrentLinkedHashMap.java:710) 在 io.micronaut.context.DefaultBeanContext.findConcreteCandidate(DefaultBeanContext.java:2154) 在 io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1943) 在 io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1082) 在 io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:1007)

我也尝试做另一个测试,但在这种情况下,我收到的对象没有注入字段

@MicronautTest
public class ETLExecutorTest {

    @Inject
    private BeanContext beanContext;

    @Test
    void testGetBean() {
        Object[] args = new Object[] {"name", "spec", 1L};
        BeanDefinition<ObjectInstance> definition = beanContext.getBeanDefinition(ObjectInstance.class);
        ObjectInstance instance =  definition.getConstructor().invoke(args); // there are no injections here: ObjectStorage of instance = null.
    }
}

你能告诉我,我做错了什么吗???

【问题讨论】:

    标签: java dependency-injection micronaut


    【解决方案1】:

    micronaut 试图通过 constructor 创建 bean ObjectInstance 但找不到要注入的 String name,看起来这只是 ObjectInstance 的一个简单字段,在这种情况下,它按预期工作:

    io.micronaut.context.exceptions.DependencyInjectionException:无法为参数 [name] 注入值

    如果你添加一个默认构造函数,那么ObjectInstance会被创建,你可以通过beanContext.getBean(ObjectInstance.class)获取bean:

    @Prototype
    public class ObjectInstance {
    
        @Inject
        private ObjectStorage objectStorage;
    
        private String name;
        private String spec;
        private Long id;
    
    
        public ObjectInstance() {}
    
        public ObjectInstance(String name, String spec, Long id) {
            this.name = name;
            this.spec = spec;
            this.id = id;
        }
    
    }
    

    还要注意MicronautBeanFactory实现ListableBeanFactory,这是为了与Spring集成

    附:我建议你改变你的代码结构,POJO 不应该包含beans

    【讨论】:

    • 是的,您对 MicronautBeanFactory 的看法是正确的——它与 spring 结合在一起。当我们查看 MicronautBeanFactory.getBean() 时,我们会看到它抛出 org.springframework.beans.BeansException。是的,您的报价(默认构造函数)是解决它的好方法。 @tsarenkotxt 感谢您的建议。我的第二个测试如何将 args 应用于 contrustor,但没有注入 ObjectStorage?我们可以强制 micronaut 在definition.getConstructor().invoke(args); 期间注入这个 bean 吗???
    • 你可以使用.invoke(args),但在这种情况下,你得到的只是一个不在上下文中的实例,如果这个选项在你可以注入ObjectStorage到@987654336之后适合你的测试@通过设置器objectInstance.setObjectStorage(beanContext.getBean(ObjectStorage.class)),您也可以手动将ObjectInstance注入上下文beanContext.registerSingleton(objectInstance)。老实说,我不确定这是否是一个好的解决方案force
    • 我使用了你的第一个想法。非常感谢。
    • 我在 Micronaut 2.2.0 上尝试了这些建议,发现您应该使用 @Parameter 注释来注释构造函数参数,以便在构造时考虑它们,就像在 public ObjectInstance(@Parameter String name, @Parameter String spec, @Parameter Long id) 中一样。然后你可以创建一个 Bean 并使用 ApplicationContext 中的 createBean 方法将其放入上下文中,如applicationContext.createBean(ObjectInstance.class, arg1, arg2, arg3)
    猜你喜欢
    • 1970-01-01
    • 2019-12-19
    • 2021-02-09
    • 1970-01-01
    • 2021-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-13
    相关资源
    最近更新 更多