Official documentation:
In section 1.4.6 of the core Spring5
In most applications, the container most of the bean are singletons . When singleton bean needs collaboration with another non-singleton bean singleton bean needs another embodiment when cooperative non-single bean, bean is typically defined by a bean property to another process dependencies. When the bean life cycle does not cause problems at the same time. A single embodiment of the bean is assumed that requires non-singleton (prototype) bean B, each method may be called on the A. Example bean A container creates only a single, so only one chance to set the properties. Each time desired, the container can not offer a new instance of bean B bean A.
The solution is to give up some control reversal. You can implement ApplicationContextAware
interface to the make of the bean Aware The Container A , and through to the container making a getBean ( " B") call request each time Bean A needs (typically new) bean B instance. The following example shows the method:
// a class that uses a stateful Command-style class to perform some processing 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) { // grab a new instance of the appropriate Command Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } protected Command createCommand() { // notice the Spring API dependency! return this.applicationContext.getBean("command", Command.class); } public void setApplicationContext( ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
The foregoing is undesirable, because the service code and coupled to know Spring Framework. Method injection is an advanced feature Spring IoC container, allows you to cleanly handle this use case.
Find Method Injection
Lookup Method is a method of injecting a management bean container cover and the container vessel returns the search results to another named bean ability. Find relates generally prototype bean, such as the preceding section in the scene described. Spring Framework covering dynamically generated subclass of this method to implement this method by using the injection CGLIB library to generate bytecode.
-
To make this work dynamic subclass, Spring bean container can not subclass
final
of a class, and can not be rewrittenfinal
. -
To have a
abstract
unit test class method requires you to own this class subclass, and providesabstract
stub implementation methods. -
Scanning assembly also requires a specific method, which requires a specific class to get.
-
Another key is to find a method to limit does not apply to factory methods, in particular, the configuration is not a class
@Bean
method, because in this case the container is not responsible for creating instances can not be created dynamically generated subclass runtime.
For the previous code segment CommandManager
class, Spring dynamically container cover createCommand()
implementation of the method. CommandManager
Spring class has no dependencies, because the sample rewritten show:
package fiona.apple; // no more Spring imports! public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); }
To injection containing the client class (in this case CommandManager
), the method needs to be injected in the form of signature:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
If the method abstract
is dynamically generated subclass implement the method. Otherwise, dynamically generated subclass specific methods defined in the cover original class. Consider the following example:
<!-- a stateful bean deployed as a prototype (non-singleton) --> <bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype"> <!-- inject dependencies here as required --> </bean> <!-- commandProcessor uses statefulCommandHelper --> <bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="myCommand"/> </bean>
Identified as commandManager
the bean need myCommand
to call their own when a new instance of the bean's createCommand()
methods. If you actually need, you have to be careful myCommand
bean deployed as a prototype. If Singleton , always returns the myCommand
same instance of the bean.
Alternatively, annotation-based component model, you can @Lookup
find a method, as shown in the following example annotation statement:
public abstract class CommandManager { public Object process(Object commandState) { Command command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup("myCommand") protected abstract Command createCommand(); }
Alternatively, a more conventional nature, you can rely on the return type resolution target for bean lookup method of statement:
public abstract class CommandManager { public Object process(Object commandState) { MyCommand command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup protected abstract MyCommand createCommand(); }
Please note that you should generally use specific stub implementation to declare this search method annotated to make them compatible with the rules of the Spring component scans, which by default abstract class is ignored. This restriction does not apply explicit registration or explicitly imported bean class.
Any method to replace
Compared with the method of injection find a less useful method is to form the injection method can be used to implement any other alternative methods managed bean. You can skip the rest of this section to safely until you really need this feature.
XML-based configuration metadata, you can use the replaced-method
method element to an existing implementation was replaced bean deployed. Consider the following class, which has called us want to overwrite the computeValue
method:
public class MyValueCalculator { public String computeValue(String input) { // some real code... } // some other methods... }
Implement org.springframework.beans.factory.support.MethodReplacer
the interface class to provide a new method definition, as the following example:
/** * meant to be used to override the 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 a computed result String input = (String) args[0]; ... return ...; } }
Deploy the original class and a method of covering the specified bean definition like the following example:
<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 <replaced-method/>
use one or more elements of the <arg-type/>
element to indicate the methods to be covered by the signature. Only when a plurality of signature method overloading and variants, only need the presence of the parameter class. For convenience, the character string parameter type may be a fully qualified type name substring. For example, all of the following matches java.lang.String
:
java.lang.String
String
Str
Code demonstrates:
look-up:
public abstract class Car { //用于lookup-method注入 @Lookup("taxi") public abstract Taxi createTaxi(); private Taxi taxi; public Taxi getTaxi() { return taxi; } //setter注入 public void setTaxi(Taxi taxi) { this.taxi = taxi; } }
public class Taxi { public void say() { System.out.println("I am a Taxi..."); } }
xml file
<-! ==================== Lookup Method-injection property ==================== - -> < bean the above mentioned id = "CAR" class = "spring2.Car" > ! <- Note: the following sentence injection configuration and lookup-method does not matter, we are just for demonstration and explanation to configure the bean -> < Property name = "Taxi" REF = "Taxi" /> <-! Lookup Method-injection -> < Lookup Method- name = "createTaxi" the bean = "Taxi" /> </ the bean > < the bean ID = "Taxi " class="spring2.Taxi" scope="prototype" />
Test categories:
public class Test1 { the ClassPathXmlApplicationContext XmlBeanFactory = null ; @Before public void initXmlBeanFactory () { System.out.println ( "\ n-test method begins ======== ======= \ n-" ); XmlBeanFactory = new new the ClassPathXmlApplicationContext ( "spring2.xml" ); } @After public void After () { System.out.println ( "\ n-ended test method ======== ======= \ n" ); } @Test public void test8 () { // test injection lookup-method Car CAR1 = xmlBeanFactory.getBean ( "CAR", Car. Class ); Car CAR2 = xmlBeanFactory.getBean ( "CAR", Car. Class ); System.out.println ( "Car: Singleton, Therefore animal1 == animal2 be "+ (CAR1 == CAR2)); Taxi DOG1 = car1.getTaxi (); Taxi dog2 = car2.getTaxi (); System.out.println ( " Taxi: the prototype, Car: Singleton, injection lookup-method is not used so dog1 == dog2 be "+ (== DOG1 dog2)); // Note: this is obtained by createDog () method Taxi taxi3 = car1.createTaxi (); Taxi4 Taxi =car2.createTaxi (); System.out.println ( "Taxi: the prototype, Car: Singleton, using a lookup-method implanted so dog3 == dog4 be" + (taxi3 == taxi4)); } }
result:
======== test method starts ======= Car: Singleton, so animal1 == animal2 be to true Taxi: prototype, Car: Singleton, did not use the Lookup -method injection == so DOG1 dog2 should as to true Taxi: prototype, Car: Singleton, using the Lookup -method == injected so dog3 dog4 be false ======== ======= end of the test method
replaced-method
public class ReplaceDog implements MethodReplacer { @Override public Object reimplement(Object obj, Method method, Object[] args) throws Throwable { System.out.println("Hello, I am a white dog..."); Arrays.stream(args).forEach(str -> System.out.println("参数:" + str)); return obj; } }
public class OriginalDog { public void sayHello() { System.out.println("Hello,I am a black dog..."); } public void sayHello(String name) { System.out.println("Hello,I am a black dog, my name is " + name); } }
xml configuration
<!-- ====================replace-method属性注入==================== --> <bean id="dogReplaceMethod" class="com.lyc.cn.v2.day01.method.replaceMethod.ReplaceDog"/> <bean id="originalDogReplaceMethod" class="com.lyc.cn.v2.day01.method.replaceMethod.OriginalDog"> <replaced-method name="sayHello" replacer="dogReplaceMethod"> <arg-type match="java.lang.String"></arg-type> </replaced-method> </bean>
Test categories:
@Test public void test9 () { // Test replace-method implanted OriginalDog originalDog = xmlBeanFactory.getBean ( "originalDogReplaceMethod", OriginalDog. Class ); originalDog.sayHello ( "output has been replaced ..." ); }
result:
======== ======= test method begins
December 9, 2019 3:59:33 pm org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
information: Refreshing org.springframework.context.support @ 3caeaf62 .ClassPathXmlApplicationContext: the Startup DATE [Mon Dec 09 15:59:33 CST 2019]; root of context Hierarchy
org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions December 9, 2019 3:59:33 pm
information: Definitions the XML from the bean class loading path Resource [spring2.xml]
the Hello, the I AM a White Dog ...
parameters: the output has been replaced. . .
======== ======= end of the test method