繁体   English   中英

Spring 服务定位器,无需自动装配

[英]Spring service locator without autowiring it

有这个代码:

public class ClassA {

    private InterfaceB interfaceB;

    private int a

    private int b;

    public ClassA(int a, int b) {
       this.a = a;
       this.b = b;
    }
}

我的应用程序中可能有多个ClassA对象,它们是在运行时按需创建的。 但是它们都应该使用相同的InterfaceB class 的具体实现( InterfaceB有几种实现,但根据所使用的平台,只有一种在运行时使用)。 并且应用程序中应该只有一个InterfaceB object(单例类)。

ab构造函数参数已知时,我无法自动装配interfaceB ,因为ClassA对象是在运行时创建的。

我怎么能在这里使用 Spring 框架来实现服务定位器模式? 我的计划是在ClassA中实例化服务定位器并使用它来获取InterfaceB object。

您可以创建一个额外的 class 来创建您的ClassA并且它将持有对interfaceB的引用。 例如:

@Component
public class ClassAFactory {

    @Autowired
    private InterfaceB interfaceB;

    public ClassA create(int a, int b) {
       return new ClassA(a, b, interfaceB);
    }
}

在这种情况下,您必须扩展ClassA以传递interfaceB 然后在你的代码中的某个地方你可以:

@Autowired
private ClassAFactory factory ;

...

ClassA classA = factory.create(1,1); 

我认为您不需要服务定位器模式,在现代 spring 驱动的应用程序中,它通常不再需要。

我将尝试从 Spring 框架的集成角度解决您的所有陈述:

我的应用程序中可能有多个 ClassA 对象,它们是在运行时按需创建的。

Spring 是一个运行时框架,所以一切都是在运行时创建的。 如果您需要许多由需求创建的对象,您可以将 ClassA 声明为 spring bean 和 scope 原型。 其他 bean 可以注入这个原型 bean。 如果您知道在应用程序启动期间将创建哪些实例,另一种可能的方法是定义许多相同类型的 bean,并在注入期间使用 spring 的限定符功能来区分它们。

但是它们都应该使用相同的 InterfaceB class 的具体实现(InterfaceB 有几种实现,但根据所使用的平台,只有一种在运行时使用)。

这意味着InterfaceB可以是常规的 singleton,但是,给定不同的实现,您可以定义如下内容:

@Configuration 
public class MyConfiguration {

    @Bean
    @ConditionalOnProperty(name="myprop", havingValue="true")
    public InterfaceB interfaceBImpl1() {
        return new InterfaceBImpl1();
    }

    @Bean
    @ConditionalOnProperty(name="myprop", havingValue="false")
    public InterfaceB interfaceBImpl2() {
        return new InterfaceBImpl2();
    }
}

当 a 和 b 构造函数参数已知时,我无法自动装配 interfaceB,因为 ClassA 对象是在运行时创建的。

其实可以,没有问题。 将classA的bean定义为原型。

还要检查 spring 的@Lazy注释,以防您只想在第一次调用时初始化该 classA 的实例。


public class ClassA {
  /// fields ///

  public ClassA(InterfaceB intefaceB, int a, int b) {
    this.intefaceB = intefaceB;
    this.a = a;
    this.b = b;
  }
}
@Configuration 
class MyConfig {

   @Bean
   @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
   public ClassA classA(InterfaceB bImpl, int a, int b) {
      return new ClassA(bImpl, int a, int b);
   }
}

更新 1

根据OP的评论:

这是工作示例:

在 pom.xml 中添加以下依赖项:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

它只包含接口,没有传递依赖

然后根据您在评论中解释的用例:


public class InterfaceB {
}

public class ClassA {
    private final InterfaceB interfaceB;

    public ClassA(InterfaceB interfaceB) {
        this.interfaceB = interfaceB;
    }

    public void doSomething() {
        System.out.println("Doing something on instance: [ " + this + " ]. The interface B instance is [ "+ interfaceB + " ]");
    }
}


public class ServiceA {

    private final List<ClassA> classAList;
    private Provider<ClassA> classAProvider;

    public ServiceA(Provider<ClassA> classAProvider) {
        this.classAProvider = classAProvider;
        this.classAList = new ArrayList<>();
    }

    public void addNewObject() {
        ClassA newObj = classAProvider.get();
        classAList.add(newObj);
    }

    public void doWithAllElementsInList() {
        classAList.forEach(ClassA::doSomething);
    }
}

Spring 配置如下所示:

public class SingletonWithPrototypesConfig {

    @Bean
    public ServiceA serviceA(Provider<ClassA> classAProvider) {
        return new ServiceA(classAProvider);
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public ClassA classA(InterfaceB interfaceB) {
        return new ClassA(interfaceB);
    }

    @Bean
    public InterfaceB interfaceB() {
        return new InterfaceB();
    }
}

以及从应用程序上下文获取服务 A 的主要 class (在您的情况下,它可能应该是 controller 或任何其他业务流程):

public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SingletonWithPrototypesConfig.class);
        ServiceA serviceA = ctx.getBean(ServiceA.class);
        serviceA.doWithAllElementsInList(); // won't print anything, 0 elements in the list
        System.out.println("---------");
        serviceA.addNewObject();
        serviceA.addNewObject();
        serviceA.doWithAllElementsInList();
    }

在最后的打印说明中 ClassA 实例不同,但 interfaceB 是相同的共享实例。

旁注:Provider 已经与 spring 集成,它位于新 jar 的javax.inject.Provider中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM