天天看點

spring之IOC總結

spring中,對于IOC(控制反轉)和DI(依賴注入)。

控制反轉和依賴注入,說的都是一個東西。控制反轉是說java對象中依賴對象的建立,由原來通過new建立,變成由第三方spring容器建立,用時直接在代碼中聲明,容器會主動把建立好的對象動态的注入到代碼中(依賴注入)。不管是建立一個依賴另一個對象的對象,還是建立一個簡單的對象,spring容器都可以做到。  用控制反轉的好處當然就是實作代碼間的松耦合啦,現實的例子就是:我需要一把斧子,以前的方式是自己去生産一個,而現在就是由斧子工廠生産,我直接從工廠裡拿來用,這樣達到了人和斧子對象之間很好的解耦。因為控制反轉不容易了解,是以後來改為依賴注入,因為依賴注入說明了這樣思想的實作方式。

為了實作這樣的功能,在代碼中又是怎呀實作的呢?首先,我們需要考慮兩個問題:1.在spring容器中的bean是怎樣建立的?2.建立好的bean又是怎樣動态注入到代碼中的?

如果這兩個問題解決了,那麼就可以實作上面說的那種控制反轉的思想了。

首先,講bean的建立前先說spring容器,spring架構提供給我們一個spring容器,也叫IOC容器。這個容器就是用來建立bean的,不僅可以建立對象,還維護對象間的關系,管理對象的生命周期。代碼中由BeanFactory來表示,但是我們開發一般用它的子接口ApplicationContext,因為它裡面的方法更多一些,比如更易 與Spring AOP內建、資源處理(國際化處理)、事件傳遞及各種不同應用層的context實作 (如針對web應用的WebApplicationContext)。簡而言之,BeanFactory提供了配制架構及基本功能,而 ApplicationContext 則增加了更多支援企業核心内容的功能。除了功能不同外,還有個差別,ApplicationContext是在初始化容器時就直接建立在裡面注冊的bean,而BeanFactory容器是延遲加載的,即容器初始化時,裡面的bean不是立即建立,而是代碼中用到bean時才建立執行個體化。而對于接口ApplicationContext,他的實作類有兩種ClassPathXmlApplicationContext(從類路徑下加載配置檔案)和FileSysytemApplicationContext(從檔案系統加載配置檔案)

代碼實作:1.建立一個spring容器

//從類路徑下加載配置檔案,配置檔案裡就是一些需要注冊進容器的bean。此句表示建立一個spring容器并初始化裡面的bean。	
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); 
         //從容器中取出bean
	Food food=(Food) context.getBean("food22");  //food22已經在容器中注冊過才能拿來用,後面會講bean如何注冊進容器
           

spring容器中的bean是怎樣建立的呢?---有3種。1.通過構造函數(實質就是通過反射機制實作的),一般我們用的是這種方式。2.通過靜态工廠方法。3.通過執行個體工廠方法。

1.通過構造器,可以用預設的無參構造器,也可以用自定義的有參構造器

package com.yxj.spring.entity;

public class Food {
	private String name;
	private int price;
	private String type;
	
	//1.此處用預設構造器建立一個bean對象
	public Food() {
		super();
	}
	
	//2.同樣是構造器建立一個bean,可以根據自己需求設定一個有參構造器
	public Food(String name, int price, String type) {
		super();
		this.name = name;
		this.price = price;
		this.type = type;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}

	@Override
	public String toString() {
		return "Food [name=" + name + ", price=" + price + ", type=" + type + "]";
	}
}
           

web.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


           
<!--spring容器通過java代碼中的預設構造器(上1),在容器中建立一個執行個體bean(反射機制實作) 。 -->
<bean id="food" class="com.yxj.spring.entity.Food"/>

<!--通過自定義構造器參數(上2),執行個體化一個bean。注意!此處屬性的順序要和自定義構造函數的參數的一樣,否則會報錯。 此處寫法表示已經給對象初始化了 -->
<bean id="food22" class="com.yxj.spring.entity.Food">
			<constructor-arg value="巧克力"></constructor-arg>
			<constructor-arg value="120"></constructor-arg>
			<constructor-arg value="sweet"></constructor-arg>
</bean>
</beans>
           

2和3,通過工廠方式建立bean就不作多說明,想要了解的請自行百度。

上面隻是注入一個簡單的java對象,對于對象需要依賴另一個對象的情景,則需要用到依賴注入來實作另一個對象注入到對象中。比如,User這個對象,依賴食物Food這個對象,怎樣把食物對象注入到人對象中呢?----3種方式。1,通過setter方法注入 ;2,通過構造器注入 3.基于注解的注入

package com.yxj.spring.entity;

public class User {
	private String name;
	private Food food;
	
	public User() {
		super();
	}

	public User(String name, Food food) {
		super();
		this.name = name;
		this.food = food;
	}
           
public Food getFood() {
		return food;
	}


           
//通過setter方法,将Food這個對象注入進來
	public void setFood(Food food) {
		this.food = food;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", food=" + food + "]";
	}
}
           

Food類就是上面的。

xml配置。1.通過setter方法注入。通過在類中寫依賴類的set方法。在xml配置中通過ref表示兩個對象間的關聯關系

<bean id="food" class="com.yxj.spring.entity.Food"/>  //要把food對象動态注入到User對象中,需在容器中建立一個food對象

<bean id="user" class="com.yxj.spring.entity.User">   //在容器中建立一個user對象
 	<!--通過property元素來表示對象的屬性。-->
	<property name="name" value="這是name屬性的值"></property>
	<!--兩個對象間的關聯,由ref指定 -->
	<property name="food" ref="food"></property>
</bean>
           

               2.通過構造器注入依賴對象,java代碼中就可以不用寫food的setter方法,加入相應的構造器即可。

<bean id="user" class="com.yxj.spring.entity.User">
	<constructor-arg value="Jack"></constructor-arg>	
        <constructor-arg ref="food" ></constructor-arg>
</bean>
           

3.注解方式注入。從spring2.5開始支援,現在很流行這種方式,注解使整個代碼量減少了。主要由@Autowired   @Resource  @Inject  

在講注解方式注入前,先說一下什麼是自動裝配(autowire)。自動裝配是指,Spring 在裝配 Bean 的時候,根據指定的自動裝配規則,将某個 Bean 所需要引用類型的 Bean 注入進來。注意,目前隻支援自動裝配一個依賴對象,對于基本的屬性類型如string,int等不支援。<bean> 元素提供了一個指定自動裝配類型的 autowire 屬性。autowire由幾個屬性,byName,byType等,之間差別就不詳說了。主要是說autowire的功能。

對于上面的例子,可以簡化為:

<bean id="food" class="com.yxj.spring.entity.Food"/> 
           
<bean id="user" class="com.yxj.spring.entity.User" autowire="byName"/> //使用autowire屬性,則不用再配對象的依賴對象,當然對于基本屬性還是可以繼續配的
           
使用@Autowired @Resource @Inject等注解實作自動注入---替代setter方法或是構造器,用于類中依賴對象的自動注入。     3.1 @Autowired 注解進行自動裝配,隻能是根據類型進行比對。@Autowired 注解可以用于 Setter 方法、構造函數、字段,甚至普通方法,前提是方法必須有至少一個參數。@Autowired 可以用于數組和使用泛型的集合類型。然後 Spring 會将容器中所有類型符合的 Bean 注入進來。 xml中:
<bean id="food" class="com.yxj.spring.entity.Food" />
<bean id="user" class="com.yxj.spring.entity.User"/>
           
java代碼中:
public class User {
	private String name;
	
	@Autowired
	private Food food;   //使用@Autowired注解,不用寫Food對象的set方法.則xml中配置user對象時也不用寫            
//<property name="food" ref="food">來進行裝配
           
public User() {
		super();
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", food=" + food + "]";
	}
}
           
如果food類沒有在容器中注冊,那麼會報錯。解決這一問題,隻需這樣寫@Autowired(required=false),這樣不會報錯,隻是表示這個依賴對象為null。 如果在xml檔案中出現多個類型相同的bean,比如注冊了food1,food2等,則在使用@Autowired時也會出錯,因為@Autowired是根據類型比對的,當出現多個類型相同的bean時,容器會不知道比對哪個bean。解決方案,用@Qualifier注解指明裝配哪個bean。   3.2  @Resource 是根據bean的name名字進行自動裝配的,而不是像@Autowired和@Qualifier結合根據類型裝配。可以作用于帶一個參數的 Setter 方法、字段,以及帶一個參數的普通方法上。@Resource會先根據bean的名字裝配,如果沒有符合的bean則退居根據類型裝配。   3.3  @Inject注解幾乎可以完全替代Spring的@Autowired注解。是以除了使用Spring特定的@Autowired注解,我們可以選擇@Inject。和@Autowired一樣,@Inject可以用來自動裝配屬性、方法和構造器;與@Autowired不同的是,@Inject沒有required屬性。是以@Inject注解注入的依賴關系必須存在,否則報異常。 上面所說的@Autowired   @Resource  @Inject  都是用于實作對象中依賴對象的裝配。而對于一個類的自動裝配,可以用下面的注解。 @Service @Component @Controller @Repository,用他們放在類上,就不用在xml檔案中配寫這個類的<bean />了。容器會掃描到後自動注冊進容器。(用于類的自動注入)
package com.yxj.spring.entity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component   //使用@Component注解後,就不用在xml中寫<bean  />了,會自動把這個類注入到容器中
public class User {
	private String name;
	
	@Autowired
	private Food food;
	
	public User() {
		super();
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", food=" + food + "]";
	}
}
           
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<context:component-scan base-package="com.yxj.spring.entity"></context:component-scan>  
</beans>
           
還有其他功能的注解,後面會繼續補充。