轉(zhuǎn)自:https://javadoop.com/post/spring-ioc
初始化所有的 singleton beans
我們的重點(diǎn)當(dāng)然是 finishBeanFactoryInitialization(beanFactory); 這個(gè)巨頭了,這里會負(fù)責(zé)初始化所有的 singleton beans。
注意,后面的描述中,我都會使用初始化或預(yù)初始化來代表這個(gè)階段。主要是 Spring 需要在這個(gè)階段完成所有的 singleton beans 的實(shí)例化。
我們來總結(jié)一下,到目前為止,應(yīng)該說 BeanFactory 已經(jīng)創(chuàng)建完成,并且所有的實(shí)現(xiàn)了 BeanFactoryPostProcessor 接口的 Bean 都已經(jīng)初始化并且其中的 postProcessBeanFactory(factory) 方法已經(jīng)得到執(zhí)行了。所有實(shí)現(xiàn)了 BeanPostProcessor 接口的 Bean 也都完成了初始化。
剩下的就是初始化其他還沒被初始化的 singleton beans 了,我們知道它們是單例的,如果沒有設(shè)置懶加載,那么 Spring 會在接下來初始化所有的 singleton beans。
// AbstractApplicationContext.java 834
// 初始化剩余的 singleton beans
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 首先,初始化名字為 conversionService 的 Bean。本著送佛送到西的精神,
// 我在附錄中簡單介紹了一下 ConversionService,因?yàn)檫@實(shí)在太實(shí)用了
// 什么,看代碼這里沒有初始化 Bean ??!
// 注意了,初始化的動作包裝在 beanFactory.getBean(...) 中,這里先不說細(xì)節(jié),先往下看吧
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded(嵌入式) value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
@Override
public String resolveStringValue(String strVal) {
return getEnvironment().resolvePlaceholders(strVal);
}
});
}
// 先初始化 LoadTimeWeaverAware 類型的 Bean
// 一般用于織入第三方模塊,在 class 文件載入 JVM 的時(shí)候動態(tài)織入,這里不展開說
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// 沒什么別的目的,因?yàn)榈竭@一步的時(shí)候,Spring 已經(jīng)開始預(yù)初始化 singleton beans 了,
// 肯定不希望這個(gè)時(shí)候還出現(xiàn) bean 定義加載、解析、注冊。
beanFactory.freezeConfiguration();
// 開始初始化剩下的
beanFactory.preInstantiateSingletons();
}
從上面最后一行往里看,我們又回到 DefaultListableBeanFactory 這個(gè)類了,這個(gè)類大家應(yīng)該都不陌生了吧。
preInstantiateSingletons
// DefaultListableBeanFactory 728
@Override
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 觸發(fā)所有的非懶加載的 singleton beans 的初始化操作
for (String beanName : beanNames) {
// 合并父 Bean 中的配置,注意 <bean id="" class="" parent="" /> 中的 parent,用的不多吧,
// 考慮到這可能會影響大家的理解,我在附錄中解釋了一下 "Bean 繼承",請移步
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 非抽象、非懶加載的 singletons。如果配置了 'abstract = true',那是不需要初始化的
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 處理 FactoryBean(讀者如果不熟悉 FactoryBean,請移步附錄區(qū)了解)
if (isFactoryBean(beanName)) {
// FactoryBean 的話,在 beanName 前面加上 ‘&’ 符號。再調(diào)用 getBean,getBean 方法別急
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
// 判斷當(dāng)前 FactoryBean 是否是 SmartFactoryBean 的實(shí)現(xiàn),此處忽略,直接跳過
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
// 對于普通的 Bean,只要調(diào)用 getBean(beanName) 這個(gè)方法就可以進(jìn)行初始化了
getBean(beanName);
}
}
}
// 到這里說明所有的非懶加載的 singleton beans 已經(jīng)完成了初始化
// 如果我們定義的 bean 是實(shí)現(xiàn)了 SmartInitializingSingleton 接口的,那么在這里得到回調(diào),忽略
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
接下來,我們就進(jìn)入到 getBean(beanName) 方法了,這個(gè)方法我們經(jīng)常用來從 BeanFactory 中獲取一個(gè) Bean,而初始化的過程也封裝到了這個(gè)方法里。
getBean
在繼續(xù)前進(jìn)之前,讀者應(yīng)該具備 FactoryBean 的知識,如果讀者還不熟悉,請移步附錄部分了解 FactoryBean。
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
// 我們在剖析初始化 Bean 的過程,但是 getBean 方法我們經(jīng)常是用來從容器中獲取 Bean 用的,注意切換思路,
// 已經(jīng)初始化過了就從容器中直接返回,否則就先初始化再返回
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// 獲取一個(gè) “正統(tǒng)的” beanName,處理兩種情況,一個(gè)是前面說的 FactoryBean(前面帶 ‘&’),
// 一個(gè)是別名問題,因?yàn)檫@個(gè)方法是 getBean,獲取 Bean 用的,你要是傳一個(gè)別名進(jìn)來,是完全可以的
final String beanName = transformedBeanName(name);
// 注意跟著這個(gè),這個(gè)是返回值
Object bean;
// 檢查下是不是已經(jīng)創(chuàng)建過了
Object sharedInstance = getSingleton(beanName);
// 這里說下 args 唄,雖然看上去一點(diǎn)不重要。前面我們一路進(jìn)來的時(shí)候都是 getBean(beanName),
// 所以 args 其實(shí)是 null 的,但是如果 args 不為空的時(shí)候,那么意味著調(diào)用方不是希望獲取 Bean,而是創(chuàng)建 Bean
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("...");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 下面這個(gè)方法:如果是普通 Bean 的話,直接返回 sharedInstance,
// 如果是 FactoryBean 的話,返回它創(chuàng)建的那個(gè)實(shí)例對象
// (FactoryBean 知識,讀者若不清楚請移步附錄)
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
// 當(dāng)前線程已經(jīng)創(chuàng)建過了此 beanName 的 prototype 類型的 bean,那么拋異常
throw new BeanCurrentlyInCreationException(beanName);
}
// 檢查一下這個(gè) BeanDefinition 在容器中是否存在
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 如果當(dāng)前容器不存在這個(gè) BeanDefinition,試試父容器中有沒有
String nameToLookup = originalBeanName(name);
if (args != null) {
// 返回父容器的查詢結(jié)果
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
// typeCheckOnly 為 false,將當(dāng)前 beanName 放入一個(gè) alreadyCreated 的 Set 集合中。
markBeanAsCreated(beanName);
}
/*
* 稍稍總結(jié)一下:
* 到這里的話,要準(zhǔn)備創(chuàng)建 Bean 了,對于 singleton 的 Bean 來說,容器中還沒創(chuàng)建過此 Bean;
* 對于 prototype 的 Bean 來說,本來就是要?jiǎng)?chuàng)建一個(gè)新的 Bean。
*/
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 先初始化依賴的所有 Bean,這個(gè)很好理解。
// 注意,這里的依賴指的是 depends-on 中定義的依賴
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 檢查是不是有循環(huán)依賴,這里的循環(huán)依賴和我們前面說的循環(huán)依賴又不一樣,
// 這里肯定是不允許出現(xiàn)的,不然要亂套了,讀者想一下就知道了
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 注冊一下依賴關(guān)系
registerDependentBean(dep, beanName);
// 先初始化被依賴項(xiàng)
getBean(dep);
}
}
// 創(chuàng)建 singleton 的實(shí)例
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 執(zhí)行創(chuàng)建 Bean,詳情后面再說
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 創(chuàng)建 prototype 的實(shí)例
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
// 執(zhí)行創(chuàng)建 Bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 如果不是 singleton 和 prototype 的話,需要委托給相應(yīng)的實(shí)現(xiàn)類來處理
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
// 執(zhí)行創(chuàng)建 Bean
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 最后,檢查一下類型對不對,不對的話就拋異常,對的話就返回了
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
大家應(yīng)該也猜到了,接下來當(dāng)然是分析 createBean 方法:
protected abstract Object createBean(String beanName, RootBeanDefinition mbd,
Object[] args) throws BeanCreationException;
第三個(gè)參數(shù) args 數(shù)組代表創(chuàng)建實(shí)例需要的參數(shù),不就是給構(gòu)造方法用的參數(shù),或者是工廠 Bean 的參數(shù)嘛,不過要注意,在我們的初始化階段,args 是 null。
這回我們要到一個(gè)新的類了 AbstractAutowireCapableBeanFactory,看類名,AutowireCapable?類名是不是也說明了點(diǎn)問題了。
主要是為了以下場景,采用 @Autowired 注解注入屬性值:
public class MessageServiceImpl implements MessageService {
@Autowired
private UserService userService;
public String getMessage() {
return userService.getMessage();
}
}
<bean id="messageService" class="com.javadoop.example.MessageServiceImpl" />
好了,讀者要知道這么回事就可以了,繼續(xù)向前。
// AbstractAutowireCapableBeanFactory 447
/**
* Central method of this class: creates a bean instance,
* populates the bean instance, applies post-processors, etc.
* @see #doCreateBean
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// 確保 BeanDefinition 中的 Class 被加載
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// 準(zhǔn)備方法覆寫,這里又涉及到一個(gè)概念:MethodOverrides,它來自于 bean 定義中的 <lookup-method />
// 和 <replaced-method />,如果讀者感興趣,回到 bean 解析的地方看看對這兩個(gè)標(biāo)簽的解析。
// 我在附錄中也對這兩個(gè)標(biāo)簽的相關(guān)知識點(diǎn)進(jìn)行了介紹,讀者可以移步去看看
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// 讓 BeanPostProcessor 在這一步有機(jī)會返回代理,而不是 bean 實(shí)例,
// 要徹底了解清楚這個(gè),需要去看 InstantiationAwareBeanPostProcessor 接口,這里就不展開說了
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
// 重頭戲,創(chuàng)建 bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
創(chuàng)建 Bean
往里看 doCreateBean 這個(gè)方法:
/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 說明不是 FactoryBean,這里實(shí)例化 Bean,這里非常關(guān)鍵,細(xì)節(jié)之后再說
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 這個(gè)就是 Bean 里面的 我們定義的類 的實(shí)例,很多地方我描述成 "bean 實(shí)例"
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
// 類型
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
// 建議跳過吧,涉及接口:MergedBeanDefinitionPostProcessor
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// MergedBeanDefinitionPostProcessor,這個(gè)我真不展開說了,直接跳過吧,很少用的
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 下面這塊代碼是為了解決循環(huán)依賴的問題,以后有時(shí)間,我再對循環(huán)依賴這個(gè)問題進(jìn)行解析吧
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 這一步也是非常關(guān)鍵的,這一步負(fù)責(zé)屬性裝配,因?yàn)榍懊娴膶?shí)例只是實(shí)例化了,并沒有設(shè)值,這里就是設(shè)值
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 還記得 init-method 嗎?還有 InitializingBean 接口?還有 BeanPostProcessor 接口?
// 這里就是處理 bean 初始化完成后的各種回調(diào)
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
//
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
到這里,我們已經(jīng)分析完了 doCreateBean 方法,總的來說,我們已經(jīng)說完了整個(gè)初始化流程。
接下來我們挑 doCreateBean 中的三個(gè)細(xì)節(jié)出來說說。一個(gè)是創(chuàng)建 Bean 實(shí)例的 createBeanInstance 方法,一個(gè)是依賴注入的 populateBean 方法,還有就是回調(diào)方法 initializeBean。
注意了,接下來的這三個(gè)方法要認(rèn)真說那也是極其復(fù)雜的,很多地方我就點(diǎn)到為止了,感興趣的讀者可以自己往里看,最好就是碰到不懂的,自己寫代碼去調(diào)試它。
創(chuàng)建 Bean 實(shí)例
我們先看看 createBeanInstance 方法。需要說明的是,這個(gè)方法如果每個(gè)分支都分析下去,必然也是極其復(fù)雜冗長的,我們挑重點(diǎn)說。此方法的目的就是實(shí)例化我們指定的類。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// 確保已經(jīng)加載了此 class
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 校驗(yàn)一下這個(gè)類的訪問權(quán)限
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
if (mbd.getFactoryMethodName() != null) {
// 采用工廠方法factory-bean,factory-method實(shí)例化,不熟悉這個(gè)概念的讀者請看附錄,注意,不是 FactoryBean
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// 如果不是第一次創(chuàng)建,比如第二次創(chuàng)建 prototype bean。
// 這種情況下,我們可以從第一次創(chuàng)建知道,采用無參構(gòu)造函數(shù),還是構(gòu)造函數(shù)依賴注入 來完成實(shí)例化
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
// 構(gòu)造函數(shù)依賴注入
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 無參構(gòu)造函數(shù)
return instantiateBean(beanName, mbd);
}
}
// 判斷是否采用有參構(gòu)造函數(shù)
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
// 構(gòu)造函數(shù)依賴注入
return autowireConstructor(beanName, mbd, ctors, args);
}
// 調(diào)用無參構(gòu)造函數(shù)
return instantiateBean(beanName, mbd);
}
挑個(gè)簡單的無參構(gòu)造函數(shù)構(gòu)造實(shí)例來看看:
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
// 策略模式,默認(rèn)調(diào)用CglibSubclassingInstantiationStrategy
return getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
}, getAccessControlContext());
}
else {
// 策略模式,實(shí)例化
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
// 包裝一下,返回
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
我們可以看到,關(guān)鍵的地方在于:
// 策略模式,默認(rèn)調(diào)用CglibSubclassingInstantiationStrategy
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
這里會進(jìn)行實(shí)際的實(shí)例化過程,我們進(jìn)去看看:
// SimpleInstantiationStrategy 59
@Override
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
// 如果不存在方法覆寫,那就使用 java 反射進(jìn)行實(shí)例化,否則使用 CGLIB,
// 方法覆寫 請參見附錄"方法注入"中對 lookup-method 和 replaced-method 的介紹
if (bd.getMethodOverrides().isEmpty()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
// //獲取構(gòu)造方法或者工廠方法
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
@Override
public Constructor<?> run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
}
});
}
else {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
// 利用構(gòu)造方法進(jìn)行實(shí)例化
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// 存在方法覆寫,利用 CGLIB 來完成實(shí)例化,需要依賴于 CGLIB 生成子類,這里就不展開了
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
到這里,我們就算實(shí)例化完成了。我們開始說怎么進(jìn)行屬性注入。
bean 屬性注入
// AbstractAutowireCapableBeanFactory 1203
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// bean 實(shí)例的所有屬性都在這里了
PropertyValues pvs = mbd.getPropertyValues();
if (bw == null) {
if (!pvs.isEmpty()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
// 到這步的時(shí)候,bean 實(shí)例化完成(通過工廠方法或構(gòu)造方法),但是還沒開始屬性設(shè)值,
// InstantiationAwareBeanPostProcessor 的實(shí)現(xiàn)類可以在這里對 bean 進(jìn)行狀態(tài)修改,
// 我也沒找到有實(shí)際的使用,所以我們暫且忽略這塊吧
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 如果返回 false,代表不需要進(jìn)行后續(xù)的屬性設(shè)值,也不需要再經(jīng)過其他的 BeanPostProcessor 的處理
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// 通過名字找到所有屬性值,如果是 bean 依賴,先初始化依賴的 bean。記錄依賴關(guān)系
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// 通過類型裝配。復(fù)雜一些
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 這里有個(gè)非常有用的 BeanPostProcessor 進(jìn)到這里: AutowiredAnnotationBeanPostProcessor
// 對采用 @Autowired、@Value 注解的依賴進(jìn)行設(shè)值,這里的內(nèi)容也是非常豐富的,
// 不過本文不會展開說了,感興趣的讀者請自行研究
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
// 設(shè)置 bean 實(shí)例的屬性值
applyPropertyValues(beanName, mbd, bw, pvs);
}
initializeBean
屬性注入完成后,這一步其實(shí)就是處理各種回調(diào)了。
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
// 如果 bean 實(shí)現(xiàn)了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 接口,回調(diào)
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor 的 postProcessBeforeInitialization 回調(diào)
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 處理 bean 中定義的 init-method,
// 或者如果 bean 實(shí)現(xiàn)了 InitializingBean 接口,調(diào)用 afterPropertiesSet() 方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor 的 postProcessAfterInitialization 回調(diào)
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
大家發(fā)現(xiàn)沒有,BeanPostProcessor 的兩個(gè)回調(diào)都發(fā)生在這邊,只不過中間處理了 init-method,是不是和讀者原來的認(rèn)知有點(diǎn)不一樣了?
附錄
id 和 name
每個(gè) Bean 在 Spring 容器中都有一個(gè)唯一的名字(beanName)和 0 個(gè)或多個(gè)別名(aliases)。
我們從 Spring 容器中獲取 Bean 的時(shí)候,可以根據(jù) beanName,也可以通過別名。
beanFactory.getBean("beanName or alias");
在配置 <bean /> 的過程中,我們可以配置 id 和 name,看幾個(gè)例子就知道是怎么回事了。
<bean id="messageService" name="m1, m2, m3"
class="com.javadoop.example.MessageServiceImpl">
以上配置的結(jié)果就是:beanName 為 messageService,別名有 3 個(gè),分別為 m1、m2、m3。
<bean name="m1, m2, m3"
class="com.javadoop.example.MessageServiceImpl">
以上配置的結(jié)果就是:beanName 為 m1,別名有 2 個(gè),分別為 m2、m3。
<bean class="com.javadoop.example.MessageServiceImpl">
beanName 為:com.javadoop.example.MessageServiceImpl#0,
別名 1 個(gè),為: com.javadoop.example.MessageServiceImpl
<bean id="messageService"
class="com.javadoop.example.MessageServiceImpl">
以上配置的結(jié)果就是:beanName 為 messageService,沒有別名。
配置是否允許 Bean 覆蓋、是否允許循環(huán)依賴
我們說過,默認(rèn)情況下,allowBeanDefinitionOverriding 屬性為 null。如果在同一配置文件中 Bean id 或 name 重復(fù)了,會拋錯(cuò),但是如果不是同一配置文件中,會發(fā)生覆蓋。
可是有些時(shí)候我們希望在系統(tǒng)啟動的過程中就嚴(yán)格杜絕發(fā)生 Bean 覆蓋,因?yàn)槿f一出現(xiàn)這種情況,會增加我們排查問題的成本。
循環(huán)依賴說的是 A 依賴 B,而 B 又依賴 A?;蛘呤?A 依賴 B,B 依賴 C,而 C 卻依賴 A。默認(rèn) allowCircularReferences 也是 null。
它們兩個(gè)屬性是一起出現(xiàn)的,必然可以在同一個(gè)地方一起進(jìn)行配置。
添加這兩個(gè)屬性的作者 Juergen Hoeller 在這個(gè) jira 的討論中說明了怎么配置這兩個(gè)屬性。
public class NoBeanOverridingContextLoader extends ContextLoader {
@Override
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
super.customizeContext(servletContext, applicationContext);
AbstractRefreshableApplicationContext arac = (AbstractRefreshableApplicationContext) applicationContext;
arac.setAllowBeanDefinitionOverriding(false);
}
}
public class MyContextLoaderListener extends org.springframework.web.context.ContextLoaderListener {
@Override
protected ContextLoader createContextLoader() {
return new NoBeanOverridingContextLoader();
}
}
<listener>
<listener-class>com.javadoop.MyContextLoaderListener</listener-class>
</listener>
如果以上方式不能滿足你的需求,請參考這個(gè)鏈接:解決spring中不同配置文件中存在name或者id相同的bean可能引起的問題
profile
我們可以把不同環(huán)境的配置分別配置到單獨(dú)的文件中,舉個(gè)例子:
<beans profile="development"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="...">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
應(yīng)該不必做過多解釋了吧,看每個(gè)文件第一行的 profile=”"。
當(dāng)然,我們也可以在一個(gè)配置文件中使用:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<beans profile="development">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>
理解起來也很簡單吧。
接下來的問題是,怎么使用特定的 profile 呢?Spring 在啟動的過程中,會去尋找 “spring.profiles.active” 的屬性值,根據(jù)這個(gè)屬性值來的。那怎么配置這個(gè)值呢?
Spring 會在這幾個(gè)地方尋找 spring.profiles.active 的屬性值:操作系統(tǒng)環(huán)境變量、JVM 系統(tǒng)變量、web.xml 中定義的參數(shù)、JNDI。
最簡單的方式莫過于在程序啟動的時(shí)候指定:
-Dspring.profiles.active="profile1,profile2"
profile 可以激活多個(gè)
當(dāng)然,我們也可以通過代碼的形式從 Environment 中設(shè)置 profile:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh(); // 重啟
如果是 Spring Boot 的話更簡單,我們一般會創(chuàng)建 application.properties、application-dev.properties、application-prod.properties 等文件,其中 application.properties 配置各個(gè)環(huán)境通用的配置,application-{profile}.properties 中配置特定環(huán)境的配置,然后在啟動的時(shí)候指定 profile:
java -Dspring.profiles.active=prod -jar JavaDoop.jar
如果是單元測試中使用的話,在測試類中使用 @ActiveProfiles 指定,這里就不展開了。
工廠模式生成 Bean
請讀者注意 factory-bean 和 FactoryBean 的區(qū)別。這節(jié)說的是前者,是說靜態(tài)工廠或?qū)嵗S,而后者是 Spring 中的特殊接口,代表一類特殊的 Bean,附錄的下面一節(jié)會介紹 FactoryBean。
設(shè)計(jì)模式里,工廠方法模式分靜態(tài)工廠和實(shí)例工廠,我們分別看看 Spring 中怎么配置這兩個(gè),來個(gè)代碼示例就什么都清楚了。
靜態(tài)工廠:
bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
// 靜態(tài)方法
public static ClientService createInstance() {
return clientService;
}
}
實(shí)例工廠:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
FactoryBean
FactoryBean 適用于 Bean 的創(chuàng)建過程比較復(fù)雜的場景,比如數(shù)據(jù)庫連接池的創(chuàng)建。
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<T> getObjectType();
boolean isSingleton();
}
public class Person {
private Car car ;
private void setCar(Car car){ this.car = car; }
}
我們假設(shè)現(xiàn)在需要?jiǎng)?chuàng)建一個(gè) Person 的 Bean,首先我們需要一個(gè) Car 的實(shí)例,我們這里假設(shè) Car 的實(shí)例創(chuàng)建很麻煩,那么我們可以把創(chuàng)建 Car 的復(fù)雜過程包裝起來:
public class MyCarFactoryBean implements FactoryBean<Car>{
private String make;
private int year ;
public void setMake(String m){ this.make =m ; }
public void setYear(int y){ this.year = y; }
public Car getObject(){
// 這里我們假設(shè) Car 的實(shí)例化過程非常復(fù)雜,反正就不是幾行代碼可以寫完的那種
CarBuilder cb = CarBuilder.car();
if(year!=0) cb.setYear(this.year);
if(StringUtils.hasText(this.make)) cb.setMake( this.make );
return cb.factory();
}
public Class<Car> getObjectType() { return Car.class ; }
public boolean isSingleton() { return false; }
}
我們看看裝配的時(shí)候是怎么配置的:
<bean class = "com.javadoop.MyCarFactoryBean" id = "car">
<property name = "make" value ="Honda"/>
<property name = "year" value ="1984"/>
</bean>
<bean class = "com.javadoop.Person" id = "josh">
<property name = "car" ref = "car"/>
</bean>
看到不一樣了嗎?id 為 “car” 的 bean 其實(shí)指定的是一個(gè) FactoryBean,不過配置的時(shí)候,我們直接讓配置 Person 的 Bean 直接依賴于這個(gè) FactoryBean 就可以了。中間的過程 Spring 已經(jīng)封裝好了。
說到這里,我們再來點(diǎn)干貨。我們知道,現(xiàn)在還用 xml 配置 Bean 依賴的越來越少了,更多時(shí)候,我們可能會采用 java config 的方式來配置,這里有什么不一樣呢?
@Configuration
public class CarConfiguration {
@Bean
public MyCarFactoryBean carFactoryBean(){
MyCarFactoryBean cfb = new MyCarFactoryBean();
cfb.setMake("Honda");
cfb.setYear(1984);
return cfb;
}
@Bean
public Person aPerson(){
Person person = new Person();
// 注意這里的不同
person.setCar(carFactoryBean().getObject());
return person;
}
}
這個(gè)時(shí)候,其實(shí)我們的思路也很簡單,把 MyCarFactoryBean 看成是一個(gè)簡單的 Bean 就可以了,不必理會什么 FactoryBean,它是不是 FactoryBean 和我們沒關(guān)系。
這個(gè)時(shí)候,其實(shí)我們的思路也很簡單,把 MyCarFactoryBean 看成是一個(gè)簡單的 Bean 就可以了,不必理會什么 FactoryBean,它是不是 FactoryBean 和我們沒關(guān)系。
初始化 Bean 的回調(diào)
有以下四種方案:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
@Bean(initMethod = "init")
public Foo foo() {
return new Foo();
}
@PostConstruct
public void init() {
}
銷毀 Bean 的回調(diào)
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
@Bean(destroyMethod = "cleanup")
public Bar bar() {
return new Bar();
}
@PreDestroy
public void cleanup() {
}
ConversionService
既然文中說到了這個(gè),順便提一下好了。
最有用的場景就是,它用來將前端傳過來的參數(shù)和后端的 controller 方法上的參數(shù)進(jìn)行綁定的時(shí)候用。
像前端傳過來的字符串、整數(shù)要轉(zhuǎn)換為后端的 String、Integer 很容易,但是如果 controller 方法需要的是一個(gè)枚舉值,或者是 Date 這些非基礎(chǔ)類型(含基礎(chǔ)類型包裝類)值的時(shí)候,我們就可以考慮采用 ConversionService 來進(jìn)行轉(zhuǎn)換。
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.javadoop.learning.utils.StringToEnumConverterFactory"/>
</list>
</property>
</bean>
ConversionService 接口很簡單,所以要自定義一個(gè) convert 的話也很簡單。
下面再說一個(gè)實(shí)現(xiàn)這種轉(zhuǎn)換很簡單的方式,那就是實(shí)現(xiàn) Converter 接口。
來看一個(gè)很簡單的例子,這樣比什么都管用。
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
return DateUtils.parseDate(source, "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "HH:mm:ss", "HH:mm");
} catch (ParseException e) {
return null;
}
}
}
只要注冊這個(gè) Bean 就可以了。這樣,前端往后端傳的時(shí)間描述字符串就很容易綁定成 Date 類型了,不需要其他任何操作。
Bean 繼承
在初始化 Bean 的地方,我們說過了這個(gè):
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
這里涉及到的就是 <bean parent=”" /> 中的 parent 屬性,我們來看看 Spring 中是用這個(gè)來干什么的。
首先,我們要明白,這里的繼承和 java 語法中的繼承沒有任何關(guān)系,不過思路是相通的。child bean 會繼承 parent bean 的所有配置,也可以覆蓋一些配置,當(dāng)然也可以新增額外的配置。
Spring 中提供了繼承自 AbstractBeanDefinition 的 ChildBeanDefinition 來表示 child bean。
看如下一個(gè)例子:
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
</bean>
parent bean 設(shè)置了 abstract=”true” 所以它不會被實(shí)例化,child bean 繼承了 parent bean 的兩個(gè)屬性,但是對 name 屬性進(jìn)行了覆寫。
child bean 會繼承 scope、構(gòu)造器參數(shù)值、屬性值、init-method、destroy-method 等等。
當(dāng)然,我不是說 parent bean 中的 abstract = true 在這里是必須的,只是說如果加上了以后 Spring 在實(shí)例化 singleton beans 的時(shí)候會忽略這個(gè) bean。
比如下面這個(gè)極端 parent bean,它沒有指定 class,所以毫無疑問,這個(gè) bean 的作用就是用來充當(dāng)模板用的 parent bean,此處就必須加上 abstract = true。
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
方法注入
一般來說,我們的應(yīng)用中大多數(shù)的 Bean 都是 singleton 的。singleton 依賴 singleton,或者 prototype 依賴 prototype 都很好解決,直接設(shè)置屬性依賴就可以了。
但是,如果是 singleton 依賴 prototype 呢?這個(gè)時(shí)候不能用屬性依賴,因?yàn)槿绻脤傩砸蕾嚨脑?,我們每次其?shí)拿到的還是第一次初始化時(shí)候的 bean。
一種解決方案就是不要用屬性依賴,每次獲取依賴的 bean 的時(shí)候從 BeanFactory 中取。這個(gè)也是大家最常用的方式了吧。怎么取,我就不介紹了,大部分 Spring 項(xiàng)目大家都會定義那么個(gè)工具類的。
另一種解決方案就是這里要介紹的通過使用 Lookup method。
lookup-method
我們來看一下 Spring Reference 中提供的一個(gè)例子:
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();
}
xml 配置 <lookup-method />:
<!-- 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>
Spring 采用 CGLIB 生成字節(jié)碼的方式來生成一個(gè)子類。我們定義的類不能定義為 final class,抽象方法上也不能加 final。
lookup-method 上的配置也可以采用注解來完成,這樣就可以不用配置 <lookup-method /> 了,其他不變:
public abstract class CommandManager {
public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
注意,既然用了注解,要配置注解掃描:<context:component-scan base-package=”com.javadoop” />
甚至,我們可以像下面這樣:
public abstract class CommandManager {
public Object process(Object commandState) {
MyCommand command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract MyCommand createCommand();
}
上面的返回值用了 MyCommand,當(dāng)然,如果 Command 只有一個(gè)實(shí)現(xiàn)類,那返回值也可以寫 Command。
replaced-method
記住它的功能,就是替換掉 bean 中的一些方法。
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
方法覆寫,注意要實(shí)現(xiàn) MethodReplacer 接口:
public class ReplacementComputeValue implements org.springframework.beans.factory.support.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 id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- 定義 computeValue 這個(gè)方法要被替換掉 -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
arg-type 明顯不是必須的,除非存在方法重載,這樣必須通過參數(shù)類型列表來判斷這里要覆蓋哪個(gè)方法。
BeanPostProcessor
應(yīng)該說 BeanPostProcessor 概念在 Spring 中也是比較重要的。我們看下接口定義:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
看這個(gè)接口中的兩個(gè)方法名字我們大體上可以猜測 bean 在初始化之前會執(zhí)行 postProcessBeforeInitialization 這個(gè)方法,初始化完成之后會執(zhí)行 postProcessAfterInitialization 這個(gè)方法。但是,這么理解是非常片面的。
首先,我們要明白,除了我們自己定義的 BeanPostProcessor 實(shí)現(xiàn)外,Spring 容器在啟動時(shí)自動給我們也加了幾個(gè)。如在獲取 BeanFactory 的 obtainFactory() 方法結(jié)束后的 prepareBeanFactory(factory),大家仔細(xì)看會發(fā)現(xiàn),Spring 往容器中添加了這兩個(gè) BeanPostProcessor:ApplicationContextAwareProcessor、ApplicationListenerDetector。
我們回到這個(gè)接口本身,讀者請看第一個(gè)方法,這個(gè)方法接受的第一個(gè)參數(shù)是 bean 實(shí)例,第二個(gè)參數(shù)是 bean 的名字,重點(diǎn)在返回值將會作為新的 bean 實(shí)例,所以,沒事的話這里不能隨便返回個(gè) null。
那意味著什么呢?我們很容易想到的就是,我們這里可以對一些我們想要修飾的 bean 實(shí)例做一些事情。但是對于 Spring 框架來說,它會決定是不是要在這個(gè)方法中返回 bean 實(shí)例的代理,這樣就有更大的想象空間了。
最后,我們說說如果我們自己定義一個(gè) bean 實(shí)現(xiàn) BeanPostProcessor 的話,它的執(zhí)行時(shí)機(jī)是什么時(shí)候?
如果仔細(xì)看了代碼分析的話,其實(shí)很容易知道了,在 bean 實(shí)例化完成、屬性注入完成之后,會執(zhí)行回調(diào)方法,具體請參見類 AbstractAutowireCapableBeanFactory#initBean 方法。
首先會回調(diào)幾個(gè)實(shí)現(xiàn)了 Aware 接口的 bean,然后就開始回調(diào) BeanPostProcessor 的 postProcessBeforeInitialization 方法,之后是回調(diào) init-method,然后再回調(diào) BeanPostProcessor 的 postProcessAfterInitialization 方法。