Spring Ioc容器依賴注入

1、時序圖

IOC容器的依賴注入是建立在數據BeanDefinition準備好的前提之下的。依賴注入的發生有兩種情況:系統第一次向容器索要bean的時候;bean在配置的時候設置了Lazy-init屬性,該屬性會讓容器完成預實例化,預實例化就是一個依賴注入的過程。Bean的依賴注入看下DefaultListableBeanFactory的基類AbstractBeanFactory的getBean裏面的實現來看下bean依賴注入的全流程。IOC依賴注入的時序圖如下所示:


圖10、spring ioc容器bean注入時序圖

在時序圖中可以看到依賴注入主要有兩個階段:instantiate即bean的實例化;populateBean即對bean所依賴的引用以及屬性進行初始化。

2、instantiate

<pre name="code" class="java">public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (beanDefinition.getMethodOverrides().isEmpty()) {
			Constructor<?> constructorToUse;
			synchronized (beanDefinition.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class clazz = beanDefinition.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
								public Constructor run() throws Exception {
									return clazz.getDeclaredConstructor((Class[]) null);
								}
							});
						}
						else {
							constructorToUse =	clazz.getDeclaredConstructor((Class[]) null);
						}
						beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Exception ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(beanDefinition, beanName, owner);
		}
	}



代碼層次比較清晰,beanDefinition是從之前載入的BeanDefinition中獲取的根節點數據,一種是使用BeanUtils進行實例化,其實就是反射機制進行實例化,找出類的構造函數,再使用構造函數ctor.newInstance(args)實例化一個對象。另外一種就是使用CGLIB進行實例化,CGLIB構造一個實例的基本實現流程如下:

public Object instantiate(Constructor ctor, Object[] args) {
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(this.beanDefinition.getBeanClass());
			enhancer.setCallbackFilter(new CallbackFilterImpl());
			enhancer.setCallbacks(new Callback[] {
					NoOp.INSTANCE,
					new LookupOverrideMethodInterceptor(),
					new ReplaceOverrideMethodInterceptor()
			});
			return (ctor == null) ? 
					enhancer.create() : 
					enhancer.create(ctor.getParameterTypes(), args);
		}

首先生成一個Enhancer對象,在使用enhancer對基類、回調方法進行設置,最後使用cglib的create生成一個bean對象。這裏簡單闡述一下反射進行實例化的一個流程。

ClassInstance ci05 = null;
        //額外的思考 在第二種類實例化的方式中有沒有一種方法實現有參數的構造方式
        //獲得類的構造信息
        Constructor[] ctor = Class.forName("ClassInstance").getDeclaredConstructors();
        //找到我們需要的構造方法
        for(int i=0;i<ctor.length;i++ ){
            Class[] cl = ctor[i].getParameterTypes();
            if(cl.length == 1){
                //實例化對象
                ci05 = (ClassInstance) Class.forName("ClassInstance").getConstructor(cl).newInstance(new Object[]{"05"});
            }
        }
        ci05.fun();

基本流程就是上述。類實例化都是在jvm裏面進行的,所以實例化的步驟是如下的:

1、  類加載到jvm;

2、  Jvm對類進行鏈接;

3、  Jvm實例化對象。

上述代碼流程裏面Class.forName("ClassInstance")這個的作用就是前兩步功能;第三步就是ctor. newInstance(args),這樣就是完成了一個類實例化的過程,一般我們在系統裏面new關鍵字就已經將上述三個步驟全部涵蓋了。

在Spring IOC實例化的過程中還有一個地方需要注意的:

protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
		....
		}

值得注意的地方就是if (sharedInstance != null && args == null) 這段代碼的判斷是看這個bean是從beanFactory裏面獲取(false)還是從factoryBean來生產(true)FactoryBean.getObject(),FactoryBean是一個工廠模式。Beanfactory是所有bean的出處,即使FactoryBean也是從Beanfactory來的,那麼FactoryBean是做什麼的呢?我們看下它類的繼承關係就比較容易理解:


圖11、FacotryBean的類關係圖

也就是說FactoryBean其實是各類特定類型beanfactory的工廠,說起來有些拗口,其實就是裏面出來的都是一個factory,例如RMI,JNDI,PROXY等等類型的factory。就可以由其生產。它的設計模式是一個工廠方法。

 

3、populateBean

populateBean就是bean相關屬性以及依賴類的加載,這裏會觸發相關bean的依賴注入。

在這個過程中也有兩個階段,如下代碼所示:
List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
		boolean resolveNecessary = false;
		for (PropertyValue pv : original) {
			if (pv.isConverted()) {
				deepCopy.add(pv);
			}
			else {
				String propertyName = pv.getName();
				Object originalValue = pv.getValue();
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				boolean convertible = bw.isWritableProperty(propertyName) &&
						!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
				if (convertible) {
					convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
				}
				// Possibly store converted value in merged bean definition,
				// in order to avoid re-conversion for every created bean instance.
				if (resolvedValue == originalValue) {
					if (convertible) {
						pv.setConvertedValue(convertedValue);
					}
					deepCopy.add(pv);
				}
				else if (convertible && originalValue instanceof TypedStringValue &&
						!((TypedStringValue) originalValue).isDynamic() &&
						!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
					pv.setConvertedValue(convertedValue);
					deepCopy.add(pv);
				}
				else {
					resolveNecessary = true;
					deepCopy.add(new PropertyValue(pv, convertedValue));
				}
			}
		}
		if (mpvs != null && !resolveNecessary) {
			mpvs.setConverted();
		}

		// Set our (possibly massaged) deep copy.
		try {
			bw.setPropertyValues(new MutablePropertyValues(deepCopy));
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Error setting property values", ex);
		}

第一個階段就是resolveValueIfNecessary,這個就是將改類相關的依賴類找出來,這個有很多類型,list,set,map以及object等等,最終模式都是迴歸到resolveReference上來,這個功能就是最終使用beanFacotry.getBean()來完成bean的依賴實例化。

第二個階段就是setPropertyValues,這個過程就是將bean的set和get方法讀出來,將這些類set進去或是read出來。對於在xml中手動配置的bean ref這個比較容易理解,而對於系統自動依賴注入的方式這裏簡單介紹下:

Spring不但支持自己定義的@Autowired註解,還支持幾個由JSR-250規範定義的註解,它們分別是@Resource、@PostConstruct以及@PreDestroy。

  @Resource的作用相當於@Autowired,只不過@Autowired按byType自動注入,而@Resource默認按 byName自動注入罷了。@Resource有兩個屬性是比較重要的,分是name和type,Spring將@Resource註解的name屬性解析爲bean的名字,而type屬性則解析爲bean的類型。所以如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不指定name也不指定type屬性,這時將通過反射機制使用byName自動注入策略。

  @Resource裝配順序

  1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常

  2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常

  3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常

4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退爲一個原始類型進行匹配,如果匹配則自動裝配;

@Autowired 與@Resource的區別:

1、 @Autowired與@Resource都可以用來裝配bean. 都可以寫在字段上,或寫在setter方法上。

2、 @Autowired默認按類型裝配(這個註解是屬業spring的),默認情況下必須要求依賴對象必須存在,如果要允許null值,可以設置它的required屬性爲false,如:@Autowired(required=false) ,如果我們想使用名稱裝配可以結合@Qualifier註解進行使用,如下:

@Autowired()

@Qualifier("baseDao")

private BaseDaobaseDao;

3、@Resource(這個註解屬於J2EE的),默認安裝名稱進行裝配,名稱可以通過name屬性進行指定,如果沒有指定name屬性,當註解寫在字段上時,默認取字段名進行安裝名稱查找,如果註解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。

@Resource(name="baseDao")

private BaseDao baseDao;

4、IOC依賴注入設計模式

在上面講到FactoryBean採用的是工廠方法,我們在這裏講述下改設計模式:

在FactoryBean的設計中Create就是FactoryBean,而ConcreateCreate就相當於HttpInvokerProxyFactoryBean,FactoryMethod就是getObject(),獲取到的object其實就是一個產品,例如ProxyFactory,而這個proxyFacory就可以生產中各類的Proxy出來,這個ProxyFactory就相當於ConcreateProduct了。

工廠方法的優點:

首先,良好的封裝性,代碼結構清晰。一個對象創建是有條件約束的,如一個調用者需要一個具體的產品對象,只要知道這個產品的類名(或約束字符串)就可以了,不用知道創建對象的艱辛過程,減少模塊間的耦合。 

其次,工廠方法模式的擴展性非常優秀。在增加產品類的情況下,只要適當地修改具體的工廠類或擴展一個工廠類,就可以完成“擁抱變化”。

再次,屏蔽產品類。這一特點非常重要,產品類的實現如何變化,調用者都不需要關心,它只需要關心產品的接口,只要接口保持不表,系統中的上層模塊就不要發生變化,因爲產品類的實例化工作是由工廠類負責,一個產品對象具體由哪一個產品生成是由工廠類決定的。在數據庫開發中,大家應該能夠深刻體會到工廠方法模式的好處:如果使用JDBC連接數據庫,數據庫從MySql切換到Oracle,需要改動地方就是切換一下驅動名稱(前提條件是SQL語句是標準語句),其他的都不需要修改,這是工廠方法模式靈活性的一個直接案例。

最後,工廠方法模式是典型的解耦框架。高層模塊值需要知道產品的抽象類,其他的實現類都不用關心,符合迪米特原則,我不需要的就不要去交流;也符合依賴倒轉原則,只依賴產品類的抽象;當然也符合里氏替換原則,使用產品子類替換產品父類。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章