IOC容器管理 bean Bean的命名以及實例化方法 Bean的命名 每一個交給Spring IOC(后面統稱Spring容器)容器創建的對象必須被分配至少一個名稱,如果開發者沒有提供,Spring容器將會為其分配一個內部名稱,通過Bean的名稱,我們可以在其他類中查找該類並使用它,如前面的案例,也是通過Bean名稱獲取到實際對象並執行對應的操作。在基於xml的配置信息中,可以使用id屬性來為一個Bean分配名稱,在同一個xml配置文件中,id必須是唯一的,但不同的xml可以相同,當然還可以使用name來為Bean分配名稱,name屬性可以分配多個名稱,此時可使用空格、逗號、分號來分離給定Bean分配多個名稱,而id屬性則無法這樣使用。 <!-- name屬性配置多個名稱 --> <bean name="accountDao,accountDao2" class="com.zejian.spring.springIoc.dao.impl.AccountDaoImpl"/> <!-- id屬性配置唯一名稱而且不能與name相同--> <bean id="accountDaoId" class="com.zejian.spring.springIoc.dao.impl.AccountDaoImpl"/> 在name屬性中聲明了兩個名稱,除了第一個名稱外,其他的名稱都被稱為別名(aliase)。除了在Bean中定義名稱外,還可利用<alias>標簽向Bean賦予別名: <bean id="accountDaoId" class="com.zejian.spring.springIoc.dao.impl.AccountDaoImpl"/> <!-- name屬性指明要給那個Bean賦予別名,alias則指明賦予的別名--> <alias name="accountDaoId" alias="accountDao3" /> 顯然如果我們想要配置的Bean對象已存在,並且希望向一些Bean賦予特別的名稱,此時別名就相當有用了。上述的Bean對象聲明使用都在xml內聲明手動聲明的方式,一旦Bean對象多起來,管理Bean可能會發生繁瑣的情況,為了Spring提供了基於Java注解的配置方式,下面分別使用org.springframework.stereotype.Service(@Service)和org.springframework.stereotype.Repository(@Repository)聲明AccountServiceImpl和AccountDaoImpl類,使用@Autowired注解注入accountDao(需要在xml聲明注解驅動) //@Component 相同效果 @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; } //@Component 相同效果 @Repository public class AccountDaoImpl implements AccountDao{ //...... }
有了注解聲明,我們就不需要在xml中聲明以上兩個Bean,但需要明確告訴Spring注解的Bean在那些包下,因此需要添加包掃描機制,此時需要啟用Spring的context命名空間:
<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.xsd "> <!-- 聲明包掃描 --> <context:component-scan base-package="com.zejian.spring.springIoc" /> </beans>
以上的聲明方式與之前在xml聲明bean的效果相同。這里我們需要明白可以使用@Component注解達到與@Service和@Repository的效果,@Component與@Service的含義並無差異,只不過@Service更能讓我們明白該類為業務類罷了。至於@Repository在表示數據訪問層含義的同時還能夠啟用與Spring數據訪問相關鏈的其他功能(這個在Spring jdbc相關內容時再詳談,此時我們只需明白@Repository與@Component等效即可),同時還可給@Component、@Service和@Repository輸入一個String值的名稱,如果沒有提供名稱,那么默認情況下就是一個簡單的類名(第一個字符小寫)變成Bean名稱。
@Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; } @Repository("accountDao") public class AccountDaoImpl implements AccountDao{ //...... }
因此到這我們也就知道了,Spring的框架中提供了與@Component注解等效的三個注解,@Repository 用於對DAO實現類進行標注,@Service 用於對Service實現類進行標注,@Controller 用於對Controller實現類進行標注(web層控制器),同時也了解了Spring 容器通過xml的bean標簽配置和java注解兩種方式聲明的Bean對象,我們可以單獨使用其中一種也可以兩種混合使用,取決於各自的需求。
Bean的重寫機制
Bean的重寫機制並沒有那么神秘,主要是當不同的xml文件中出現同名id屬性的bean時讀取的優先級問題,同樣簡單看個例子就明白了。定義兩個spring的配置文件並同時加載它們:spring-ioc.xml <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.xsd "> <!-- 默認構造創建,並通過property 注入屬性值 --> <bean id="account" class="com.zejian.spring.springIoc.pojo.Account" > <property name="name" value="I am SpringIOC1" /> <property name="pwd" value="123" /> </bean> </beans> spring-ioc2.xml <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.xsd "> <!-- 默認構造創建,並通過property 注入屬性值 --> <bean id="account" class="com.zejian.spring.springIoc.pojo.Account" > <property name="name" value="I am SpringIOC2" /> <property name="pwd" value="123" /> </bean> </beans>
獲取bean並調用
@Test public void test1() { ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring/spring-ioc.xml","spring/spring-ioc2.xml"); Account account= (Account) applicationContext.getBean("account"); System.out.println("調用結果:"+account.getName()); }

顯然在不同的xml配置文件中使用相同id命名,並聲明相同類型的bean對象時,spring容器會默認加載最后添加的spring-ioc2.xml中account而忽略spring-ioc.xml中的account,也就是說Bean的重寫機制原則是當聲明的bean的名稱一樣時,后者會覆蓋前者。我們還需要明確的一點時,在web應用開發過程中,一般都會將配置進行分層管理,然后通過一個主springApplication.xml來聚合它,在這樣的情況下分層的配置文件屬於springApplication.xml的子文件,在這樣的關系遇到上述的情況一般都子文件的優先級高,因此會加載子文件的bean。如在spring-ioc.xml主文件導入子文件spring-ioc2.xml:
<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.xsd "> <!-- 默認構造創建,並通過property 注入屬性值 --> <bean id="account" class="com.zejian.spring.springIoc.pojo.Account" > <property name="name" value="I am SpringIOC1" /> <property name="pwd" value="123" /> </bean> <!-- 導入的子文件 --> <import resource="spring-ioc2.xml" /> </beans>
運行代碼:
@Test public void test1() { ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring/spring-ioc.xml"); Account account= (Account) applicationContext.getBean("account"); System.out.println("調用結果:"+account.getName()); }
上述代碼會優先加載spring-ioc2.xml中的account而忽略spring-ioc.xml中的account,效果與前面的代碼相同。
Spring中bean的作用域
Spring IOC容器創建一個Bean實例時,可以為Bean指定實例的作用域,作用域包括singleton(單例模式)、prototype(原型模式)、request(HTTP請求)、session(會話)、global-session(全局會話)。
Bean的延長加載
在某些情況下,我們可能希望把bean的創建延遲到使用階段,以免消耗不必要的內存,Spring也非常自願地支持了延遲bean的初始化。因此可以在配置文件中定義bean的延遲加載,這樣Spring容器將會延遲bean的創建直到真正需要時才創建。通常情況下,從一個已創建的bean引用另外一個bean,或者顯示查找一個bean時會觸發bean的創建即使配置了延遲屬性,因此如果Spring容器在啟動時創建了那些設為延長加載的bean實例,不必驚訝,可能那些延遲初始化的bean可能被注入到一個非延遲創建且作用域為singleton的bean。在xml文件中使用bean的lazy-init屬性可以配置改bean是否延遲加載,如果需要配置整個xml文件的bean都延遲加載則使用defualt-lazy-init屬性,請注意lazy-init屬性會覆蓋defualt-lazy-init屬性。
<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.xsd " default-lazy-init="true"> <!--default-lazy-init="true" xml中全部bean延遲加載 --> <!-- lazy-init="false" 表示非延長加載--> <bean name="accountDao" lazy-init="false" class="com.zejian.spring.springIoc.dao.impl.AccountDaoImpl"/> <!-- 聲明accountService對象,交給spring創建 --> <bean name="accountService" class="com.zejian.spring.springIoc.service.impl.AccountServiceImpl"> <!-- 注入accountDao對象,需要set方法--> <property name="accountDao" ref="accountDao"/> </bean> </beans>
默認情況下Spring容器在啟動階段就會創建bean,這個過程被稱為預先bean初始化,這樣是有好處的,可盡可能早發現配置錯誤,如配置文件的出現錯別字或者某些bean還沒有被定義卻被注入等。當然如存在大量bean需要初始化,這可能引起spring容器啟動緩慢,一些特定的bean可能只是某些場合需要而沒必要在spring容器啟動階段就創建,這樣的bean可能是Mybatis的SessionFactory或者Hibernate SessionFactory等,延遲加載它們會讓Spring容器啟動更輕松些,從而也減少沒必要的內存消耗。
<context:component-scan/>與<context:annotation-config/>
前面我們使用@Autowired、@Resource、@Value等自動裝配注解時用<context:annotation-config/>進行注解驅動注冊,從而使注解生效。實際上這樣<context:annotation-config/>一條配置,它的作用是式地向 Spring 容器注冊AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及RequiredAnnotationBeanPostProcessor 這 4 個BeanPostProcessor。注冊這4個 BeanPostProcessor后Spring容器就能夠識別相應的注解了,當然它們也是也可單獨配置的。
假如想使用@ Resource 、@ PostConstruct、@ PreDestroy等注解就可以單獨聲明CommonAnnotationBeanPostProcessor
如果想使用@PersistenceContext注解,聲明PersistenceAnnotationBeanPostProcessor的Bean即可。
如果想使用 @Required的注解,就必須聲明RequiredAnnotationBeanPostProcessor的Bean。
一般來說,這些注解是隨處可見的,如果總是需要一條一條配置自然就非常繁瑣了,於是spring容器非常智能地為我們提供<context:annotation-config/>的簡化配置方式,自動聲明。
對於<context:component-scan/>,前面在使用@Service、@Component、@Controller 、@Repository等注解時,需要在xml配置文件聲明包掃描驅動<context:component-scan/>,它的作用是Spring容器在啟動時會啟動注解驅動去掃描對應包下的bean對象並將創建它們的實例,這樣我們就無法一個個地進行bean配置聲明了,極大簡化了編程代碼。請注意,當spring的xml配置文件出了<context:component-scan/> 后,<context:annotation-config/>就可以退休了,因為<context:component-scan/>已包含了<context:annotation-config/>的功能了。在大部分情況下,都會直接使用<context:component-scan/>進行注解驅動注冊和包掃描功能。