前言
我們知道,IOC是Spring的核心。它來負責控制物件的生命週期和物件間的關係。 舉個例子,我們如何來找物件的呢?常見的情況是,在路上要到處去看哪個MM既漂亮身材又好,符合我們的口味。就打聽她們的電話號碼,製造關聯想辦法認識她們,然後...這裡省略N步,最後談戀愛結婚。 IOC在這裡就像婚介所,裡面有很多適婚男女的資料,如果你有需求,直接告訴它你需要個什麼樣的女朋友就好了。它會給我們提供一個MM,直接談戀愛結婚,完美! 下面就來看Spring是如何生成並管理這些物件的呢?
一、方法入口
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons()
方法是今天的主角,一切從它開始。
public void preInstantiateSingletons() throws BeansException {
//beanDefinitionNames就是上一節初始化完成後的所有BeanDefinition的beanName
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//getBean是主力中的主力,負責例項化Bean和IOC依賴注入
getBean(beanName);
}
}
}
複製程式碼
二、Bean的例項化
在入口方法getBean中,首先呼叫了doCreateBean方法。第一步就是通過反射例項化一個Bean。
protected Object doCreateBean(final String beanName,
final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//createBeanInstance就是例項化Bean的過程,就是一些判斷加反射,最後呼叫ctor.newInstance(args);
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
}
複製程式碼
三、Annotation的支援
在Bean例項化完成之後,會進入一段後置處理器的程式碼。從程式碼上看,過濾實現了MergedBeanDefinitionPostProcessor介面的類,呼叫其postProcessMergedBeanDefinition()方法。都是誰實現了MergedBeanDefinitionPostProcessor介面呢?我們重點看三個
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor
複製程式碼
記不記得在Spring原始碼分析(一)Spring的初始化和XML這一章節中,我們說Spring對annotation-config標籤的支援,註冊了一些特殊的Bean,正好就包含上面這三個。下面來看它們偷偷做了什麼呢? 從方法名字來看,它們做了相同一件事,載入註解後設資料。方法內部又做了相同的兩件事
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback()
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback()
複製程式碼
看方法的引數,targetClass就是Bean的Class物件。接下來就可以獲取它的欄位和方法,判斷是否包含了相應的註解,最後轉成InjectionMetadata物件,下面以一段虛擬碼展示處理過程。
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.viewscenes.netsupervisor.entity.User");
Field[] fields = clazz.getFields();
Method[] methods = clazz.getMethods();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(Autowired.class)) {
//轉換成AutowiredFieldElement物件,加入容器
}
}
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.isAnnotationPresent(Autowired.class)) {
//轉換成AutowiredMethodElement物件,加入容器
}
}
return new InjectionMetadata(clazz, elements);
}
複製程式碼
InjectionMetadata物件有兩個重要的屬性:targetClass ,injectedElements,在註解式的依賴注入的時候重點就靠它們。
public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
//targetClass是Bean的Class物件
this.targetClass = targetClass;
//injectedElements是一個InjectedElement物件的集合
this.injectedElements = elements;
}
//member是成員本身,欄位或者方法
//pd是JDK中的內省機制物件,後面的注入屬性值要用到
protected InjectedElement(Member member, PropertyDescriptor pd) {
this.member = member;
this.isField = (member instanceof Field);
this.pd = pd;
}
複製程式碼
說了這麼多,最後再看下原始碼裡面是什麼樣的,以Autowired 為例。
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields");
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
}
});
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods");
}
return;
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should be used on methods with parameters:" );
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
}
});
複製程式碼
四、依賴注入
前面完成了在doCreateBean()方法Bean的例項化,接下來就是依賴注入。 Bean的依賴注入有兩種方式,一種是配置檔案,一種是註解式。
1、 註解式的注入過程
在上面第3小節,Spring已經過濾了Bean例項上包含@Autowired、@Resource等註解的Field和Method,並返回了包含Class物件、內省物件、成員的InjectionMetadata物件。還是以@Autowired為例,這次呼叫到AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues()
。
首先拿到InjectionMetadata物件,再判斷裡面的InjectedElement集合是否為空,也就是說判斷在Bean的欄位和方法上是否包含@Autowired。然後呼叫InjectedElement.inject()。InjectedElement有兩個子類AutowiredFieldElement、AutowiredMethodElement
,很顯然一個是處理Field,一個是處理Method。
AutowiredFieldElement
如果Autowired註解在欄位上,它的配置是這樣。
public class User {
@Autowired
Role role;
}
複製程式碼
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
//以User類中的@Autowired Role role為例,這裡的field就是
//public com.viewscenes.netsupervisor.entity.Role
//com.viewscenes.netsupervisor.entity.User.role
Field field = (Field) this.member;
Object value;
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
//這裡的beanName因為Bean,所以會重新進入populateBean方法,先完成Role物件的注入
//value == com.viewscenes.netsupervisor.entity.Role@7228c85c
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
if (value != null) {
//設定可訪問,直接賦值
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
複製程式碼
AutowiredFieldElement
如果Autowired註解在方法上,就得這樣寫。
public class User {
@Autowired
public void setRole(Role role) {}
}
複製程式碼
它的inject方法和上面類似,不過最後是method.invoke。感興趣的小夥伴可以去翻翻原始碼。
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
複製程式碼
2、配置檔案的注入過程
先來看一個配置檔案,我們在User類中注入了id,name,age和Role的例項。
<bean id="user" class="com.viewscenes.netsupervisor.entity.User">
<property name="id" value="1001"></property>
<property name="name" value="網機動車"></property>
<property name="age" value="24"></property>
<property name="role" ref="role"></property>
</bean>
<bean id="role" class="com.viewscenes.netsupervisor.entity.Role">
<property name="id" value="1002"></property>
<property name="name" value="中心管理員"></property>
</bean>
複製程式碼
在Spring原始碼分析(一)Spring的初始化和XML這一章節的4.2 小節,bean標籤的解析,我們看到在反射得到Bean的Class物件後,會設定它的property屬性,也就是呼叫了parsePropertyElements()方法。在BeanDefinition物件裡有個MutablePropertyValues屬性。
MutablePropertyValues:
//propertyValueList就是有幾個property 節點
List<PropertyValue> propertyValueList:
PropertyValue:
name //對應配置檔案中的name ==id
value //對應配置檔案中的value ==1001
PropertyValue:
name //對應配置檔案中的name ==name
value //對應配置檔案中的value ==網機動車
複製程式碼
上圖就是BeanDefinition物件裡面MutablePropertyValues屬性的結構。既然已經拿到了property的名稱和值,注入就比較簡單了。從內省物件PropertyDescriptor中拿到writeMethod物件,設定可訪問,invoke即可。PropertyDescriptor有兩個物件readMethodRef、writeMethodRef
其實對應的就是get set方法。
public void setValue(final Object object, Object valueToApply) throws Exception {
//pd 是內省物件PropertyDescriptor
final Method writeMethod = this.pd.getWriteMethod());
writeMethod.setAccessible(true);
final Object value = valueToApply;
//以id為例 writeMethod == public void com.viewscenes.netsupervisor.entity.User.setId(string)
writeMethod.invoke(getWrappedInstance(), value);
}
複製程式碼
五、initializeBean
在Bean例項化和IOC依賴注入後,Spring留出了擴充套件,可以讓我們對Bean做一些初始化的工作。
1、Aware
Aware是一個空的介面,什麼也沒有。不過有很多xxxAware繼承自它,下面來看原始碼。如果有需要,我們的Bean可以實現下面的介面拿到我們想要的。
//在例項化和IOC依賴注入完成後呼叫
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
//讓我們的Bean可以拿到自身在容器中的beanName
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
//可以拿到ClassLoader物件
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
//可以拿到BeanFactory物件
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
......未完
}
}
複製程式碼
做法如下:
public class AwareTest1 implements BeanNameAware,BeanClassLoaderAware,BeanFactoryAware{
public void setBeanName(String name) {
System.out.println("BeanNameAware:" + name);
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware:" + beanFactory);
}
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("BeanClassLoaderAware:" + classLoader);
}
}
//輸出結果
BeanNameAware:awareTest1
BeanClassLoaderAware:WebappClassLoader
context: /springmvc_dubbo_producer
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
java.net.URLClassLoader@2626b418
BeanFactoryAware:org.springframework.beans.factory.support.DefaultListableBean
Factory@5b4686b4: defining beans ...未完
複製程式碼
2、初始化
Bean的初始化方法有三種方式,按照先後順序是,@PostConstruct、afterPropertiesSet、init-method
@PostConstruct
這個註解隱藏的比較深,它是在CommonAnnotationBeanPostProcessor的父類InitDestroyAnnotationBeanPostProcessor呼叫到的。這個註解的初始化方法不支援帶引數,會直接拋異常。
if (method.getParameterTypes().length != 0) {
throw new IllegalStateException("Lifecycle method annotation requires a no-arg method");
}
public void invoke(Object target) throws Throwable {
ReflectionUtils.makeAccessible(this.method);
this.method.invoke(target, (Object[]) null);
}
複製程式碼
afterPropertiesSet
這個要實現InitializingBean介面。這個也不能有引數,因為它介面方法就沒有定義引數。
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
((InitializingBean) bean).afterPropertiesSet();
}
複製程式碼
init-method
ReflectionUtils.makeAccessible(initMethod);
initMethod.invoke(bean);
複製程式碼
六、註冊
registerDisposableBeanIfNecessary()完成Bean的快取註冊工作,把Bean註冊到Map中。