这是您的问题的解决方案。SdkFactory
正如@crizzis 建议的那样,它确实创建了一个 bean,但它也为每个实例创建 bean 实例,以便每个ClientSdk
实例都可以自动装配或由 Spring 提供帮助。请注意,我ident()
在接口中添加了一个方法,ClientSdk
只是为了表明MyClientSdk
bean 实际上已经与 Spring 自动装配Environment
:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
interface ClientSdk {
void sendRequestToClient();
}
// This class is instantiated via a @Bean method inside SdkFactory. Since it is annotated as a Prototype bean,
// multiple instances of this class can be created as beans.
class MyClientSdk implements ClientSdk {
@Autowired
Environment environment;
private final String clientSupplier;
MyClientSdk(String clientSupplier) {
this.clientSupplier = clientSupplier;
System.out.printf("@@@ Created MyClientSdk for: %s\n", clientSupplier);
}
public void sendRequestToClient() {
System.out.printf("Sending request for client: %s\n", clientSupplier);
System.out.printf("CS: %s Environment Prop: %s\n", clientSupplier, environment.getProperty("spring.application.name"));
}
}
@Component
class SdkFactory implements BeanFactoryAware {
private Map<String, ClientSdk> sdks = new HashMap<>();
private BeanFactory beanFactory;
// Here's the key logic to insure that we get just one instance of ClientSdk per clientSupplier value.
ClientSdk getClientSdk(String clientSupplier) {
if (!sdks.containsKey(clientSupplier))
sdks.put(clientSupplier, beanFactory.getBean(ClientSdk.class, clientSupplier));
return sdks.get(clientSupplier);
}
// This is probably the most important bit. This creates a Spring Bean unique to a particular 'clientSupplier'
// value, but only does so when requested so that the factory can control when these beans are created, creating
// only one per a particular `clientSupplier` value.
@Bean
@Scope("prototype")
ClientSdk createSdk(String clientSupplier) {
return new MyClientSdk(clientSupplier);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
@Service
class ClientRequestService {
@Autowired
SdkFactory factory;
public void sendRequestToClient(String clientSupplier) {
ClientSdk clientSdk = factory.getClientSdk(clientSupplier);
clientSdk.sendRequestToClient();
}
}
@SpringBootApplication
public class HarmonyTestApp implements CommandLineRunner {
@Autowired
ClientRequestService service;
public static void main(String[] args) {
try {
ApplicationContext applicationContext = new SpringApplication(new Class<?>[]{HarmonyTestApp.class}).run(args);
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public void run(String... args) {
service.sendRequestToClient("client1");
service.sendRequestToClient("client2");
service.sendRequestToClient("client1");
service.sendRequestToClient("client1");
service.sendRequestToClient("client2");
}
}
结果:
@@@ Created MyClientSdk for: client1
Sending request for client: client1
CS: client1 Environment Prop: TestApp
@@@ Created MyClientSdk for: client2
Sending request for client: client2
CS: client2 Environment Prop: TestApp
Sending request for client: client1
CS: client1 Environment Prop: TestApp
Sending request for client: client1
CS: client1 Environment Prop: TestApp
Sending request for client: client2
CS: client2 Environment Prop: TestApp
请注意,根据输出,每个client1
' 和client2
' ClientSdk 对象都只创建一次,即使它们被多次使用。另请注意,由于对ident()
in的调用打印了由自动装配实例sendRequestToClient
获得的属性的值,因此每个对象的自动装配都有效。Environment
ClientSdk
我确实意识到我使用 aString
而不是ClientSupplier
对象作为每个 ClientSdk 对象的标识键。我这样做只是为了使示例尽可能简单。我希望您可以扩展示例以将 替换为clientSupplier
String
的实例,ClientSupplier
并以某种方式将该对象用作键/标识符,以确保ClientSdk
每个ClientSupplier
. 这个细节并不是这里的基本想法。
另外,请注意,在我进行实施时,问题本身发生了变化。鉴于现在正好有两个 ClientSdk 子类,您可以简单地制作这些常规@Component
Spring bean。拥有少量静态数量会使这个问题变得不那么有趣。我在这里演示的技术允许类的无限数量的 bean 实例,ClientSdk
而不必为每个实例定义一个唯一的类。这要求 Spring 根据运行时信息创建它们的任意实例。这似乎是问题的原始形式所要求的。