1

这有点难以解释......希望这个问题不是含糊的......

您可以查看代码以了解想法...

ClassA.java

public class ClassA {
    @Autowired
    InterA abcd;
    public void dododo() {
        abcd.doit();
    }
}

ClassB.java

@Component
public class ClassB implements InterA {
    @Override
    public void doit() {
        System.out.println("hoo hoo");
    }
}

ClassC.java

@Component("classc")
public class ClassC {
    public void doFromAbove() {
        ClassA cls = new ClassA();
        cls.dododo();
    }
}

接口InterA.java

public interface InterA {
    public void doit();
}

配置ClassConfig.java(在同一个包的其他java类文件上)

@Configuration
@ComponentScan
public class ClassConfig {
}

主要方法

public static void main(String[] args) {
    try(AbstractApplicationContext appctx = new AnnotationConfigApplicationContext(ClassConfig.class)) {
        ClassC obj = (ClassC) appctx.getBean("classc");
        obj.doFromAbove();
    }
}

当我执行 main 方法时,ClassA没有注入 Autowired 字段“abcd”并导致NullPointerException

它仅在我声明ClassA为 a@Component并获得它的 bean 时才有效......间接自动装配不会发生

我应该解耦ClassA并使ClassC一切松散耦合吗?

是否有任何简单的注释可以用来告诉 Spring 自动注入 @Autowired 字段,即使对象是以紧密耦合的方式创建的?

注意 请不要告诉我使用 ApplicationContextClassC来创建ClassA.

任何可以找到答案的Spring Geek?

4

5 回答 5

1

问题在于ClassC

    ClassA cls = new ClassA();

如果你ClassA像这样调用构造函数,Spring 不会发挥它的魔力。如果您需要一个ClassA带有注入字段的实例,请向 Spring 询问一个实例(使用注入或getBean())。

(为避免出现null假定注入的字段,我建议使用构造函数注入。)

于 2017-07-07T06:50:30.397 回答
1

经过激烈的谷歌搜索,Spring Documentation Skimming,我相信这个困境有更多可能的解决方案......

可能的解决方案:

  1. 将 JSR 330Provider<T>@Autowired
  2. FactoryBean<T>与 in 中的初始化代码一起使用getObject()(但工厂返回的 bean 不是 spring 管理的,因此原型类中的任何自动装配字段都将返回 NullPointerException)
  3. 使用查找方法注入(包括 CGLIB 库)(我不喜欢这个,因为它修改了编译的代码,听起来像是创建了抽象类的 bean 对象)(违反了 Java 的纯度)
  4. 实现ApplicationContextAware接口并获取上下文对象(不推荐)
  5. 自动接线ApplicationContext和使用getBean()(不推荐)

以上方法中最微妙的方法是 JSR330 Provider

A类

@Component("classa")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA implements InterB {
    private static int counter=0;

    private int objectid = 0;
    @Autowired
    InterA abcd;

    public ClassA() {
        super();
        this.objectid = ++counter;
    }

    @Override
    public void dododo() {
        System.out.println("instance number "+objectid++);
        abcd.doit();
    }
}

B类

@Component
public class ClassB implements InterA {
    @Override
    public void doit() {
        System.out.println("hoo hoo");
    }

}

C类

@Component("classc")
public class ClassC {

    @Autowired
    Provider<InterB> classAPrototypeobj;

    public void doFromAbove() {
        //you can do a for loop here and get a set of objects for use
        InterB cls = (InterB) classAPrototypeobj.get();
        InterB cls1 = (InterB) classAPrototypeobj.get();
        cls.dododo();
        cls1.dododo();
        System.out.println(cls);
        System.out.println(cls1);
    }
}

现在它可以完美运行,并且初始化的对象也是弹簧管理的......

注意: 必须在 maven pom.xml 中设置 JSR330 依赖项

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
于 2017-07-12T09:43:02.273 回答
0

在 Spring 容器中声明的 bean(通过 XML 或类似的注解@Component)是Spring 管理的- Spring 将处理它们,将确保它们在您通过请求它们时存在ApplicationContext.getBean()并且它们的依赖项也被注入。

当您自己创建一个实例 ( cls = new ClassA()) 时,该实例不是 Spring 管理的,Spring 不会对它做任何事情。事实上,Spring 不会(也不能)知道对象的存在。

一些混淆可能源于您使用 Spring 注释对进行注释 - 但它实际上是 Java 中实际使用的对象(实例);即使类被注解,注解也只适用于由 Spring 创建和管理的实例。

于 2017-07-07T07:03:56.177 回答
0

为什么不使用@Lookup 注解?根据接受的答案,我假设您ClassA每次都需要一个新实例ClassC

@Component("classA")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA {
    @Autowired
    InterA abcd;

    private ObjectA objA;

    private ObjectB objB;

    public ClassA(objA, objB) {
        this.objA = objA;
        this.objB = objB;
    }

    public void dododo() {
        abcd.doit();
    }
}

@Component("classc")
public class ClassC {
    public void doFromAbove() {
        ClassA cls = getNewInstanceOfClassA(objA, objB);
        cls.dododo();
    }

    @Lookup("classA")
    private getNewInstanceOfClassA(ObjectA objA, ObjectB objB) {
        //Spring creates a runtime implementation of this method
        return null;
    }
}

其余的类实现保持不变。我在实现中包含了 objA 和 objB 以便清楚地了解注入构造函数参数。

所以main方法得到一个classC的spring bean并调用doFromAbove()方法。这反过来调用getNewInstanceOfClassA方法,该方法返回一个带有构造函数参数 objA、objB 的 classA 类型的 spring bean。由于我们将其注释为原型 bean,因此每次调用此方法时都会得到一个新的 classA 实例。你不需要实现getNewInstanceOfClassA方法。Spring 在运行时添加它自己的代码。

本质上,您的问题归结为在单例 bean 中注入原型 bean。查找注释是解决该问题的最佳方法。

于 2017-07-20T05:22:12.333 回答
0

如果启用加载时编织,则可以使用@Configurable,这将使对象的每个新实例成为托管弹簧组件,因此您使用的新语句将起作用。

除此之外,您可以创建一个原型范围的 bean 定义和一个引用该 bean 的工厂 bean,这意味着它每次都会给您一个新的 bean,因此您将注入工厂并且只为新实例调用 get 方法。

于 2017-07-08T04:31:09.850 回答