[toc]
## 典型的方法注入
大部分的應用場景,bean都是單例的.當一個單例的bean需要和另一個單例的bean組合在一起,或者一個非單例的bean和另一個非單例的bean組合在一起,典型的處理方式就是把bean設置成另一個bean的屬性.
但是,當兩個bean的生命周期不一致時就有問題了,假設單例bean A需要使用非單例的bean B,容器只創建A一次,也就是只有一次機會設置A的屬性,所以不可能每次使用A的時候提供新的實例B.
一種解決方案就是放棄部分控制反轉,可以使bean A實現接口`ApplicationContextAware`來關聯容器,然后在每次需要B的時候,通過容器調用`getBean("B")`獲取新的實例,如下:
~~~java
// 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;
}
}
~~~
前面的這種方式不可取的,因為業務代碼和spring框架耦合在一起,方法注入是spring的高級特性,下面的查找方法的注入方式更干凈一點
> 了解方法注入的更多機制參考[blog](https://spring.io/blog/2004/08/06/method-injection/)
## 查找方法的注入
查找方法的注入會覆蓋容器管理bean的方法,Spring Framework通過使用CGLIB庫中的字節碼動態生成覆蓋該方法的子類,從而實現了方法注入。
> * 為了動態子類可以正常工作,spring容器中的要被繼承的類不能是final的,同時要重寫的方法也不能是final的
> * 單元測試類有個`abstract`方法,需要你自行實現
> * 組件掃描也需要具體的類來提取
> * 查找方法的注入不能用于工廠方法,也不能用于配置類的@Bean注解的方法,因為容器不負責創建對象,不能創建運行時子類
>
對上面的示例改造
~~~ java
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();
}
~~~
需要方法注入的`createCommand`,必須滿足下面的方法簽名
~~~
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
~~~
如果是方法是`abstract`的,動態子類會實現它,否則動態子類會重寫原始類的方法,例如:
~~~xml
<!-- 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>
~~~
`commandManager `需要新的myCommand實例時就調用自己的`createCommand`方法.如果實際需要,請仔細確認發布的bean `myCommand `為原型的,如果是發布為單例的,每次返回的都是同一個實例.
使用注解`@Lookup` 同樣可以實現
~~~java
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
~~~
或者,更多的在返回類型上聲明目標,而不是在注解上
~~~
public abstract class CommandManager {
public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract MyCommand createCommand();
}
~~~
> 另一種注入不同范圍的bean方式是`ObjectFactory/ Provider`,參考[Scoped beans as dependencies.](https://docs.spring.io/spring/docs/5.0.7.RELEASE/spring-framework-reference/core.html#beans-factory-scopes-other-injection)
> 感興趣的同學會發現使用的類`ServiceLocatorFactoryBean`,在包`org.springframework.beans.factory.config`
>
## 任意方法替換
比查找方法注入還少用的就是任意方法替換
基于xml的配置,使用`replaced-method`,考慮下面這種情況,想要重寫方法`computeValue`
~~~
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
~~~
一個類實現接口`org.springframework.beans.factory.support.MethodReplacer`,提供一個新的方法
~~~
/**
* 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 ...;
}
}
~~~
原始bean的定義和方法重寫如下:
~~~xml
<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"/>
~~~
可以使用`<arg-type/>`表明重寫方法的參數,當方法有重載的時候,這個是必需的,為了方便使用,類型可以簡寫,如`java.lang.String`
~~~
java.lang.String
String
Str
~~~
- 正確打開本書的姿勢
- 第一部分 Core
- 1. Ioc container
- 1.1. Introduction to the Spring IoC container and beans
- 1.2. Container overview
- 1.2.1. Configuration metadata
- 1.2.2. Instantiating a container
- 1.2.3. Using the container
- 1.3. Bean overview
- 1.3.1. Naming beans
- 1.3.2. Instantiating beans
- 1.4. Dependencies
- 1.4.1. Dependency Injection
- 1.4.2. Dependencies and configuration in detail
- 1.4.3. Using depends-on
- 1.4.4. Lazy-initialized beans
- 1.4.5. Autowiring collaborators
- 1.4.6. Method injection
- 1.5 Bean Scopes
- 1.6. Customizing the nature of a bean TODO
- 1.7. Bean definition inheritance TODO
- 1.8. Container Extension Points TODO
- 1.9. Annotation-based container configuration
- 1.9.1. @Required
- 1.9.2. @Autowired
- 1.9.3. Fine-tuning annotation-based autowiring with @Primary
- 1.9.4. Fine-tuning annotation-based autowiring with qualifiers TODO
- 1.9.5. Using generics as autowiring qualifiers TODO
- 1.9.6. CustomAutowireConfigurer TODO
- 1.10. Classpath scanning and managed components
- 1.10.1. @Component and further stereotype annotations
- 1.11. Using JSR 330 Standard Annotations TODO
- 1.12. Java-based container configuration
- 1.12.1. Basic concepts: @Bean and @Configuration
- 1.12.2. Instantiating the Spring container using AnnotationConfigApplicationContext
- 2. Resources
- 2.1. Introduction
- 2.2. The Resource interface
- 2.3. Built-in Resource implementations
- 2.3.1. UrlResource
- 2.3.2. ClassPathResource
- 2.3.3. FileSystemResource
- 2.3.4. ServletContextResource
- 2.3.5. InputStreamResource
- 2.3.6. ByteArrayResource
- 2.4. The ResourceLoader
- 2.5. The ResourceLoaderAware interface
- 2.6. Resources as dependencies
- 2.7. Application contexts and Resource paths
- 2.7.1. Constructing application contexts
- 2.7.2. Wildcards in application context constructor resource paths
- 2.7.3. FileSystemResource caveats
- 3. Validation, Data Binding, and Type Conversion
- 4. Spring Expression Language (SpEL)
- 5. Aspect Oriented Programming with Spring
- 5.1. Introduction
- 5.1.1. AOP concepts
- 5.1.2. Spring AOP capabilities and goals
- 5.1.3. AOP Proxies
- 5.2. @AspectJ support
- 5.2.1. Enabling @AspectJ Support
- 5.2.2. Declaring an aspect
- 5.2.3. Declaring a pointcut
- 5.2.4. Declaring advice
- 5.2.5. Introductions TODO
- 5.2.6. Aspect instantiation models TODO
- 5.2.7. Example
- 5.3. Schema-based AOP support TODO
- 5.4. Choosing which AOP declaration style to use TODO
- 5.5. Mixing aspect types TODO
- 5.6. Proxying mechanisms
- 5.6.1. Understanding AOP proxies
- 5.7. Programmatic creation of @AspectJ Proxies
- 5.8. Using AspectJ with Spring applications
- 5.8.1. Using AspectJ to dependency inject domain objects with Spring
- 5.8.2. Other Spring aspects for AspectJ
- 第二部分 Testing
- 第三部分 Data Access
- 1. Transaction Management
- 1.1. Introduction to Spring Framework transaction management
- 1.2 Advantages of the Spring Framework’s transaction support model
- 1.2.1. Global transactions
- 1.2.2. Local transactions
- 1.2.3. Spring Framework’s consistent programming model
- 1.3. Understanding the Spring Framework transaction abstraction
- 1.4. Synchronizing resources with transactions
- 1.4.1. High-level synchronization approach
- 1.4.2. Low-level synchronization approach
- 1.4.3. TransactionAwareDataSourceProxy
- 1.5. Declarative transaction management
- 1.5.1. Understanding the Spring Framework’s declarative transaction implementation
- 1.5.2. Example of declarative transaction implementation
- 1.5.3. Rolling back a declarative transaction
- 1.5.4. Configuring different transactional semantics for different beans
- 1.5.5. tx:advice元素的 settings
- 1.5.6. Using @Transactional
- 1.5.7. Transaction propagation
- 1.5.8. Advising transactional operations
- 1.5.9. Using @Transactional with AspectJ TODO
- 第四部分 web servlet
- 第五部分 Web Reactive
- 第六部分 Integration
- 第七部分 Languages