Spring Framework 源碼閱讀(一):Aware接口及其實作類的作用
上一篇博文已經介紹過怎麼去編譯
Spring Framework
源碼了,其實很簡單,無非就是下載下傳依賴然後使用編譯器
build
項目。
- 編譯 Spring Framework 5.2.17源碼 & 在源碼中使用 ApplicationContext 擷取定義的Bean
使用過
Spring
系列架構進行開發的朋友應該都知道
Bean
,它是
Spring IOC
(控制反轉)或者說
DI
(依賴注入)的基本機關,
Spring
通過
BeanFactory
來管理
Bean
,使得我們不需要手動去執行個體化這些被依賴的
Bean
,而是由
Spring
的
BeanFactory
去發現這些
Bean
之間的依賴關系,以便确認目前需要的
Bean
的執行個體化先後順序;當然實作這些功能還是有點複雜的,從源碼中也可以看出,這裡隻是簡單的概述;看源碼不能急躁,一步一步去探索,最後發現沒有盡頭(ㄒoㄒ)。
比如
AbstractBeanFactory
中的
doGetBean
方法就有保證對目前
Bean
所依賴的
Bean
進行初始化。
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 保證對目前bean所依賴的bean進行初始化。
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
// beanName和dep之間存在循環依賴關系
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
上面隻是為了引出
Bean
以及
BeanFactory
的一些概念,實際上要複雜的多,部落客也會在以後的部落格中進行詳細介紹。
很顯然
BeanFactory
能夠感覺到
Bean
的存在(
ApplicationContext
(應用上下文)的實作類通過一些工具類來解析注解定義、類路徑資訊以及
XML
配置檔案等
Bean
定義源來感覺
Bean
,如
AnnotationConfigApplicationContext
中的
AnnotatedBeanDefinitionReader
、
ClassPathBeanDefinitionScanner
以及
XmlWebApplicationContext
中的
XmlBeanDefinitionReader
等),并且建立和管理這些
Bean
。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SN1ETOxgzN0cDOlNDZ3ImNzYzX4EzMxgTM5AzLcFTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
但
Bean
怎麼感覺到
BeanFactory
的存在呢?,我們定義
Bean
,一般就直接加注解或者在
XML
配置檔案中進行定義,但這些定義不會涉及到
BeanFactory
的資訊,
BeanFactory
就像是一個黑盒子;那現在就有一個問題了,當我們定義的
Bean
需要知道
BeanFactory
的一些資訊該怎麼辦呢?這就是
Aware
接口及其實作類的工作了,而
aware
也有意識到的意思,也就是意識到
BeanFactory
中某些資訊的存在。
package org.springframework.beans.factory;
/**
* A marker superinterface indicating that a bean is eligible to be notified by the
* Spring container of a particular framework object through a callback-style method.
* The actual method signature is determined by individual subinterfaces but should
* typically consist of just one void-returning method that accepts a single argument.
*
* <p>Note that merely implementing {@link Aware} provides no default functionality.
* Rather, processing must be done explicitly, for example in a
* {@link org.springframework.beans.factory.config.BeanPostProcessor}.
* Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
* for an example of processing specific {@code *Aware} interface callbacks.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public interface Aware {}
英文的大概意思:
- 這是一個起标記作用的超接口,訓示
可以通過回調形式的方法得到特定架構對象的Spring容器的通知。實際的方法由各個子接口決定(根據需要),但通常隻包含一個接受單個參數并且傳回Bean
的方法。void
- 僅僅實作
接口并不會提供預設功能。相反,處理必須顯式地完成,例如在Aware
接口中。參考BeanPostProcessor
中處理特定ApplicationContextAwareProcessor
接口回調的例子。Aware
先不深究
BeanPostProcessor
的實作,隻需要知道在
Bean
調用初始化方法的前後會調用
BeanPostProcessor
中的方法,以便對
Bean
實作定制化。
ApplicationContextAwareProcessor
中的
postProcessBeforeInitialization
方法就是在
Bean
調用初始化方法之前會被調用,該方法又會調用
invokeAwareInterfaces
方法,在該方法中就會調用
Aware
接口實作類中的接受單個參數并且傳回
void
的方法。
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
/**
* Create a new ApplicationContextAwareProcessor for the given context.
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
return bean;
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
EnvironmentAware
、
EmbeddedValueResolverAware
、
ResourceLoaderAware
、
ApplicationEventPublisherAware
、
MessageSourceAware
以及
ApplicationContextAware
都是
Aware
接口的子接口。
public interface ApplicationContextAware extends Aware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
是以,在
Bean
調用初始化方法的前後,會有一些方法去調用
Aware
接口實作類中的方法,進而實作通過回調形式的方法得到特定架構對象的
Spring
容器的通知,這樣
Bean
就可以擷取到
BeanFactory
的一些資訊了。
當然,還有一些
Aware
接口的子接口,比如
BeanNameAware
、
BeanClassLoaderAware
、
BeanFactoryAware
、
ServletContextAware
以及
ServletConfigAware
等,而這些子接口的回調是在其他類中實作的,
AbstractAutowireCapableBeanFactory
抽象類和
ServletContextAwareProcessor
類中。其實目前介紹過的
Aware
子接口,在
Bean
的整個生命周期中有所展現,以便讓
Bean
感覺
BeanFactory
的各種資訊,實作特定的功能。
AbstractAutowireCapableBeanFactory
抽象類(繼承了
AbstractBeanFactory
抽象類):
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
ServletContextAwareProcessor
類(實作了
BeanPostProcessor
接口):
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (getServletContext() != null && bean instanceof ServletContextAware) {
((ServletContextAware) bean).setServletContext(getServletContext());
}
if (getServletConfig() != null && bean instanceof ServletConfigAware) {
((ServletConfigAware) bean).setServletConfig(getServletConfig());
}
return bean;
}
到這裡大家應該知道
Aware
接口及其實作類的作用了吧,其中的方法會在建立
Bean
的過程中被回調,以便設定一些
BeanFactory
中的資訊。
在
Spring Framework
源碼中建立一個子
Module
,在上一篇部落格中已經介紹過了,這裡就不贅述了。
IMessageService
接口:
package com.kaven.service;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.ApplicationContextAware;
public interface IMessageService extends ApplicationContextAware, BeanFactoryAware {
String getMessage();
}
實作類
MessageServiceImpl
:
package com.kaven.service.impl;
import com.kaven.service.IMessageService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
@Service("message")
public class MessageServiceImpl implements IMessageService {
@Override
public String getMessage() {
return "Hello Kaven";
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("----------------MessageServiceImpl----------------------");
System.out.println("setApplicationContext");
System.out.println(applicationContext.getBeanDefinitionCount());
System.out.println(applicationContext);
System.out.println("----------------MessageServiceImpl----------------------");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("----------------MessageServiceImpl----------------------");
System.out.println("setBeanFactory");
System.out.println(beanFactory);
System.out.println("----------------MessageServiceImpl----------------------");
}
}
啟動類
Application
:
package com.kaven;
import com.kaven.service.IMessageService;
import com.kaven.service.impl.MessageServiceImpl;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;
import java.util.Arrays;
@ComponentScan({"com.kaven"})
public class Application implements BeanNameAware, EnvironmentAware {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
IMessageService service = (MessageServiceImpl) context.getBean("message");
System.out.println(service.getMessage());
System.out.println(context.getBeanDefinitionCount());
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
((Application)context.getBean("application")).print("Hello Kaven");
}
public void print(String str){
System.out.println(str);
}
@Override
public void setBeanName(String name) {
System.out.println("---------Application----------");
System.out.println(name);
System.out.println("---------Application----------");
}
@Override
public void setEnvironment(Environment environment) {
System.out.println("---------Application----------");
System.out.println(environment);
System.out.println("---------Application----------");
}
}
輸出結果:
顯然是可以的。