For my application, I have a Scale interface and multiple classes implementing this interface, for example NormalizedScale, LogScale, etc. In one of my Services, I need to create many Scales, and I want to use Spring to define which implementation of the Scale it should create. How would I implement something like this?
--
I was thinking to create a factory ScaleFactory, like in the Abstract Factory Pattern, which I could call ScaleFactory.getScale() to get a Scale of whichever implementation I configured in the Spring XML:
class ScaleFactory {
Class<? extends Scale> scaleImplClass;
public static Scale getScale() {
return scaleImplClass.newInstance();
}
}
Scale myScale = ScaleFactory.getScale();
But with that approach, how could I configure which implementation the ScaleFactory should use from Spring XML?
--
An alternative would be to make the ScaleFactory a #Service, and then autowire the ScaleFactory into my service:
#Autowired
ScaleFactory scaleFactory;
...
Scale myScale = scaleFactory.getScale();
Then I can use an autowired property in the ScaleFactory to define the scaleImplClass. But that seems weird because my Factory is also a Service and I have an instance of that factory.
--
Another approach would be to have the Class scaleImplementationClass property in my service instead of the ScaleFacotry and use the ScaleFactory like so:
#Value("${scaleImplementationClass}")
Class scaleImplementationClass
...
Scale myScale = ScaleFactory.getScale(scaleImplementationClass);
But then the factory is quite pointless because I could also just as well run scaleImplementationClass.newInstance().
There are a couple of different Spring-like ways you can handle this. The approach I have personally gone for looks a bit like this:
public interface ScaleFactory {
public Scale newInstance();
public String type();
}
public class FirstScaleFactory implements ScaleFactory {
public Scale newInstance() {
return new FirstScale();
}
public String type() {
return "first";
}
}
public class SecondScaleFactory implements ScaleFactory {
public Scale newInstance() {
return new SecondScale();
}
public String type() {
return "second";
}
}
public class ScaleManager {
private final Map<String, ScaleFactory> factories;
#Autowired
public ScaleManager(List<ScaleFactory> factories) {
this.factories = factories.stream()
.collect(Collectors.toMap(f -> f.type(), Function::identity));
}
public Scale newInstance(String type) {
return Optional.ofNullable(factories.get(type))
.map(factory -> factory.newInstance())
.orElseThrow(IllegalArgumentException::new);
}
}
With this approach, your ScaleManager is a standard Spring bean that can be wired into any class that needs a scale instance. At initialization time, it gets all ScaleFactories that are defined in the Spring context, and autowires them in as a List<ScaleFactory>, which is then converted to a Map (where the ScaleFactory type is the key). This avoids you needing to worry about class names of Scale, and gives your the ability to change them later (as long as you keep the type key consistent)`
Your ScaleFactory implementations can then do whatever they need to do. For example, if you have one type of Scale that you know is immutable, you can have the factory return the same instance every time. Alternatively you can have every invocation return a separate instance - the instantiation of the Scale is up to the implementation-dependent factory.
You can simply use "Qualifiers" which is basically going to point to a specific "named" bean. By default the bean names are the name of your classes, with the first letter in lower case (MyClass -> myClass). If you want to define your own names you can do as follow :
#Service("customizedBeanName")
You would end up doing something like this :
#Autowired
#Qualifier("logScale")
private Scale logScale;
#Autowired
#Qualifier("anotherScale")
private Scale anotherScale;
As for spring 5.x there's a simpler and cleaner way of doing this. I have decided to use #ConditionalOnProperty annotation but you may choose any #Conditional* of your preference.
Here's the thing, I've have simplified to extreme:
public interface MyService {}
#Service
#ConditionalOnProperty(prefix = "myService", name = "Impl", havingValue = "Some")
public class SomeService implements MyService {}
#Service
#ConditionalOnProperty(prefix = "myService", name = "Impl", havingValue = "Foo")
public class FooService implements MyService {}
#Service
public class SimpleService {
#Autowired
SimpleService(MyService service) {
// service instance will depend on configuration
}
}
I'm using springboot so I've decided to use application.properties in order to set values via environment variables like this:
myService.Impl=${MY_SERVICE_IMPL}
Then, I have a fully dynamic injection based on environment variables that may be passed to a docker container for instance.
Related
I have a Spring design problem that I've not come across before:
I have an application that manages a bunch of POJOs, I would like to register a factory for each of these POJO classes so a centralised #Component can do the management. Something like this:
class RouteLink implements Link {
}
class HiddenLink implements Link {
}
... lots of others
interface Loader {
Link load(Element xml);
}
#Component
class Manager {
private final Map<String, Loader> loaders = ...
public void create(Element xml) {
// Create link
final String type = ... // from XML
final Loader loader = loaders.get(type);
final Link link = loader.load(xml);
// Do something with the link we created
...
}
}
(The links are created from an XML element, but that's not important to the question - it could be from text, or other POJOs, etc).
Now I would like to co-locate the POJO and it's associated Loader. I could create a #Configuration class that creates a #Bean for each type but that violates the co-location and requires the developer (me!) to continually switch between source files.
Ideally I would like to do something like the following:
class RouteLink implements Link, Loader {
...
// <--- some Spring magic here to register this as a factory
public Link load(Element xml) {
...
}
}
The load() method cannot be a #Bean or a #Component because we are dealing with POJOs. So I'm forced to create a new class just to call a single method:
class RouteLink ... {
...
#Component
public class RouteLinkLoader implements Loader {
public Link load(Element xml) { .... }
}
}
and the loaders are registered with the manager like this:
public Manager(ApplicationContext ctx) {
loaders = ctx.getBeansOfType(Loader.class);
}
It works, sort of, but I can't help feeling I'm missing something. Is there any way of registering the load methods as components?
Notes:
I've tried denoting the POJO classes with #Configuration but that
then treats the class as a bean. But without that or another
stereotype annotation any #Bean in a POJO is not scanned.
I could just programatically register each POJO loader with the
manager but that seems a bit daft when using a DI framework.
The manager looks up the loader by name which is why we look them
up from the context rather than simply auto-wiring.
I've been developing with Spring for years but seem to have a blind-spot with this design (or lack of it!) Is there a better approach?
I'm not sure if I understand your problem correctly, what you want is to create objects that will be managed by Spring? Have you looked at Spring BeanFactory?
So it looks like there is no magic solution to this if we want to co-locate the factory and POJO code in the same source file, basically one has to create a separate #Component to register the POJO factories.
This means we have to wrap every factory method in a class such that it can be component scanned, rather than (for example) just having a static class constant to do it, something like this:
class PojoClass {
#MagicAnnotationHere
public static final Loader LOADER = xml -> new PojoClass(...);
}
Can't be done, so the factory method becomes a first-class component class which means a lot more code:
class PojoClass {
...
#Component
class PojoLoader implements Loader {
#Override
public void load(Element xml) { ... }
}
}
On the positive side the factories can be #AutoWired into the manager component as follows:
#Component
class Manager {
#Autowired
private final Map<String, Loader> loaders = new HashMap<>();
Spring auto-magically populates the map indexed by bean name, which is precisely what I needed to lookup the factory by name. I'll call that a score-draw!
I am trying to initialize a Spring component with a set of all beans of a certain type (well really, anything I can iterate).
The Spring core documentation talks about collection merging, but only in the context of annotation-based configuration.
Suppose I have the following configuration
#Configuration
public class MyConfig {
#Bean
public SomeInterface single() {
return new SomeInterface() {};
}
#Bean
public Set<SomeInterface> multi() {
return Collections.singleton(
new SomeInterface() {}
);
}
}
Where the interface is defined as
public interface SomeInterface {}
I would like this component to get an aggregate of both beans - some collection containing both anonymous classes.
#Component
public class MyComponent {
public MyComponent(Set<SomeInterface> allInterfaces) {
System.out.println(allInterfaces.size()); // expecting 2, prints 1
}
}
I see why Spring has come to the result it has; it sees this method is expecting a Set<SomeInterface> and MyConfig::multi is a bean of type Set<SomeInterface>, so it autowires with that.
If I change the signature to Collection<SomeInterface>, it autowires with MyConfig::single. Again, I see why: there's nothing matching exactly, but there's beans of type SomeInterface (in this case, just one) so it constructs a temporary collection of them and autowires with that. Fine, but not what I'm after.
I would like the solution to be extensible so that if another bean is added, the dependent component does not need to change. I've tried using two parameters, each with a #Qualifier, and that works but is not extensible.
How can I get this to work?
As you already mentioned, MyConfig::multi is a bean of type Set<SomeInterface>, so autowiring Collection<Set<SomeInterface>> would give you all of those sets. The following should work
public MyComponent(Collection<SomeInterface> beans,
Collection<Set<SomeInterface>> beanSets) {
// merge both params here
}
If you need all implementations in multiple places it might make sense to define another bean containing the merged collection and autowire that bean:
static class SomeInterfaceCollection {
final Set<SomeInterface> implementations;
SomeInterfaceCollection(Set<SomeInterface> implementations) {
this.implementations = implementations;
}
}
#Bean
public SomeInterfaceCollection collect(Collection<SomeInterface> beans,
Collection<Collection<SomeInterface>> beanCollections) {
final HashSet<SomeInterface> merged = ...
return new SomeInterfaceCollection(merged);
}
My current situation:
I want to inject the following class into my application:
public interface IConfigAccessor<T extends IConfig> {
...
}
ConfigAccessors are a proxy-objects, created dynamically at runtime. The creation of these object works as follows:
public class ConfigFactory implements IConfigFactory {
private final IConfigUpdater updater;
#Inject
public ConfigFactory(IConfigUpdater updater) {
this.updater = updater;
}
#Override
public <T extends IConfig> IConfigAccessor<T> register(final String configKey, final Class<T> configClass) {
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
As you can see, to create these objects, I need to inject the ConfigUpdater and other depdencies. This means, that guice needs to be fully configured already.
To get the instance out of Guice, I use the following code:
IConfigFactory configClient = injector.getInstance(IConfigFactory.class);
IConfigAccessor<ConcreteConfig> accessor = configClient.register("key", ConcreteConfig.class)
How I want to inject them via Guice:
Currently, I can get the requried objects, but I have to manually pass them around in my application.
Instead, what I want to have is the following:
public class SomeClass {
#Inject
public SomeClass(#Config(configKey="key") IConfigAccessor<ConcreteConfig> accessor) {
// hurray!
}
}
What's the correct approach/technology to get this working?
After a lot of research, I'm feeling a bit lost on how to approach this topic. There are a lot of different things Guice offers, including simple Providers, custom Listeners which scan classes and identify custom annotations, FactoryModuleBuilders and more.
My problem is quite specific, and I'm not sure which of these things to use and how to get it working. I'm not even sure if this is even possible with Guice?
Edit: What I have so far
I have the following annotation which I want to use inside constructor paramters:
#Target({ ElementType.FIELD, ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectConfig {
String configKey();
}
Inside the module, I can bind a provider to IConfigAccessor (with the above annotation) as such:
bind(IConfigAccessor.class).annotatedWith(InjectConfig.class)
.toProvider(new ConfigProvider<>());
However, there are two problems whith this:
The provider cannot provide IConfigAccessor. To create such an instance, the provider would need an IConfigUpdater, but since I use 'new' for the provider, I can't inject it.
Inside the provider, there is no way to find out about the configKey used in the Annotation.
Second approach:
Let's assume that I already know all configurations and configKeys I want to inject during startup. In this case, I could loop over all possible configKeys and have the following binding:
String configKey = "some key";
final Class<? extends IConfig> configClass =...;
bind(IConfigAccessor.class).annotatedWith(Names.named(configKey))
.toProvider(new ConfigProvider<>(configKey, configClass));
However, problem (1) still resides: The provider cannot get an IConfigUpdater instance.
The main problem here is that you cannot use the value of the annotation in the injection. There is another question which covers this part:
Guice inject based on annotation value
Instead of binding a provider instance, you should bind the provider class, and get the class by injecting a typeliteral.
That way, your config factory can look like that:
public class ConfigFactory<T extends IConfig> implements IConfigFactory {
#Inject private final IConfigUpdater updater;
#Inject private TypeLiteral<T> type;
#Override
public IConfigAccessor<T> register(final String configKey) {
Class<T> configClass = (Class<T>)type.getRawType();
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
And then SomeClass:
public class SomeClass {
#Inject
public SomeClass(ConfigFactory<ConcreteConfig> accessor) {
ConcreteConfig config = accessor.register("key");
}
}
Since SomeClass needs to know "key" anyway, this is not too much a change information-wise. The downside is that the SomeClass API now gets a factory instead of the concrete config.
[EDIT]
And here is someone who actually did inject annotated values using custom injection.
I am using Spring DI to wire my components and I came across this issue.
I have a BaseService class which has multiple implementations. And the layer above it, has a builder which calls the service to get data to populate POJOs. Service implementation I need to call (ServiceA,ServiceB) changes according to the type of POJO I need to build.
In such case, how can I autowire the service, as it requires late binding the service. How can I tackle this kind of scenario? (Example in Spring DI would really help)
I read similar questions but could not find the answer. And I read that SOA patterns such as Service Host provide different solutions to exact use case.
Please help.
Thanks
How about using a FactoryBean:
public class BuilderFactory implements FactoryBean<Builder> {
#Autowired
private ApplicationContext appContext;
...
#Override
public Builder getObject() {
Builder builder = new Builder();
switch(something()) {
case "foo":
builder.service = new ServiceA();
break;
case "bar":
builder.service= new ServiceB();
break;
...
default:
//handle cases where it's unclear which type to create
}
return builder;
}
}
where Builder instances have a public/package-private field BaseService service that gets called in their getData(), buildPojos() and wherever other methods.
(you could also use static factory methods to instantiate Builder if you want this field to be private)
You can use ServiceLocatorFactoryBean. In your case you would do something like this:
public interface BaseServiceLocator {
BaseService lookup(String qualifier); //use whatever qualifier type makes sense here
}
<bean id="serviceLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="your.package.BaseServiceLocator" />
</bean>
Then your builder would look something like this:
public class Builder {
#Autowired
private BaseServiceLocator baseServiceLocator;
#Override
public YourReturnType businessMethod() {
SomeData data = getData();
BaseService baseService = baseServiceLocator(data.getType()); //here I am assuming that getType() is a String
//whatever
}
I had the same requirement in one of my projects. I used reflection to get the services according to the pojo requirement. This way there will be no static values even if you define new pojo and service in future you wont have to change any implementation.
I had named my pojos and Services similarly. ie
POJO Name:Pond5DownloadStrategy and ServiceName: Pond5DownloadStrategyService.
I defined all the services in spring. I had a DownloadStrategyFactory which had a single method
getService(Object obj). which is also instantiated as spring bean.
what getService method did is.
I get the POJO name as string using obj.getClass().getSimpleName() and then I append Service at the end. ex.
If I pass Pond5DownloadStrategy then I do AppContext.getBean("Pond5DownloadStrategyService");
Please look at my answer here.
Although is under spring batch topic it’s actually related to your question and the Strategy Design pattern.
StrategyA StrategyB are your ServiceA,ServiceB etc.
You need to use the StrategyLocator in your Builder class (in the original answer it’s equivalent is MyTaskelt). The look-up will be based on your pojo type.
strategy = strategyLocator.lookup(POJOs.class);
In the answer I suggested a PlugableStrategyMapper, but if you predefine all Servcies you can place them in a Map in the application-context.xml
For example, for manual binding:
public class Builder {
#Autowired
private Map<String, Service> services;
// Bind pojo classes to bean names.
private Map<Class<?>, String> binding;
public Service getService(Object object) {
return services.get(binding.get(object.getClass()));
}
public Map<Class<?>, String> getBinding() {
return binding;
}
public void setBinding(Map<Class<?>, String> binding) {
this.binding = binding;
}
}
However, manual binding could be repetitive so if you don't really need his flexibility, you could use a naming convention (#AmitChotaliya answer) or enforce the binding via Service method.
public interface Service {
Class<?> getTargetType();
}
public class Builder {
#Autowired
private Set<Service> services;
// Bind pojo classes to Services.
private Map<Class<?>, Service> binding = new ConcurrentHashMap<Class<?>, Service>();
#PostConstruct
public void init() {
for (Service service : services) {
binding.put(service.getTargetType(), service);
}
}
public Service getService(Object object) {
return binding.get(object.getClass());
}
}
I would like to be able to change the Guice injections at runtime to support multiple injections based on user input. This is what I would like to achieve:
public interface IDao {
public int someMethod();
}
public class DaoEarth implements IDao {
#Override
public int someMethod(){ ... }
}
public class DaoMars implements IDao {
#Override
public int someMethod(){ ... }
}
public class MyClass {
#Inject
private IDao myDao;
public int myMethod(String domain) {
//If Domain == Earth, myDao should be of the type DaoEarth
//If Domain == DaoMars, myDao should be of the type DaoMars
}
}
I was thinking of writing my own Provider, but I don't know how to use that provider to change my bindings at runtime. Any input is welcome and appreciated :)!
Update
Here's what I currently came up with, it's not as pretty as I'd like, so I'm still looking for feedback
public class DomainProvider {
#Inject #Earth
private IDaoProvider earthDaoProvider;
#Inject #Mars
private IDaoProvider marsDaoProvider;
public IDaoProvider get(Domain domain){
switch (domain){
case EARTH:
return earthDaoProvider;
case MARS:
return marsDaoProvider;
}
}
public IDaoProvider get(String domain){
Domain parsedDomain = Domain.valueOf(domain.toUpperCase());
return get(parsedDomain);
}
}
//MarsDaoProvider would be equivalent
public class EarthDaoProvider implements IDaoProvider {
#Inject #Earth
private IDao earthDao;
public IDao getDao() {
return earthDao;
}
}
// This means that in "MyClass", I can do:
public class MyClass {
#Inject
private DomainProvider domainProvider;
public int myMethod(String domain) {
IDaoProvider daoProvider = domainProvider.get(domain);
IDao dao = daoProvider.getDao();
//Now "dao" will be of the correct type based on the domain
}
}
//Of course elsewhere I have the bindings set like
bind(IDao.class).annotatedWith(Earth.class).to(EarthDao.class);
Your version is almost perfect as it is: You're going to need to inject some kind of object that returns one or the other based on code you write, and don't need assisted injection or anything like that. That said, you can skip some of the boilerplate:
public class DomainProvider {
// Just inject Providers directly without binding them explicitly.
#Inject #Earth Provider<IDao> earthDaoProvider;
#Inject #Mars Provider<IDao> marsDaoProvider;
public Provider<IDao> get(Domain domain){
switch (domain){
case EARTH:
return earthDaoProvider;
case MARS:
return marsDaoProvider;
}
}
public Provider<IDao> get(String domain){
Domain parsedDomain = Domain.valueOf(domain.toUpperCase());
return get(parsedDomain);
}
}
Your MyClass in that case would be exactly identical. Here, Provider is either the one-method generic interface com.google.inject.Provider, or the equivalent builtin javax.inject.Provider that it extends. Read more about Guice Providers on the relevant Guice wiki topic.
bind(IDao.class).annotatedWith(Earth.class).to(EarthDao.class);
// You can now inject "#Earth IDao" and also "#Earth Provider<IDao>".
Basically, if you bind a key Foo (to a class, provider, #Provides method, or instance), you automatically get to inject either a Foo or Provider<Foo> with no additional work. Providers are also a great way to ensure that you get a new instance with every call to get, if that's what you want; with your original, you'll always get the same instance of EarthDao or MarsDao for any given DomainProvider you inject. (If you have a scoped binding like #Singleton, Guice will respect that too; Provider just lets Guice get involved, rather than reusing a plain old Java reference.)
This means you can skip your custom EarthDaoProvider and MarsDaoProvider, unless you really need to perform any external initialization on them—at which point you'd probably be better off calling bind(EarthDao.class).toProvider(EarthDaoProvider.class) so the preparation also happens when injecting EarthDao directly. You could also just have DomainProvider return an IDao instance directly by calling get on the appropriate Provider, and be assured that it'll be a new instance every time.