简体   繁体   English

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

[英]Spring service locator without autowiring it

Having this code:有这个代码:

public class ClassA {

    private InterfaceB interfaceB;

    private int a

    private int b;

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

There could be several objects of ClassA in my application, created on demand at runtime.我的应用程序中可能有多个ClassA对象,它们是在运行时按需创建的。 But all of them should use the same concrete implementation of InterfaceB class (there are several implementations of InterfaceB but only one is used at runtime depending on the platform used).但是它们都应该使用相同的InterfaceB class 的具体实现( InterfaceB有几种实现,但根据所使用的平台,只有一种在运行时使用)。 And there should be only one InterfaceB object in the application (singleton class).并且应用程序中应该只有一个InterfaceB object(单例类)。

I can not autowired interfaceB as ClassA objects are created at runtime when a and b constructor parameters are known.ab构造函数参数已知时,我无法自动装配interfaceB ,因为ClassA对象是在运行时创建的。

How could I use Spring Framework to implement the service locator pattern here?我怎么能在这里使用 Spring 框架来实现服务定位器模式? My plan is to instantiate the service locator in ClassA and use it to get the InterfaceB object.我的计划是在ClassA中实例化服务定位器并使用它来获取InterfaceB object。

You could create an additional class that will be creating your ClassA and it will be holding a reference to an interfaceB .您可以创建一个额外的 class 来创建您的ClassA并且它将持有对interfaceB的引用。 For example:例如:

@Component
public class ClassAFactory {

    @Autowired
    private InterfaceB interfaceB;

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

In this case you have to extend the ClassA to pass an interfaceB .在这种情况下,您必须扩展ClassA以传递interfaceB Then somewhere in your code you could:然后在你的代码中的某个地方你可以:

@Autowired
private ClassAFactory factory ;

...

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

I don't think you need a service locator pattern, in the modern spring driven applications it usually not required anymore.我认为您不需要服务定位器模式,在现代 spring 驱动的应用程序中,它通常不再需要。

I'll try to address all your statements from the Spring Framework's integration standpoint:我将尝试从 Spring 框架的集成角度解决您的所有陈述:

There could be several objects of ClassA in my application, created on demand at runtime.我的应用程序中可能有多个 ClassA 对象,它们是在运行时按需创建的。

Spring is a runtime framework, so everything is created in runtime anyway. Spring 是一个运行时框架,所以一切都是在运行时创建的。 If you need many objects created by demand you can declare ClassA as a spring bean with scope prototype.如果您需要许多由需求创建的对象,您可以将 ClassA 声明为 spring bean 和 scope 原型。 Other beans could have this prototype bean injected.其他 bean 可以注入这个原型 bean。 Another possible approach if you know which instances will be created during the application startup is to define many beans of the same type and use spring's qualifier feature to differentiate between them during the injection.如果您知道在应用程序启动期间将创建哪些实例,另一种可能的方法是定义许多相同类型的 bean,并在注入期间使用 spring 的限定符功能来区分它们。

But all of them should use the same concrete implementation of InterfaceB class (there are several implementations of InterfaceB but only one is used at runtime depending on the platform used).但是它们都应该使用相同的 InterfaceB class 的具体实现(InterfaceB 有几种实现,但根据所使用的平台,只有一种在运行时使用)。

This means that InterfaceB can be a regular singleton, however, given different implementations you can define something like this:这意味着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();
    }
}

I can not autowired interfaceB as ClassA objects are created at runtime when a and b constructor parameters are known.当 a 和 b 构造函数参数已知时,我无法自动装配 interfaceB,因为 ClassA 对象是在运行时创建的。

Actually you can, no issues with that.其实可以,没有问题。 Define the bean of classA as a prototype.将classA的bean定义为原型。

Check also @Lazy annotation of spring in case you want to initialize the instance of that classA upon the first invocation only.还要检查 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);
   }
}

Update 1更新 1

Based on OP's comments:根据OP的评论:

Here is the working example:这是工作示例:

Add the following dependency in pom.xml:在 pom.xml 中添加以下依赖项:

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

It contains only interfaces and has not transitive dependencies它只包含接口,没有传递依赖

Then based on your use case explained in comment:然后根据您在评论中解释的用例:


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 configuration looks like this: 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();
    }
}

And the main class that gets the service A from the application context (in your case it should be probably a controller or any other business flow):以及从应用程序上下文获取服务 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();
    }

In the last print note that ClassA instances are different, the interfaceB is the same shared instance though.在最后的打印说明中 ClassA 实例不同,但 interfaceB 是相同的共享实例。

Sidenote: the Provider is something integrated with spring already, it resides in javax.inject.Provider of that new jar.旁注:Provider 已经与 spring 集成,它位于新 jar 的javax.inject.Provider中。

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

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