In most use cases, most beans in a container are singletons. If a singleton bean needs to interact with another singleton bean, or a non-singler bean needs to interact with another non-singler bean, then typically the way to deal with a dependency is to define one bean as a property of the other. The problem arises if the life cycles of the beans are different. Suppose that a singleton bean A must use a non-singler bean (prototype) B, for example, every time a method is called in A. The container creates the singleton bean A only once, and thus the ability to set properties appears only once. The container cannot provide bean A with a new instance of bean B every time it is needed.
The solution is to give up some degree of inversion of control. You can make bean A aware of the
container by implementing the ApplicationContextAware
interface and calling getBean("B")
on the container, requesting a (typically new) instance bean B whenever bean A needs it. The following example
demonstrates this approach:
// class that uses a stateful Command style class to perform some processing on
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// create a new instance of the corresponding
Command Command command = createCommand();
// set the state for the (expectedly brand new)
Command instance command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// note the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
// class that uses a stateful Command style class to perform specific processing
package fiona.apple
// Spring-API imports
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
class CommandManager : ApplicationContextAware {
private lateinit var applicationContext: ApplicationContext
fun process(commandState: Map<*, *>): Any {
// create a new instance of the corresponding Command
val command = createCommand()
// set the state for the (expectedly brand new)
Command instance command.state = commandState
return command.execute()
}
// note the dependency on the Spring API !
protected fun createCommand() =
applicationContext.getBean("command", Command::class.java)
override fun setApplicationContext(applicationContext: ApplicationContext) {
this.applicationContext = applicationContext
}
}
The previous option is not desirable because the business code (domain layer) is aware of and bound to the Spring framework. Method Injection is a slightly advanced Spring IoC container feature that allows you to handle this use case cleanly.
Dependency injection via lookup
Dependency injection via lookup is the ability of a container to override the methods of container-managed beans and return the search result for another named bean in the container. The search typically involves a prototype bean, as in the scenario described in the previous section. The Spring framework implements this dependency injection through a method, using bytecode generation from the CGLIB library to dynamically generate a subclass that overrides the method.
For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be
final
, and the overridden method cannot befinal
either.Unit testing a class that has an
abstract
method requires you to subclass the class yourself and provide an implementation of the stub functionabstract
method.Concrete methods are also needed to scan components, which require specific classes.
Another key limitation is that lookup methods do not work with factory methods and in particular with
@Bean
methods in configuration classes, since in this case the container is not responsible for instantiation and therefore does not can create a runtime-generated subclass on the fly.
In the case of the CommandManager
class in the previous code snippet, the Spring container
dynamically overrides the method implementation createCommand()
. The CommandManager
class does not have any Spring dependencies, as the reworked example shows:
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {
public Object process(Object commandState) {
// create a new instance of the corresponding interface
Command Command command = createCommand();
// set the state for the (expectedly brand new)
Command instance command.setState(commandState);
return command.execute();
}
// ok... but where is the implementation of this method?
protected abstract Command createCommand();
}
package fiona.apple
// no more Spring imports!
abstract class CommandManager {
fun process(commandState: Any): Any {
// create a new instance of the appropriate Command interface
val command = createCommand()
// set the state for the (expectedly brand new) Command instance
command.state = commandState
return command. execute()
}
// ok... but where is the implementation of this method?
protected abstract fun createCommand(): Command
}
In the client class containing the method to be injected (in this case CommandManager
), must
have a signature like this:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
If a method is an abstract
, then the dynamically generated subclass implements that method.
Otherwise, the dynamically generated subclass overrides the concrete method defined in the original class. Consider
the following example:
< !-- a stateful bean deployed as a prototype (not a singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as needed -->
</bean>
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
The bean identified as commandManager
calls its own createCommand()
whenever it
needs a new instance of the myCommand
bean. Deploying the myCommand
bean as a prototype
should be done with caution unless absolutely necessary. If it is a singleton bean, then the same instance of the
myCommand
bean will be returned each time.
Alternatively, in an annotation-based component
model, you can declare a lookup method using the annotation @Lookup
, as shown in the following
example:
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
abstract class CommandManager {
fun process(commandState: Any): Any {
val command = createCommand()
command.state = commandState
return command.execute()
}
@Lookup("myCommand")
protected abstract fun createCommand(): Command
}
Or more typically, you can rely on the target bean to be resolved according to the declared return type of the lookup method:
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract Command createCommand();
}
abstract class CommandManager {
fun process(commandState: Any): Any {
val command = createCommand()
command.state = commandState
return command.execute()
}
@Lookup
protected abstract fun createCommand(): Command
}
Note that how Typically, such annotated lookup methods should be declared with a specific stub function implementation so that they are compatible with Spring bean scanning rules, where abstract classes are ignored by default. This restriction does not apply to explicitly registered or explicitly imported bean classes.
Another way to access target beans that are in different scopes is the dot
implementation of ObjectFactory
/ Provider
. See " Beans in scope as
dependencies".
You may also find the ServiceLocatorFactoryBean
(in the org.springframework.beans.factory.config
package) useful.
Custom method replacement
A less useful form of method dependency injection than lookup method dependency injection is the ability to arbitrarily replace methods in a managed bean with another method implementation. You can safely skip the rest of this section until you need this functionality.
With XML-based configuration metadata, you can use the replaced-method
element to
replace an existing method implementation with another for the expanded bean. Consider the following class, which
has a computeValue
method that we need to override:
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
class MyValueCalculator {
fun computeValue(input: String): String {
// some real code...
}
// some other methods...
}
A class that implements the interface
org.springframework.beans.factory.support.MethodReplacer
provides a new method definition, as shown in
the following example:
/**
* is intended to override an existing computeValue(String).
* implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it and return the calculated result
String input = (String) args [0];
...
return ...;
}
}
/**
* is intended to override an existing computeValue(String).
* implementation in MyValueCalculator
*/
class ReplacementComputeValue : MethodReplacer {
override fun reimplement(obj: Any, method: Method, args: Array<out Any>): Any {
// get the input value, work with it and return the calculated result
val input = args[0] as String;
...
return ...;
}
}
The definition of a bean to expand the original class and set the override method will look like this:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
You can use one or more <arg-type/>
elements inside a <replaced-method/>
element to specify the method signature of the method being overridden. A signature for arguments is only needed
if
the method is overloaded and there are multiple variants in the class. For convenience, the type string for the
argument can be a substring of the fully qualified type name. For example, all of the following correspond to
java.lang.String
:
java.lang.String
String
Str
Since the number of arguments is often sufficient to distinguish between each possible option, this shortcut can save a lot of time by allowing you to enter only the shortest string that matches the argument type.