In Guice 2 or 3, exists so called Assisted/Partial Inject described here. With this, Guice synthesizes factory implementation (implementing my interface) for my object and some of the constructor arguments are injected by Guice, and some are provided from the context.
Is it possible and how to do the same thing with Spring?
AssistedInject allows you to dynamically configure Factory for class instead of coding it by yourself. This is often useful when you have an object that has a dependencies that should be injected and some parameters that must be specified during creation of object.
We can combine constructor-based and setter-based types of injection for the same bean. The Spring documentation recommends using constructor-based injection for mandatory dependencies, and setter-based injection for optional ones.
The assisted injection uses a factory to provide your assisted dependency, the steps are as follows: Annotate your dependency with @AssitedInject. Provide the dependencies that can be automatically wired by the DI library. Annotate your dynamically added dependencies with @Assisted and provide them with a name if ...
The following does exactly what i asked for. Though, it does not synthesize the implementation of the factory, it is good enough as the factory has access to the injection context so that can use other beans (injectable artifacts) during construction. It uses java based @Configuration
instead of XML, but it will work with XML too.
The factory interface:
public interface Robot {
}
// Implementation of this is to be injected by the IoC in the Robot instances
public interface Brain {
String think();
}
public class RobotImpl implements Robot {
private final String name_;
private final Brain brain_;
@Inject
public RobotImpl(String name, Brain brain) {
name_ = name;
brain_ = brain;
}
public String toString() {
return "RobotImpl [name_=" + name_ + "] thinks about " + brain_.think();
}
}
public class RobotBrain implements Brain {
public String think() {
return "an idea";
}
}
// The assisted factory type
public interface RobotFactory {
Robot newRobot(String name);
}
// this is the Spring configuration showing how to do the assisted injection
@Configuration
class RobotConfig {
@Bean @Scope(SCOPE_PROTOTYPE)
public RobotFactory robotFactory() {
return new RobotFactory() {
@Override
public Robot newRobot(String name) {
return new RobotImpl(name, r2dxBrain());
}
};
}
@Bean @Scope(SCOPE_PROTOTYPE)
public Brain r2dxBrain() {
return new RobotBrain();
}
}
The test code:
public class RobotTest {
@Test
public void t1() throws Exception {
ApplicationContext ctx = new
AnnotationConfigApplicationContext(RobotConfig.class);
RobotFactory rf = ctx.getBean(RobotFactory.class);
assertThat(rf.newRobot("R2D2").toString(),
equalTo("RobotImpl [name_=R2D2] thins about an idea"));
}
}
This achieves exactly what Guice does. The tricky difference is the Scope
. Spring's default scope is Singleton
and Guice's is not (it is prototype).
AFAIK you can't. In Spring you can have Instantiation using a static factory method or Instantiation using an instance factory method. With the second option you can define a bean myFactoryBean
working as a factory for another bean. You can also pass construction arguments to myFactoryBean
by using constructor-arg
(see for example the section Using An Instance Factory Method on this blog), which gives you the equivalent of Guice-injected arguments. However, I don't know of any way to provide further arguments from context when invoking the factory method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With