1.4.2. Dependencies and detailed configuration
As described in the previous section, you can define bean properties and constructor parameters as references to or inline defined values to other managed beans (collaborators). Spring’s XML-based configuration metadata supports child element types in its and elements for this purpose.
A straight value (primitive, string, etc.) specifies a human-readable string representation of an attribute or constructor parameter in the attribute element described in value. Spring’s conversion service is used to convert these values from the conversion String to the actual type of the property or parameter. The following example shows the various values set:
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <! -- results in a setDriverClassName(String) call --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="misterkaoli"/> </bean> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/mydb" p:username="root" p:password="misterkaoli"/> </beans>Copy the code
The previous XML is more concise. However, spelling errors are found at run time, not design time, unless you use an IDE(such as IntelliJ IDEA or the Spring tool for Eclipse) to support automatic property completion when creating bean definitions.
You can also configure the java.util.properties instance, as follows:
<bean id="mappings"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
Copy the code
The Spring container converts the text in the element to java.util. Property instance by using JavaBeans’ PropertyEditor mechanism. This is a nice shortcut, and one of the few places where the Spring team prefers to use nested elements instead of the value attribute style.
Idref elements
The IDREF element is an error-proof method, primarily by passing the ID of another bean in the container (passing the ID as a string value – rather than a reference) to the or label.
The following example shows how to use it:
<bean id="theTargetBean" class="..." /> <bean id="theClientBean" class="..." > <property name="targetName"> // Here theTargetBean is passed as a string to theClientBean's targetName property // instead of passing an instance of theTargetName to theTargetName attribute <idref bean="theTargetBean"/> </property> </bean>Copy the code
The previous bean-definition snippet (at run time) is exactly equivalent to the following snippet: idref is equivalent to the value tag rather than the ref tag
<bean id="theTargetBean" class="..." /> <bean id="client" class="..." > <property name="targetName" value="theTargetBean"/> </bean>Copy the code
The local attribute of the element is no longer supported in idref4.0 Bean XSD because it no longer provides a value on a regular Bean reference. When upgrading to mode 4.0, change the existing IDref Local reference to the IDref bean.Copy the code
The idref usage verifies that the passed bean ID will be used to verify the existence of the bean with the current id
A common place (at least in versions prior to spring2.0) for the <idref></idref> element is in the AOP interceptor configuration in the ProxyFactoryBean bean definition. Using the < IDref ></idref> element when specifying the interceptor name prevents spelling errors.Copy the code
Ref references to other beans
The ref element is or defines the last element in the element. Here, you set the value of the bean’s specified property to a reference to another container-managed bean. The referenced bean is a dependency of the bean whose properties are to be set, and it is initialized as needed before the properties are set (if the collaborator is a singleton bean, it may already be initialized by the container). All references are ultimately references to another object. The scope and validation depend on whether the ID or name of the other object is specified through the bean or parent property.
Specifying the target bean through the bean attribute of the tag is the most general form, which allows you to create a reference to any bean in the same container or parent container, regardless of whether it is in the same XML file. The value of the bean property can be the same as the id property of the target bean, or as a value in the name property of the target bean. The following example shows how to use the ref element:
<ref bean="someBean"/>Copy the code
The local attribute of the ref element is no longer supported in ref4.0 Bean XSD because it no longer provides a value on a regular Bean reference. When upgrading to mode 4.0, change the existing ref local reference to the ref bean to.
A collection of
The,,,, and elements set the properties and parameters of the Java collection types list, set, Map, and Properties, respectively. The following example shows how to use them:
<bean id="moreComplexObject" class="example.ComplexObject"> <! -- results in a setAdminEmails(java.util.Properties) call --> <property name="adminEmails"> <props> <prop key="administrator">[email protected]</prop> <prop key="support">[email protected]</prop> <prop key="development">[email protected]</prop> </props> </property> <! -- results in a setSomeList(java.util.List) call --> <property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource" /> </list> </property> <! -- results in a setSomeMap(java.util.Map) call --> <property name="someMap"> <map> <entry key="an entry" value="just some string"/> <entry key ="a ref" value-ref="myDataSource"/> </map> </property> <! -- results in a setSomeSet(java.util.Set) call --> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource" /> </set> </property> </bean>Copy the code
The key or value of a map or the value of a set can also be any of the following elements:
bean | ref | idref | list | set | map | props | value | null
Copy the code
Collection of merger
The Spring container also supports merging collections. An application developer can define a parent element, and have the child element, and, or inherit and override the values of the collection of parent elements. That is, the value of a subset is the result of merging the parent set with the elements of the subset, and the subset elements override the values specified in the parent set.
This section on merging discusses the parent-child bean mechanism. Readers unfamiliar with the definitions of parent and child beans may want to read the relevant sections before continuing.
The following example demonstrates collection merging:
<beans> <bean id="parent" abstract="true" class="example.ComplexObject"> <property name="adminEmails"> <props> <prop key="administrator">[email protected]</prop> <prop key="support">[email protected]</prop> </props> </property> </bean> <bean id="child" parent="parent"> <property name="adminEmails"> <! -- the merge is specified on the child collection definition --> <props merge="true"> <prop key="sales">[email protected]</prop> <prop key="support">[email protected]</prop> </props> </property> </bean> <beans>Copy the code
Notice that the Merge =true attribute is used on the element of the adminEmails attribute defined by the child bean. When the container parses and instantiates a child bean, the generated instance has a collection of adminEmails properties that contain the result of merging the collection of adminEmails from the child bean with the collection of adminEmails from the parent bean. The following listing shows the result:
[email protected]
[email protected]
[email protected]
Copy the code
The value set of the child attribute set inherits all the attribute elements in the parent attribute, and the value of the child attribute support value overrides the value in the parent attribute set.
This merging behavior applies similarly to the,, and collection types. In the specific case of an element, the semantics associated with the list collection type (the concept of an ordered collection of values) are maintained. The value of the parent element precedes the value of the list of all child elements. For the Map, Set, and Properties collection types, there is no ordering. Therefore, sorting semantics do not apply to collection types under the associative mapping, collection, and attribute implementation types used inside the container.
Limitations of collection merging
You cannot combine different collection types (such as Map and List). If you try to do so, an exception will be thrown. The Merge property must be specified in the inherited subcollection definition below. Merge specifies attributes on the parent collection definition that are redundant and do not result in the desired merge.
Strongly typed set
With the introduction of generic types in Java 5, you can use strongly typed collections. That is, you can declare a Collection type that contains only (for example) String elements. If you use Spring to inject a strongly-typed dependency Collection into a Bean, you can take advantage of Spring’s type-casting support to convert elements of a strongly-typed Collection instance to the appropriate type Collection before adding them to it. The following Java class and bean definitions show how to do this:
public class SomeClass { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) { this.accounts = accounts; }} <beans> <bean id="something" class=" x.y.someclass "> <property name="accounts"> <map> <entry key="one" value="9.99"/> <entry key="two" value="2.75"/> <entry key="six" value="3.99"/> </map> </property> </bean> </beans> When ready to inject beans You can get generic information about strongly typed element types of something by reflection Map<String,Float>. Thus, Spring's type-conversion infrastructure identifies various value elements as Type floats and converts string values (9.99, 2.75, and 3.99) to actual Float types.Copy the code
Empty string values and empty string values
Spring treats empty parameters such as attributes as empty strings. The following XML-based configuration metadata fragment sets the email attribute to an empty string value (“”).
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
Copy the code
The previous example is equivalent to the following Java code:
exampleBean.setEmail("");
Copy the code
This element handles the value of null. The following listing shows an example:
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
Copy the code
The previous configuration is equivalent to the following Java code:
exampleBean.setEmail(null);
Copy the code
XML shortcut with p-namespace
P-namespaces allow you to use the attributes of a bean element, rather than nested elements, to describe the attribute values of the bean with which you collaborate, or both.
Spring supports extensible configuration formats with namespaces that are defined based on XML schemas. The bean configuration format discussed in this chapter is defined in an XML schema document. However, p-namespaces are not defined in XSD files and only exist in Spring’s core.
The following example shows two XML fragments parsed to the same result (the first using standard XML format, the second using p-namespaces):
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" <! - this line is to -- > xsi: schemaLocation = "HTTP: / / http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="classic" class="com.example.ExampleBean"> <property name="email" value="[email protected]"/> </bean> <bean name="p-namespace" class="com.example.ExampleBean" p:email="[email protected]"/> </beans>Copy the code
This example shows the email property in the P-namespace that is called in the bean definition. This tells Spring to include a property declaration. As mentioned earlier, the P namespace has no schema definition, so you can set the property name to the property name.
The next example includes two more bean definitions that reference another bean:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
Copy the code
This example not only contains an attribute value using the p-namespace, but also uses a special format to declare attribute references. The first bean definition is used to create a reference from bean John to bean Jane, and the second bean definition uses P: spoule-ref =” Jane “as a property to do exactly the same job. In this case, spouse is the attribute name, and the -ref part indicates that this is not a direct value, but a reference to another bean.
P-namespaces are not as flexible as standard XML formats. For example, the format of a declared attribute reference conflicts with an attribute ending in Ref, whereas the standard XML format does not. We recommend that you choose your methods carefully and communicate with your team members to avoid generating XML documents that use all three methods at the same time. The p-namespace is not as flexible as the standard XML format. For example, the format for declaring property references clashes with properties that end in Ref,whereas the standard XML format does not. We recommend that you choose your approach carefully and communicate this to your team members to avoid producing XML documents that use all three approaches at the same time. P: xxxref-ref p: xxxref-ref p: xxxref-refCopy the code
XML shortcut with C-namespace
Similar to the XML shortcut using p-Namespace, the C-Namespace introduced in Spring 3.1 allows you to configure constructor parameters using inline properties instead of nested constructor parameter elements. Property for p-namespace and constructor-arg for c-namespace
The following example uses the C: namespace to perform the same operation as construction-based dependency injection:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" <! - add this -- > xsi: schemaLocation = "HTTP: / / http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="beanTwo" class="x.y.ThingTwo"/> <bean id="beanThree" class="x.y.ThingThree"/> <! <constructor-arg name="thingTwo" ref="beanTwo"/> <constructor-arg name="thingTwo" ref="beanTwo" <constructor-arg name="thingThree" ref="beanThree"/> <constructor-arg name="email" value="[email protected]"/> </bean> <! C: thingtwo-ref ="beanTwo" c: thingthree-ref ="beanThree" c: thingthree-ref ="beanThree" c:email="[email protected]"/> </beans>Copy the code
In rare cases where constructor parameter names are not available (usually when there is no debugging information when the bytecode is compiled), fallback to parameter indexes can be used, as follows:
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
c:_2="[email protected]"/>
Copy the code
Compound attribute name One bean has another bean nested within it and needs to assign a value to the inner bean
You can use compound or nested property names when setting bean properties, as long as none of the components of the path (except the final property name) are empty. Consider the following bean definition:
<bean id="something" class=" things.thingone "> <property name="fred.bob. Sammy "value="123" /> </bean> said something The bean has the Fred property, which has the Bob property, which has the Sammy property, and eventually the Sammy property is set to a value of 123. The Fred property of something bean and the Bob property of Fred must not be null after constructing the bean. Otherwise, a NullPointerException will be raised.Copy the code
1.4.3. Use the depends – on
If a bean is a dependency of another bean, this usually means setting one bean as a property of the other bean. Typically, you can do this using elements in XML-based configuration metadata. However, sometimes the dependencies between beans are less straightforward. One example is when static initializers in a class need to be triggered, for example for database driver registration. The Depends -on attribute can explicitly force the initialization of one or more beans before initializing beans that use this element. The following example uses the Depends -on attribute to indicate a dependency on a single bean:
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
Copy the code
To express a dependency on multiple beans, you need to separate multiple names with commas
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
Copy the code
The Depends -on genus can specify either an initialization time dependency or a corresponding destruction time dependency (only in singleton beans). <bean id="serviceOneRef" name="serviceOneName" class="org.springframework.example.service.ServiceOne" destroy-method="destroyed"/> public void destroyed(){ System.out.println("ServiceOne destroy"); } <bean id="serviceTwo" class="org.springframework.example.service.ServiceTwo" p:name="asdfasdf" depends-on="serviceOneRef" destroy-method="destroyed" /> public void destroyed(){ System.out.println("serviceTwo destroy"); } console: serviceTwo destroy ServiceOne destroyCopy the code
1.4.4. Lazy loading beans
By default, the ApplicationContext implementation eagerly creates and configates all singleton beans as part of the initialization process. In general, this kind of pre-instantiation is desirable because errors in the configuration or surrounding environment are discovered immediately, rather than hours or even days later. When this behavior is inappropriate, you can prevent pre-instantiation of singleton beans by marking the bean definition for lazy initialization. A delay-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.
In XML, this behavior is controlled by the element’s lazy-init attribute, as shown in the following example:
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
Copy the code
When ApplicationContext uses the previous configuration, lazy beans are not eagerly preinstantiated when ApplicationContext is started, and not. Lazy beans are eagerly preinstantiated.
However, when a lazy-loaded bean is a dependency of a non-lazy-loaded singleton bean, the ApplicationContext creates the lazy-loaded bean at startup because it must satisfy the singleton dependency.
You can also batch set lazy-loaded beans by using the element’s default-lazy-init
<beans default-lazy-init="true"> <! -- no beans will be pre-instantiated... --> </beans>Copy the code
1.4.5. Automatically.
The Spring container can automatically assemble relationships between collaborating beans. Automatic assembly has the following advantages:
- Autowaging can greatly reduce the need to specify property or constructor parameters.
- Autowiring can update the configuration as the object evolves. For example, if you need to add a dependency to a class, the dependency can be satisfied automatically without changing the configuration. Therefore, auto-assembly is especially useful during development without having to worry about the option of switching to explicit wiring when the code base becomes more stable.
When using XML-based configuration metadata, you can use the element’s Autowire attribute to specify the autowire pattern for the bean definition. The autowiring feature has four modes. You can specify autowiring for each bean, so you can select the assembly to autowiring. The following table describes the four auto-assembly modes:
model | explain |
---|---|
no | (Default) no autowiring. References to other beans must be defined by the ref element. For larger deployments, changing the default Settings is not recommended, Because explicitly specifying which beans you want to reference provides better control and clarity. In a way, it records the structure of the system. |
byName | Auto assemble by property name. Spring looks for beans with the same name as the property you want to implement automatically. For example, if a bean defines the Autowire mode as byName, And it contains a master attribute (that is, it has a setMaster(..) Methods) , Spring will look for a bean definition named Master, And use it to set the property. It’s basically a set method, so if you have a master property, But your set method is setMyMaster(..) . Spring then looks for the bean named myMaster instead of the bean named Master |
byType | The type of input parameter applicable to the set method, If a bean of the property type happens to exist in the container, the property is allowed to be injected automatically. If there are more than one, a fatal exception is thrown, This indicates that you cannot use byType autowiring on this bean. If there is no matching bean, nothing happens (no properties are set). |
constructor | Similar to byType, but applicable to constructor arguments. If there is no exact bean for the constructor parameter type in the container, a fatal error is thrown. |
Using the byType or Constructor autowiring pattern, you can automatically inject the Arrays and Collections types. In this case, all autowiring candidates in the container that match the desired type are provided to satisfy the correlation. If you receive a map with a key of type String, you can also have Spring automatically assemble a map with the key of the map instance being the corresponding bean name.
@Autowired
private List<CusService> serviceLists;
@Autowired
private Map<String,CusService> cusServiceMap;
Copy the code
Limitations and disadvantages of automatic wiring
Autoassembly works best when it is used consistently across projects. Using autoloassembly to connect one or two bean definitions can be confusing to some developers if it is not commonly used.
Consider the limitations and disadvantages of autowiring:
- Explicit dependencies in property and construction parameter Settings always override auto-assembly. You cannot automatically join simple properties such as Primitives (Boolean,int,long, etc.), String, and classes(and arrays of such simple properties). This limitation is by design.
- Automating beans is not as accurate as explicitly specifying beans. However, as noted in the previous table, Spring has done everything possible to avoid unexpected results. Relationships between spring-managed objects are no longer explicitly recorded.
- Generating connection information is not possible for tools that generate documentation from the Spring container.
- This is not a problem if there are multiple options that can be matched when auto-injecting dependencies, if the injection type is an array, collection, or mapping instance. However, this ambiguity cannot be resolved arbitrarily for dependencies that are expected to use a single value. If no unique bean definition is available, an exception is thrown.
In the latter case, you have several options:
- Forgo automatic assembly in favor of explicit cabling.
- Avoid auto-assembly by setting autowire-candidate to false for the bean definition.
- Specify the primary candidate bean definition for the element defined by a single bean by setting its primary to true.
- More fine-grained control is achieved with annotation-based configuration, which is covered in Section 1.9.
1. Abandon automatic assembly, To specify the beans into @ the Qualifier or ref attribute of XML can be 2. < bean id = "serviceOne" class = "org. Springframework. Example. Service. ServiceOne" / > ServiceTwo < bean id = "" class =" org. Springframework. Example. Service. ServiceTwo "/ > the two beans CusService inherit the same interface, @autowired private CusService service; Autowire-candidate ="false" <bean id="serviceOne" class="org.springframework.example.service.ServiceOne" autowire-candidate="false" /> ServiceTwo 3 is installed preferentially on all interfaces that automatically assemble CusService. Situation with 2 can also will increase primary serviceTwo = "true" < bean id = "serviceTwo" class = "org. Springframework. Example. Service. ServiceTwo" primary="true" />Copy the code
Exclude beans from autowiring
On a per-bean basis, you can exclude one bean from autowiring. In Spring’s XML format, set the autowire-candidate of the element to false. Containers make specific bean definitions unavailable to the autowiring infrastructure (including annotation style configurations such as @AutoWired).
The Autowire-candidate property is designed to affect type-based autowire-candidate only. It does not affect explicit references by name, which are resolved even if the specified bean is not marked as an autowiring candidate. Therefore, autowiring by name will still inject a bean if the names match.Copy the code
The element receives an Patterns string in its default-Autowire-Candidates attribute, which means that all qualified beanName autowire-candidates matched according to the pattern string will be set to true. Those that fail are set to false Patterns strings that accept one or more matching patterns, separated by commas. For example, the combined pattern (*Repository,*Service,*Dao)
The autowire-candidates attribute of the bean itself is superior to the default-Autowire-candidates attribute
These techniques are useful for beans that you would never want to be injected into another bean through autowiring. This does not mean that the excluded beans themselves cannot be configured using autowiring. Instead, the bean itself is simply an automatic connection candidate that is not used by other beans.
public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate"; AutowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); If (isDefaultValue(autowireCandidate)) {if (isDefaultValue(autowireCandidate)) {if (isDefaultValue(autowireCandidate)) The default-Autowier-candidates attribute for your beans finds the patterns expression configured. // If the expression is empty, do not process the setAutowireCandidate attribute value so that it remains true String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern ! // Set setAutowireCandidate to true within the range of the expression // Set setAutowireCandidate to false otherwise StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); }} else {// If autowireCandidate is not default is true then set to true // if it is false then set to false bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); }Copy the code
1.4.6. Methods to inject
In most application scenarios, most of the beans in the container are Singletons. When a singleton Bean needs to collaborate with another singleton Bean or a non-singleton Bean needs to collaborate with another non-singleton Bean, dependencies can usually be handled by defining one Bean as a property of another Bean, and classes with the same life cycle inject each other with no problem. Problems occur when beans have different life cycles. Suppose that singleton Bean A needs to use non-singleton (prototype) Bean B, perhaps on every method call of A. The container creates singleton bean A only once, so it has only one chance to set the properties. Each time A container is needed, the container cannot provide bean A with A new instance of Bean B.
One solution is to abandon some inversion of control. You can make Bean A aware of the container by implementing the interface ApplicationContextAware and making each time container A requires A call from container B to request A (usually new) instance of Bean B. The following example shows this method: ApplicationContextAware) getBean (” B “)
// 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; }}Copy the code
The previous content is not ideal because the business code knows and is coupled to the Spring framework. Method injection is an advanced feature of the Spring IoC container that allows you to clean up this use case.
You can learn more about method injection motivation in this blog entry.
The Lookup Method injection
Lookup Method injection is the ability of a container to override methods on a container-managed bean and return the Lookup results of another named bean in the container. Look-ups typically involve prototype beans, as described in the previous section. The Spring framework implements this method injection by dynamically generating subclasses that override the method using bytecode generation from the CGLIB library.
- For this dynamic subclass to work, the class of the Spring Bean container subclass cannot be final, nor can the method to override.
- Unit testing a class with an abstract method requires you to create your own subclass and provide a stub implementation of the abstract method.
- Concrete methods are also necessary for component scanning, which requires concrete classes to pick up.
- Another key limitation is that the Lookup Method cannot work with factory methods, especially with the @Bean Method in the configuration class, because in this case the container is not responsible for creating instances and therefore cannot dynamically create run-time generated subclasses.
For the classes in the snippet preceding the CommandManager, the Spring container dynamically overrides the implementation of the createCommand() method. The CommandManager class does not have any Spring dependencies, as shown below:
package fiona.apple; Public abstract class CommandManager {public Object process(Object commandState) {// Get a new instance of the appropriate Command interface Command = createCommand(); // Set the state command.setState(commandState) on the (hopefully new) command instance; return command.execute(); } // Where is the implementation class?? protected abstract Command createCommand(); }Copy the code
In the client class that contains the method to be injected (CommandManager in this case), the method to be injected requires a signature of the following form:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
Copy the code
If the method is abstract, a dynamically generated subclass implements the method. Otherwise, dynamically generated subclasses override concrete methods defined in the original class. Consider the following example:
<! <bean id="myCommand" class="fiona. Apple.AsyncCommand" scope="prototype"> -- inject dependencies here as required --> </bean> <! - use commandProcessor statefulCommandHelper - > < bean id = "commandManager" class = "fiona.apple.Com mandManager" > <lookup-method name="createCommand" bean="myCommand"/> </bean>Copy the code
A bean identified as commandManager calls its own createCommand() method when it needs a new instance of the myCommand Bean. If you want to deploy myCommand Bean as a prototype, you must carefully confirm your requirements. If it is a singleton, the same myCommand Bean instance is returned each time.
Alternatively, in an annotation-based component model, you can declare a Lookup method through the @lookup annotation, 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();
}
Copy the code
Or, more conventionally, you can rely on the target bean to parse according to the return type declared by the lookup method:
public abstract class CommandManager { public Object process(Object commandState) { MyCommand command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup protected abstract MyCommand createCommand(); }Copy the code
Note that you should usually declare such annotated look-up methods using a concrete stub implementation so that they are compatible with Spring’s component 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 with different scopes is ObjectFactory/ The Provider injection point (https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scop Es - other - injection). You may also find ServiceLocatorFactoryBean (in org. Springframework. Beans. Factory. Config package) useful.Copy the code
Arbitrary method substitution
In the form of method substitution injection, another method implements the ability to replace any method in a managed bean. This is so uncommon that you can skip the rest of this section and wait until you really need this feature.
For XML-based configuration metadata, you can use the appet-method element to replace an existing method implementation of a deployed bean with another method implementation. Consider the following class, which has a method called computeValue that we want to override:
public class MyValueCalculator { public String computeValue(String input) { // some real code... }}Copy the code
Implement the org. Springframework. Beans. Factory. Support. MethodReplacer the class provides a new method of the interface definition, as shown in the example below:
/** * Used to override an existing computeValue(String Input) * implementation in MyValueCalculator */ public class ReplacementComputeValue implements MethodReplacer {public Object reimplement(Object o, Method m, Object[] args) throws Throwable {// Get the input value, use it, String input = (String) args[0]; . return ... ; }}Copy the code
The Bean definition used to deploy the original class and specify method overrides looks like the following example:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
Copy the code
You can use one or more elements within the element to indicate the method signature of the method to override. Parameters need to be signed only if the method is overloaded and there are multiple variants in the class. For convenience, the type string of the argument can be a fully qualified substring of the type name. For example, all of the following are java.lang.String matches:
java.lang.String
String
Str
Copy the code
Because the number of arguments is usually enough to distinguish each possible choice, this shortcut saves a lot of input by letting you type only the shortest string that matches the parameter type