lookup-method
It is usually called getter injection. It is described in spring in action as a special method injection. It declares a method to return a certain type of bean, and the bean actually to be returned is configured in the configuration file. It can be used to design pluggable functions and remove program dependency.
example
- First, create a parent class and write a method eat().
/** * Parent: Fruit */ public class Fruit { public void eat(){ System.out.println("What fruit do you eat"); } }
2. Then create a subclass, inherit the parent class, and override the methods of the parent class.
/** * Subclass: Apple */ public class Apple extends Fruit { @Override public void eat(){ System.out.println("Eat apples"); } }
3. Create call method
public abstract class TestLookupMethod { public abstract Fruit getBean(); /** * The creation of this method will not have any impact on the coverage of LookupMethod * Spring will not handle this parameter * The place affected is the prepareMethodOverrides() method in createBean(), * overloaded Property will not be set to false. AbstractBeanDefinition#prepareMethodOverride protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException { // Gets the number of corresponding methods in the corresponding class int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName()); if (count == 0) { throw new BeanDefinitionValidationException( "Invalid method override: no method with name '" + mo.getMethodName() + "' on class [" + getBeanClassName() + "]"); } else if (count == 1) { // Mark override as not overloaded, to avoid the overhead of arg type checking. //The flag overloaded has not been overwritten to avoid the cost of parameter type checking mo.setOverloaded(false); } } */ public abstract Fruit getBean(String str); public void start(){ this.getBean().eat(); } }
4. Then write the configuration file
<bean id="fruit" class="com.gongj.lookupMethod.Fruit"></bean> <bean id="apple" class="com.gongj.lookupMethod.Apple"></bean> <bean class="com.gongj.lookupMethod.TestLookupMethod" id="lookupMethod"> // The name value is getBean <lookup-method name="getBean" bean="apple"></lookup-method> </bean>
5. Test
public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("lookup-method.xml"); TestLookupMethod bean =(TestLookupMethod) context.getBean("lookupMethod"); // Call the start method bean.start(); } Output: eat apples
When the business changes or in other cases, the business logic in apple no longer meets our business requirements, what should we do if it needs to be replaced? At this time, we need to add a new logical class.
/** * Subclass: Banana */ public class Banana extends Fruit{ @Override public void eat(){ System.out.println("Eating bananas"); } }
Then just modify the configuration class, add a Bean named banana, and modify the apple configured in the lookup method tag to banana. Call the test method again.
<bean class="com.gongj.lookupMethod.Banana" id="banana"></bean> <bean class="com.gongj.lookupMethod.TestLookupMethod" id="lookupMethod"> <lookup-method name="getBean" bean="banana"></lookup-method> </bean>
The Spring framework implements this method injection by dynamically generating subclasses covering the method by using the bytecode in the CGLIB library. Therefore, the class to be covered cannot be final, and the method to be covered cannot be final. You should also pay attention to the configuration of the scope. If the scope is configured as a singleton, each time you call the getBean method, the returned object is the same; If the scope is configured as prototype, the return is different for each call.
In the TestLookupMethod class containing the method to be injected, the method to be injected (getBean) needs the following signature:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
- public|protected: the method must be rewritable and callable by subclasses.
- Abstract: optional. If it is an abstract method, the dynamic proxy class of CGLIB will implement this method. If it is not an abstract method, it will override this method.
- Return type: the return type, of course, can be its parent class or interface.
- No arguments: arguments are not allowed.
At this point, we will already use the label of lookup method. Next, we will look at it in combination with the source code. Go directly to the parseLookupOverrideSubElements method of the BeanDefinitionParserDelegate class.
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) { // Get all child nodes under the bean node NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { //Get each child node Node node = nl.item(i); // Only valid when the child element is lookup method if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) { Element ele = (Element) node; // Get the value of the name attribute, that is, the method to modify String methodName = ele.getAttribute(NAME_ATTRIBUTE); // Get the value of the bean property, that is, the bean name returned by the configuration String beanRef = ele.getAttribute(BEAN_ELEMENT); // Build LookupOverride object according to methodName and beanRef LookupOverride override = new LookupOverride(methodName, beanRef); override.setSource(extractSource(ele)); // Add the object to the overrides collection in the MethodOverrides object overrides.addOverride(override); } } }
replaced-method
Method replacement: you can replace the existing method with a new method at runtime. Different from the previous look up, the replaced method can not only dynamically replace the returned entity bean, but also dynamically change the logic of the original method. Let's look at any use first.
example
1. Create a new class again, which provides two overloaded methods.
public class Replaced { public void replacedName(String name){ System.out.println("I'm the first edition:"+name); } public void replacedName(String name,Integer age){ System.out.println("I'm the first edition:" + name + "every year:"+ age); } }
2. Write a class that implements methodreplace
public class TestReplacedMethod implements MethodReplacer { @Override public Object reimplement(Object obj, Method method, Object[] args) throws Throwable { for (Object arg : args) { System.out.println("Receiving parameters:" + arg); } return null; } }
3. Modify profile
<bean class="com.gongj.lookupMethod.Replaced" id="replaced"> <replaced-method name="replacedName" replacer="replacedMethod"> <!--<arg-type></arg-type>You can configure as many as you want, depending on which overloaded method you want to replace--> <arg-type>String</arg-type> <arg-type>Integer</arg-type> </replaced-method> </bean> <!-- replacedMethod --> <bean class="com.gongj.lookupMethod.TestReplacedMethod" id="replacedMethod"></bean>
4. Test class
public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("replace-method.xml"); Replaced bean =(Replaced) context.getBean("replaced"); bean.replacedName("gongj",88); } Output result: Receiving parameters: gongj Receiving parameters: 88
Run the test class to see the expected results, that is, dynamically replace the original method, know the usage of this element, and then look at the extraction process of the element.
Go directly to the parsereplacedmethodessubelements method of the BeanDefinitionParserDelegate class.
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) { // Get all child nodes under the bean node NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { //Get each child node Node node = nl.item(i); // Valid only when the child element is replaced method if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) { Element replacedMethodEle = (Element) node; // Get the value of the name attribute, that is, the old method to be replaced String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE); // Get the value of the replace property, that is, the new replacement method String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE); // Build ReplaceOverride object based on name and replacer ReplaceOverride replaceOverride = new ReplaceOverride(name, callback); // Look for arg-type match elements. Find Arg type element List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT); for (Element argTypeEle : argTypeEles) { // Record parameters String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE); match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle)); if (StringUtils.hasText(match)) { replaceOverride.addTypeIdentifier(match); } } replaceOverride.setSource(extractSource(replacedMethodEle)); // Add the object to the overrides collection in the MethodOverrides object overrides.addOverride(replaceOverride); } } }
We can see that both lookup method and replaced method construct methodOverrides and finally record them in the methodOverrides attribute in AbstractBeanDefinition.
- If you have any questions or mistakes about this article, please comment and point out. If you think this article is helpful to you, you are welcome to praise and pay attention to it.