Spring源碼分析(二)bean的實例化和IOC依賴注入

前言

咱們知道,IOC是Spring的核心。它來負責控制對象的生命週期和對象間的關係。 舉個例子,咱們如何來找對象的呢?常見的狀況是,在路上要處處去看哪一個MM既漂亮身材又好,符合咱們的口味。就打聽她們的電話號碼,製造關聯想辦法認識她們,而後...這裏省略N步,最後談戀愛結婚。 IOC在這裏就像婚介所,裏面有不少適婚男女的資料,若是你有需求,直接告訴它你須要個什麼樣的女友就行了。它會給咱們提供一個MM,直接談戀愛結婚,完美! 下面就來看Spring是如何生成並管理這些對象的呢?java

1、方法入口

org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons()方法是今天的主角,一切從它開始。spring

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);
			}
		}
	}
複製代碼

2、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);
	}
}
複製代碼

3、Annotation的支持

在Bean實例化完成以後,會進入一段後置處理器的代碼。從代碼上看,過濾實現了MergedBeanDefinitionPostProcessor接口的類,調用其postProcessMergedBeanDefinition()方法。都是誰實現了MergedBeanDefinitionPostProcessor接口呢?咱們重點看三個bash

AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor
複製代碼

記不記得在Spring源碼分析(一)Spring的初始化和XML這一章節中,咱們說Spring對annotation-config標籤的支持,註冊了一些特殊的Bean,正好就包含上面這三個。下面來看它們偷偷作了什麼呢? 從方法名字來看,它們作了相同一件事,加載註解元數據。方法內部又作了相同的兩件事mvc

ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() 
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback()
複製代碼

看方法的參數,targetClass就是Bean的Class對象。接下來就能夠獲取它的字段和方法,判斷是否包含了相應的註解,最後轉成InjectionMetadata對象,下面以一段僞代碼展現處理過程。app

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,在註解式的依賴注入的時候重點就靠它們。ide

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));
		}
	}
});
複製代碼

4、依賴注入

前面完成了在doCreateBean()方法Bean的實例化,接下來就是依賴注入。 Bean的依賴注入有兩種方式,一種是配置文件,一種是註解式。post

一、 註解式的注入過程

在上面第3小節,Spring已通過濾了Bean實例上包含@Autowired、@Resource等註解的Field和Method,並返回了包含Class對象、內省對象、成員的InjectionMetadata對象。仍是以@Autowired爲例,此次調用到AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues()。 首先拿到InjectionMetadata對象,再判斷裏面的InjectedElement集合是否爲空,也就是說判斷在Bean的字段和方法上是否包含@Autowired。而後調用InjectedElement.inject()。InjectedElement有兩個子類AutowiredFieldElement、AutowiredMethodElement,很顯然一個是處理Field,一個是處理Method。ui

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);
複製代碼

二、配置文件的注入過程

先來看一個配置文件,咱們在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);
}
複製代碼

5、initializeBean

在Bean實例化和IOC依賴注入後,Spring留出了擴展,可讓咱們對Bean作一些初始化的工做。

一、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 ...未完
複製代碼

二、初始化

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);
複製代碼

6、註冊

registerDisposableBeanIfNecessary()完成Bean的緩存註冊工做,把Bean註冊到Map中。

相關文章
相關標籤/搜索