Spring(一)-Spring IoC和DI容器

1.Spring簡介

Spring是J2EE開發(fā)中一個很重要的框架。它主要用來解決下面兩個問題。

  • 解決大型軟件開發(fā)中,對象之間由于復(fù)雜的依賴關(guān)系導(dǎo)致的牽一發(fā)而動全身的強耦合問題.使用IoC思想解決。
  • 使用面向切面編程, 解決控制事務(wù)的繁瑣操作.使用AOP解決。

根據(jù)Spring解決的問題,和其對應(yīng)的專業(yè)術(shù)語。我們也說Spring是一個輕量級的DI/IoCAOP容器開源框架, 其提倡最小侵入式管理應(yīng)用中的代碼,意味著我們可以隨時卸載和安裝Spring.

接下去我們需要理解下Spring以下的專業(yè)術(shù)語,我就不抄寫網(wǎng)絡(luò)上的術(shù)語內(nèi)容,并不是那么通俗,以我的理解如下。

1.2 Spring術(shù)語

  • 應(yīng)用程序(Application).
    • 對于JavaWeb來將,應(yīng)用程序就是一個Web App.例如OA系統(tǒng)等.
  • 框架:
    • 框架是一種抽象, 其抽取了開發(fā)中可以重用和常見的功能的集合。
  • 非侵入式設(shè)計:
    • 非侵入式是編程中常見的一種設(shè)計方式,它保障了別人用你的框架,即使到時候不用了,也不會應(yīng)用刪除你的框架而要更改大量的代碼, 可以不用的時候就直接刪除,不會帶來依賴問題。
    • 從代碼的角度來說.非侵入式,保證了不用繼承框架的類.也就不會和依賴于框架的類.是一種解耦的設(shè)計、
  • 輕量級和重量級
    • 所謂的輕量級是相對于重量級來說。一般是由以下幾個特點.非侵入式、資源占用少、部署簡單、易用.所以所謂的輕是在有重的前提下的對比。
  • POJO:
    • Plain Ordinary Java Object / Pure Old Java Object.所謂的POJO是不繼承Java的任何,不實現(xiàn)任何的接口,但可以包含業(yè)務(wù)邏輯持久化邏輯
  • 容器:
    • 容器主要用來存放對象.管理對象的整個生命周期.比如Tomcat是Servlet/JSP容器.

1.3 Spring的框架構(gòu)架

之所以要了解Spring構(gòu)架是在學(xué)習(xí)之前我們要對一樣事務(wù)大體的骨架有一個了解。在介紹Spring的構(gòu)架之前,我們簡要的說下Spring的優(yōu)勢。

Spring的優(yōu)勢

  • 低侵入、低耦合
  • 聲明式事務(wù)管理
  • 方便集成其他框架
  • Spring框架包含JavaEE 三層的每一層的解決方案(一站式)

構(gòu)架

從上之下。主要分為

  • 數(shù)據(jù)訪問層(Data Access)
  • Web層。
  • AOP模塊
  • 核心容器層(Core Coniainer)。Beans、Core、Context、SpEL
  • 測試層(Test)。

Spring框架版本

這里簡要介紹下Spring各個版本的變化。其中Spring2.5是變化最大的一個版本,其已經(jīng)完成了大部分Spring的核心功能。而后續(xù)版本都是在其上增加新的語法支持等。

  • Spring2.5: 驅(qū)動編程、支持SimpleJdbcTemplate的命名參數(shù)操作
  • Spring3.x: 全面支持泛型不支持JDK 1.4。支持SpEL、支持WebService的OXM
  • Spring4.x: 支持Java8支持JavaEE6規(guī)范、泛型限定式依賴注入、對Hiberante4的集成事務(wù)管理提供更好的管理方案.

介紹完Spring的一些基礎(chǔ)常識,我們看是以Spring來寫一個最簡單的HelloWorld,學(xué)習(xí)基于Spring搭建應(yīng)用的基本步驟。

2. 基于Spring如何編寫程序

關(guān)于Spring插件

如果不是用Spring官方提供的Eclipse。需要自己安裝SIS插件.

2.1 基于Spring的HelloWorld編寫步驟

  • 導(dǎo)入jar包。并build到classpath。
    • spring-beans-4.0.0.RELEASE.jar
    • spring-core-4.0.0.RELEASE.jar
    • commons-logging-1.1.3.jar
  • 將我們的控制權(quán)轉(zhuǎn)交給Spring去控制
    • 創(chuàng)建bean的xml配置文件。
    • 注冊bean到容器, 進行屬性注入。注冊HelloWorld到IoC容器中。
  • 啟動IoC容器, 根據(jù)Bean的idBean的類型, 從Ioc容器中取出對應(yīng)的bean實例.bean的獲取方式又有下面三種.一般我們使用最后一種,因為最后這種既能獲取到bean,又無需強轉(zhuǎn).
    • 根據(jù)ID或者name獲取bean.ioc.getBean(String)
    • 根據(jù)類型獲取bean.ioc.getBean(Class)
    • 根據(jù)ID和類型獲取bean.ioc.getBean(String, Class)

目錄結(jié)構(gòu)

測試代碼

public class HelloWorldTest {
    
    @Test
    public void testHelloWorld() {
        
            Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
            BeanFactory beanFactory = new XmlBeanFactory(resource);
            HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
            helloWorld.say();
    }
}

輸出

可以看到我們使用IoC容器成功的獲取到對應(yīng)的Bean對象,并調(diào)用其say方法,輸出Hello Spring.

2.2 Spring基本配置

使用Spring編寫完第一個HelloWorld演示程序,接下來我細致的分享下Spring基本的配置方式

2.2.1 bean的id和name

在Spring的配置文件中, 也就是配置bean的文件中.我們需要給Bean配置屬性。其中比較常用,用來定位Bean的用兩種方式,一種是id一種是name.

id方式
<?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">
    <bean id="helloWorld" class="com.sweetcs._01helloworld.HelloWorld"></bean>
</beans>

測試代碼

    @Test
    public void testHelloWorldByID() {
        
            Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
            BeanFactory beanFactory = new XmlBeanFactory(resource);
            HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
            helloWorld.say();
    }

ID方式輸出

name方式
<?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">
    
<!--    <bean id="helloWorld" class="com.sweetcs._01helloworld.HelloWorld"></bean> -->
    <bean name="helloWorld,helloWorld2,helloWorld3" class="com.sweetcs._01helloworld.HelloWorld"></bean>
</beans>

測試代碼

    @Test
    public void testHelloWorldByName() {
        
            Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
            BeanFactory beanFactory = new XmlBeanFactory(resource);
            HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
            HelloWorld helloWorld2 = beanFactory.getBean("helloWorld2", HelloWorld.class);
            HelloWorld helloWorld3 = beanFactory.getBean("helloWorld3", HelloWorld.class);
            
            System.out.println(helloWorld == helloWorld2);
            System.out.println(helloWorld2 == helloWorld3);
    }

輸出

ID和name的區(qū)別
  • 如果是ID方式,一個bean只能配置一個id.并且多個bean的ID值不能重復(fù).如果是Name方式, 一個bean能配置多個別名.

2.3 模塊化xml配置文件

如果我們項目中有很多的bean需要注冊, 那么這時候如果還只是用一個bean的配置文件,那這個文件配置的bean會爆炸性的增長, 這帶來的后果就是十分的難以維護這個配置文件。這個時候我們可以將xml進行拆分, 拆分多個xml文件。一般項目都是按模塊劃分的,我們可以每個模塊下都配置一個xml文件。最后再通過一個統(tǒng)一xml文件,將這些xml文件引入。

引入其他XML配置文件

import元素

使用import元素我們可以將其他的xml配置文件導(dǎo)入。其使用resource屬性代表要導(dǎo)入的文件路徑,并且提供了classpath:file:協(xié)議分別表示從classpath路徑尋找和從文件系統(tǒng)的路徑尋找.

<?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">

    <!-- 導(dǎo)入_01helloworld包下的配置文件 -->
    <import resource="classpath:com/sweetcs/_01helloworld/hello.xml"/>

</beans>

對應(yīng)的測試類代碼
注解的意思可以先忽略,看完1.6就明白

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class ApplicationContextTest {
    
    @Autowired
    BeanFactory ioc;
    
    @Test
    public void testHelloSpring() {
        HelloWorld bean = ioc.getBean("helloWorld", HelloWorld.class);
        bean.say();
    }   
}

2.4 Spring中的測試

問題:在編寫Spring的測試用例中, 我們直接使用Spring提供的Test。因為該Test是包含在容器中, 我們不用每一次都創(chuàng)建容器,關(guān)閉容器, 這對系統(tǒng)開銷十分大。

2.4.1 測試步驟

  • 導(dǎo)入依賴jar包
    • junit4。需要兩個個jar包junithamcrest-core.jar
    • spring。需要三個jar包spring-test、spring-aopspring-expression
  • 使用Junit提供的注解
    @RunWith: 用于指定junit運行環(huán)境,是junit提供給其他框架測試環(huán)境接口擴展.
    @ContextConfiguration: 用于指定容器上下文配置文件。該文件用于指定容器創(chuàng)建的時候需要生成哪些bean對象。
  • 編寫測試類
// 告訴JVM,Spring測試運行于JVM上.而不是讓Junit運行于JVM上.
@RunWith(SpringJUnit4ClassRunner.class)
// 告訴Spring去哪里尋找配置文件.
@ContextConfiguration("classpath:com/sweetcs/_02springtest/HellWoldTest.xml")
public class HelloWorldTest {
    // 自動裝配IoC容器
    @Autowired
    BeanFactory ioc;
    
    @Test
    public void testHelloSpring() {
        
        HelloWorld helloWorld =  ioc.getBean("helloWorld", HelloWorld.class);
        helloWorld.say();
    }
}

配置文件命名的小技巧

  • 測試類的配置文件名可以命名為所在的類名-context.放在和測試類一個目錄
    原理

@ContextConfiguration也可以不用指定配置文件路徑, 如果不指定,其默認配置文件所在的類文件同一級目錄下,且該配置文件名類名-context.xml.對于上述程序可以的配置文件名就可以改成HellWoroldTest-context

  • 務(wù)必指定classpath:.指定該前綴能讓其一定去classpath目錄尋找.

上述主要講了

  • 如何像IoC容器轉(zhuǎn)交控制權(quán),讓IoC容器替我們管理這些對象.而不是我們手動去管理。
  • 如何編寫Spring中的測試。

接下來我們要比較系統(tǒng)的介紹下IoC容器的內(nèi)容。

3.IoC

IoC容器, 即Inversion Of Control, 中文翻譯就是反轉(zhuǎn)控制。IoC容器反轉(zhuǎn)了哪些控制呢?其實IoC是一種思想,這種思想被引入軟件工程學(xué)中, 主要是為了解決大型軟件項目中的耦合過深問題。

3.1 思想

傳統(tǒng)的思維

傳統(tǒng)的思維下,我們需要哪個對象就去new一個。比如下面的代碼。

public class EmployeeService {
    private IEmployeeDAO employeeDAO = new EmployeeDAO();
}

這中代碼本省寫得并沒有錯,但是隨著軟件復(fù)雜度的增加, 你可能導(dǎo)出都存在這種代碼,對于EmployeeService來說它就依賴EmployeeDAO.如果采用這種方式,可想而知,項目中會存在很多復(fù)雜的依賴,而且這些依賴有的又是重復(fù)引用的.這會導(dǎo)致,其中一個依賴要是存了問題,整個軟件系統(tǒng)就無法正常的運行。
如下圖,是我在網(wǎng)絡(luò)上找的一張圖,傳統(tǒng)軟件開發(fā)思維如下。導(dǎo)致的問題可想而知。齒輪之間相互耦合太強,牽一發(fā)而動全身。

IoC反轉(zhuǎn)控制思想

傳統(tǒng)的軟件設(shè)計是我們需要什么對象需要我們自己去創(chuàng)造,如上分析這隨著軟件復(fù)雜度增加,對于系統(tǒng)的維護和變更是十分困難的,因為存在太多耦合。
IoC的思想
IoC的思想是能不能把這種主動的控制權(quán)轉(zhuǎn)交出來,當(dāng)我們需要某個對象的時候才讓"第三方"幫我們注入我們需要的對象。這就解除了類之間的耦合關(guān)系,類于類之前的聯(lián)系需要通過這個中間容器即IoC容器來橋接。

如下圖, 只要遵守這個"第三方"的規(guī)則,所有的齒輪都能夠轉(zhuǎn)動。這個第三方就相當(dāng)于一個粘合劑,將所有齒輪"粘合"起來,保證整個系統(tǒng)的運轉(zhuǎn)。

這種控制權(quán)顛倒過來的設(shè)計, 我們就是把它稱為“控制反轉(zhuǎn)”。通過這種設(shè)計,我們可以在Spring中很方便的擴充功能,而且還能讓類之間解除耦合。

說了那么多我們先來看下Spring中IoC容器容器如何創(chuàng)建BeanBean如何實例化、Bean的作用域已經(jīng)Bean的初始化和銷毀方法。

3.2 IoC容器

獲取IoC容器的方式有兩種。一種是BeanFactory提供了最基本的IoC功能。一種是ApplicatioinContext, 基于BeanFactory擴展了更強的功能.

  • 使用實現(xiàn)BeanFacory接口的實現(xiàn)類

    BeanFacory是Spring底層的接口,只提供了簡單的IoC功能, 負責(zé)配置、創(chuàng)建、管理bean。應(yīng)用中一般不實用BeanFactory, 而推薦使用ApplicationContext。

  • 使用實現(xiàn)ApplicationContext接口的實現(xiàn)類

    一般開發(fā)中都使用ApplicationContext來代表容器.ApplicationContext是BeanFactory子接口,除了繼承IoC基本功能,其還提供了更多的功能。

    • 消息機制
    • 國際化
    • 統(tǒng)一的資源加載
    • AOP功能

3.2.1 基于ApplicationContext創(chuàng)建IoC容器(Spring容器)

基于BeanFactory的方式在HelloWorld中已經(jīng)介紹過, 我們就簡單介紹下,基于ApplicatioinContext創(chuàng)建Spring容器的方式。由于ApplicationContext也是一個接口,所以我們只能找其實現(xiàn)類。

通過查看類繼承結(jié)構(gòu)如下圖,可以發(fā)現(xiàn)有如下兩個類。

3.3 Bean創(chuàng)建時機研究

ApplicatioinContext和BeanFactory對bean的創(chuàng)建時機并不是相同的,我們來研究下他們有什么區(qū)別。之所以以下代碼不采用Spring提供的Test來自動裝配IoC容器,是因為如果使用SpringTest,它會自動加載所有Bean.就看不出ApplicationContext創(chuàng)建Bean的時機和BeanFactory的區(qū)別。

以下代碼省略了轉(zhuǎn)交控制權(quán)的步驟,需要配置xml,可以參考上面的配置步驟。

3.3.1 基于BeanFactory的Bean創(chuàng)建時機研究

  • testCreateWithBeanFactory用例,是創(chuàng)建IoC容器,并獲取Bean
  • testCreateWithBeanFactory2用例, 是只創(chuàng)建IoC容器
    @Test
    public void testCreateWithBeanFactory() {
        Resource resource = new ClassPathResource("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
        BeanFactory ioc = new XmlBeanFactory(resource);
        User user = ioc.getBean("user", User.class);
        System.out.println(user);
        /*  輸出
            User 構(gòu)造器被調(diào)用,正在初始化User對象
            User [name=SweetCS]
         */
    }
    
    @Test
    public void testCreateWithBeanFactory2() {
        Resource resource = new ClassPathResource("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
        BeanFactory ioc = new XmlBeanFactory(resource);
        /*
         * 無任何輸出
         */
    }

使用BeanFacory方式,Bean只會在需要的時候才創(chuàng)建

3.3.2 基于ApplicationContext的Bean創(chuàng)建時機研究

  • testCreateWithApplicationContext用例,是創(chuàng)建IoC容器,并獲取Bean
  • testCreateWithApplicationContext2用例, 是只創(chuàng)建IoC容器
    
    @Test
    public void testCreateWithApplicationContext() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
        User bean = ctx.getBean("user", User.class);
        System.out.println(bean);
        /*  輸出
            User 構(gòu)造器被調(diào)用,正在初始化User對象
            User [name=SweetCS]

         */
    }
    
    @Test
    public void testCreateWithApplicationContext2() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
        // 輸出 User 構(gòu)造器被調(diào)用,正在初始化User對象
    }   

運行程序后使用ApplicationContext的方式,無論有沒有取獲取Bean,Bean都會在容器啟動的時候被創(chuàng)建。

BeanFactory和ApplicationContext的應(yīng)用場景

  • ApplicatioinContext會一次性將所有Bean的創(chuàng)建出來,主要是為了優(yōu)化服務(wù)端的性能,主用應(yīng)用于Web后端開發(fā)
  • BeanFacory只會在需要使用Bean的時候在創(chuàng)建, 主要用在客戶端開發(fā)。因為客戶端直接面向用戶,關(guān)注用戶體驗。不能再一啟動創(chuàng)建大量對象,讓應(yīng)用卡頓半天。

3.3.3 能否配置延遲創(chuàng)建

其實這個需求,一般不回用到。對于我們做后端開發(fā)的來說, 最常用的是ApplicationContext, 如果要讓其延遲加載Bean也是可以。有兩種配置方式。

  • 全局配置,對所有Bean都生效。<beans default-lazy-init="true"> </beans>
  • 局部配置,指對指定Bean生效。<bean lazy-init="true"></bean>
    任選一種做測試,會發(fā)現(xiàn)在ApplicationContext下的Bean,如果沒獲取也不會創(chuàng)建.

3.4 Bean的實例化方式

之所以要聊Bean的實例化方式,是因為我們可能會在不同的情況下需要使用不同的方式,讓Spring幫我們注入對象。所以我們需要來研究下讓Spring幫我們注入對象的四種方式, 其對應(yīng)的就是Bean的實例化方式。主要有以下四種方式

通過無參構(gòu)造器方式,最標(biāo)準(zhǔn), 使用最多(重點)

轉(zhuǎn)交控制權(quán)

<?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">

<!-- 1.最標(biāo)準(zhǔn)的方式,通過無參夠照器方式創(chuàng)建 -->
    <bean id="user" class="com.sweetcs._04beaninstance.construtor.User"></bean>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class BeanInstanceTest {
    
    @Autowired
    ApplicationContext ctx;
    
    @Test
    public void testInstanceBeanWithNoArgConstrutor() {
        User bean = ctx.getBean("user", User.class);
        System.out.println(bean);
    }   
}

輸出

通過靜態(tài)工廠實例化方式

其主要解決一些系統(tǒng)留下來的問題。例如有可能你接觸的項目并不是通過最標(biāo)準(zhǔn)的創(chuàng)建Bean的方式創(chuàng)建,它可能都是使用靜態(tài)工程創(chuàng)建,那我們就應(yīng)該在接入Spring的時候,為了兼容性,使用靜態(tài)工廠實例化方式。
SomeBean2StaticFactory
配置bean,id, class ,factory-method

    
    @Test
    public void testInstanceBeanWithStaticFactory() {
        SomeBean someBean = ctx.getBean("someBean", SomeBean.class);
        System.out.println(someBean);
    }

輸出

通過實例工廠實例化方式

SomeBean2

package com.sweetcs._04beaninstance.instance_factory;
public class SomeBean2 {

}

SomeBean2Factory

package com.sweetcs._04beaninstance.instance_factory;
public class SomeBean2Factory {
    public SomeBean2 getSomeBean2() {
        return new SomeBean2();
    }
}

轉(zhuǎn)交控制權(quán)

    <!-- 3.通過實例工廠實例化 -->
    <bean id="someBean2Factory" class="com.sweetcs._04beaninstance.instance_factory.SomeBean2Factory"></bean>
    <bean id="someBean2" factory-bean="someBean2Factory" factory-method="getSomeBean2"></bean>

測試用例

    @Test
    public void testInstanceBeanWithInstanceFactory() {
        SomeBean2 someBean2 = ctx.getBean("someBean2",SomeBean2.class);
        System.out.println(someBean2);
    }

需要new出這個工廠
先要轉(zhuǎn)交實例工廠對象創(chuàng)建權(quán)
再要轉(zhuǎn)交Bean對象創(chuàng)建權(quán)

實現(xiàn)FacotryBean接口實例化。實例工廠的變種,主要為了集成其他框架。

如果發(fā)現(xiàn)了這個對象是實現(xiàn)這個接口,Spring容器會自動條用getObject方法返回創(chuàng)建的對象。

package com.sweetcs._04beaninstance.facotry_bean;

import org.springframework.beans.factory.FactoryBean;

public class MyBean {
}

package com.sweetcs._04beaninstance.facotry_bean;

import org.springframework.beans.factory.FactoryBean;

public class MyBeanFactory implements FactoryBean<MyBean>{

    @Override
    public MyBean getObject() throws Exception {
        // TODO Auto-generated method stub
        return new MyBean();
    }

    @Override
    public Class<?> getObjectType() {
        // TODO Auto-generated method stub
        return MyBean.class;
    }

    @Override
    public boolean isSingleton() {
        // TODO Auto-generated method stub
        return false;
    }
    
}

測試用例

    @Test
    public void testInstanceBeanWithImplementsFactoryBean() {
        MyBean myBean = ctx.getBean("myBean", MyBean.class);
        System.out.println(myBean);
    }

輸出

3.5 Bean的作用域

Bean的作用域研究表示Bean可以存活多久, Bean的標(biāo)簽里有一個scope屬性,要來表示Bean的作用域。這邊只介紹用得最多的兩種方式singleton(單例)和prototype(多例)。

默認方式

<?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">
    
    <bean id="myBean" class="com.sweetcs._05beanscope.MyBean"></bean>
    
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyBeanTest {
    
    @Autowired
    ApplicationContext ctx;
    
    @Test
    public void testDefaultScope() {
        
        MyBean myBean = ctx.getBean("myBean", MyBean.class);
        MyBean myBean2 = ctx.getBean("myBean", MyBean.class);
        MyBean myBean3 = ctx.getBean("myBean", MyBean.class);
        
        System.out.println(myBean + "\n" + myBean2 + "\n" + myBean3);
    }
}

輸出

Singleton方式

更改bean的配置的,添加scope屬性為singleton

    <bean id="myBean" class="com.sweetcs._05beanscope.MyBean" scope="singleton"></bean>

再運行剛才的用例,輸出

prototype方式

更該bean的配置為prototype

    <bean id="myBean" class="com.sweetcs._05beanscope.MyBean" scope="prototype"></bean>

所有作用域的總結(jié)

最常用的是singleton和prototype作用域。其他的可以參考以下

作用域 描述
singleton 該作用域?qū)?bean 的定義的限制在每一個 Spring IoC 容器中的一個單一實例(默認)。
prototype 該作用域?qū)我?bean 的定義限制在任意數(shù)量的對象實例。
request 該作用域?qū)?bean 的定義限制為 HTTP 請求。只在 web-aware Spring ApplicationContext 的上下文中有效。
session 該作用域?qū)?bean 的定義限制為 HTTP 會話。 只在web-aware Spring ApplicationContext的上下文中有效。
global-session 該作用域?qū)?bean 的定義限制為全局 HTTP 會話。只在 web-aware Spring ApplicationContext 的上下文中有效。

3.6 Bean的初始化和銷毀方法

bean標(biāo)簽中提供了init-method屬性和destory-method屬性。分別會在bean創(chuàng)建之前調(diào)用,在bean銷毀之前做收尾操作。

3.6.1 DataSource需要回收

比如DataSource最終都需要關(guān)閉資源, 在Bean銷毀之前,都要調(diào)用close方法。

MyDataSourceBean

package com.sweetcs._06bean_init_destory;

public class MyDataSourceBean {
    
    public void doingSomeWork() {
        
        System.out.println("處理業(yè)務(wù)邏輯");
    
    }
    
    
    public void init() {
        
        System.out.println("初始化配置,準(zhǔn)備連接數(shù)據(jù)庫");
    }
    
    
    public void close() { 
        System.out.println("釋放connection");
    }
    
}

轉(zhuǎn)交控制權(quán)給Spring容器

<?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">
    <bean id="myDataSource" class="com.sweetcs._06bean_init_destory.MyDataSourceBean" 
    init-method="init" destroy-method="close"></bean>
</beans>

MyDataSourceBeanTest

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration
public class MyDataSourceBeanTest {
    
    @Autowired
    ApplicationContext ctx;
    
    @Test
    public void testCreateMyDataSourceBean() {
        MyDataSourceBean dataSource = ctx.getBean("myDataSource", MyDataSourceBean.class);
        dataSource.doingSomeWork();
    }
}

輸出

3.6.2 對于多例無效

如果Bean標(biāo)簽的scope屬性配置成scope="prototype"。則容器不負責(zé)銷毀控制,而且這個bean也不會放入Spring容器里取管理, 而是一創(chuàng)建就交給程序員,讓程序員自己去負責(zé)。

    <bean id="myDataSource" class="com.sweetcs._06bean_init_destory.MyDataSourceBean" 
    init-method="init" destroy-method="close" 
    scope="prototype">
        
    </bean>

4.DI

DI又稱為依賴注入.DI是IoC思想的一種實現(xiàn),在Spring容器中就采用了DI技術(shù)來實現(xiàn)IoC, 即采用DI實現(xiàn)了反轉(zhuǎn)控制。所以我們要來探討下如何注入,注入的幾種方式。記下來我們主要來研究自動裝配屬性注入、構(gòu)造器注入、屬性占位符的使用

4.1 自動裝配

自動裝配Spring中主要提供了兩種方式。如下

  • 基于XML配置的自動裝配.Bean標(biāo)簽中配置 autowire屬性
  • 基于注解配置的自動裝配.@Autowirsed注解

基于XML的自動裝配

我們創(chuàng)建兩個類分別是ABean和CBean,讓ABean依賴于CBean.代碼如下

<?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">
    
    
    <bean id="aBean" class="com.sweetcs._07di.autowise_xml.ABean" autowire="byType"></bean>
    <bean id="cBean" class="com.sweetcs._07di.autowise_xml.CBean"></bean>
</beans>

ABean


public class ABean {
    
    private CBean cBean;
    
    public void setcBean(CBean cBean) {
        this.cBean = cBean;
    }

    @Override
    public String toString() {
        return "ABean [cBean=" + cBean + "]";
    }   
}

CBean

public class CBean {

}

測試代碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AutoWiseWithXMLTest {
    
    @Autowired
    ApplicationContext ctx;
    
    @Test
    public void testAutoWiseWithXML() {
        
        ABean bean = ctx.getBean("aBean", ABean.class);
        System.out.println(bean);
        
    }
}

輸出

如上輸出, 基于XML方式自動裝配.自動將CBean注入到了ABean當(dāng)中。使用的是byType的方式,其實還可以使用byName的方式。下面說下二者區(qū)別

byName和byType的區(qū)別

  • byName是按照屬性名去容器中找id為這個屬性名的對象,如果找不到則拋出異常。
  • byType是會根據(jù)對應(yīng)要注入的字段的類型,去容器里找。如果找到一個匹配類型的對象則直接注入。如果找到多個則拋出異常。

4.2 注入值

4.2.1 setter注入

一般我們不會在xml中使用自動裝配,應(yīng)用這樣很難看出這個bean有哪些屬性。取而代之的是有兩種方法

  • 使用屬性注入方式??梢院芮逦目闯鯾ean的屬性。
  • 使用注解裝配方式。

注入簡單類型-使用字符串

    <bean id="employee" class="com.sweetcs._07di.property.Employee">
        <property name="name" value="SweetCS"></property>
        <property name="url"  value="http://www.baidu.com"></property>
        <property name="age" value="1"></property>
    </bean>

測試用例

public class Employee {
    

    private String name;
    private URL url;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public URL getUrl() {
        return url;
    }
    public void setUrl(URL url) {
        this.url = url;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }   
    

    @Override
    public String toString() {
        return "Employee [name=" + name + ", url=" + url + ", age=" + age + "]";
    }
}
    @Test
    public void testDIWithProperyMethod() {
        
        Employee employee = ctx.getBean("employee", Employee.class);
        System.out.println(employee);
        
    }

注入引用類型

public class EmployeeDAO {
    public void save() {
        System.out.println("開始持久化數(shù)據(jù)到數(shù)據(jù)庫");
    }
}

public class EmployeeService {
    private EmployeeDAO employeeDAO;

    public void setEmployeeDAO(EmployeeDAO employeeDAO) {
        this.employeeDAO = employeeDAO;
    }

    @Override
    public String toString() {
        return "EmployeeDAO [employeeDAO=" + employeeDAO + "]";
    }
    
    public void save() {
        employeeDAO.save();
    }
}

xml配置

    <bean id="employeeService" class="com.sweetcs._07di.property.EmployeeService" autowire="byType"></bean>
    <bean id="employeeDAO" class="com.sweetcs._07di.property.EmployeeDAO"></bean>

注入集合類型


public class CollectionBean {
    
    private Set set;
    private List list;
    private String[] array;
    private Properties properties;
    private Map map;
    
    
    public void setArray(String[] array) {
        this.array = array;
    }


    public void setProperties(Properties p) {
        this.properties =p;
    }
    

    public void setSet(Set set) {
        this.set = set;
    }
    public void setList(List list) {
        this.list = list;
    }

    public void setMap(Map map) {
        this.map = map;
    }
    @Override
    public String toString() {
        return "CollectionBean [set=" + set + ", list=" + list + ", arrayList=" + array + ", map=" + map + "]";
    }
    
}

xml配置

    
    <bean id="collectionBean" class="com.sweetcs._07di.property.CollectionBean">
        <property name="set">
            <set>
                <value>"1"</value>
                <value>"2"</value>
                <value>3</value>
            </set>
        </property>
        
        <property name="list">
            <list>
                <value>"1"</value>
                <value>"2"</value>
                <value>3</value>
            </list>
        </property>
        
        <property name="array">
            <array>
                <value>"1"</value>
                <value>"2"</value>
                <value>"3"</value>
            </array>
        </property>
        
        <property name="properties">
            <value>
                key1=value1
                key2=value2
                key3=value3
            </value>
        </property>
        
        <property name="map">
            <map>
                <entry key="key1" value="value1"></entry>
                <entry key="key2" value="value2"></entry>
                <entry key="key3" value="value3"></entry>
            </map>
        </property>
        
    </bean>

4.2.2 構(gòu)造器注入

構(gòu)造器注入其實和屬性注入差不多, 主要有兩個差別,具體就不演示了。

  • 需要為相應(yīng)的Bean類提供有參的構(gòu)造方法(無參構(gòu)造也要提供)
  • 需要使用constructor-arg標(biāo)簽替換property標(biāo)簽。

4.3 property place holder(配置數(shù)據(jù)庫連接池)

屬性占位符是十分有用的一個功能。開發(fā)中一般我們都將有關(guān)數(shù)據(jù)庫的配置做成一個配置文件,方便和運維人員解耦,運維人員只要拿著配置文件取做部署,無需關(guān)心程序員的代碼實現(xiàn)細節(jié)。

步驟

  • 使用屬性占位符初始化上下文的環(huán)境變量。<context:property-placeholder location="classpath:配置文件位置" />
  • 使用屬性注入方式,配置相關(guān)信息。

XML配置, 轉(zhuǎn)交控制權(quán)

    <context:property-placeholder location="classpath:config.properties"/>
    <bean id="ds" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="initialSize" value="${jdbc.initialSize}"></property>
        <property name="maxActive" value="${jdbc.maxActive}"></property>
        <property name="minIdle" value="${jdbc.minIdle}"></property> 
    </bean>

測試用例

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration
public class DruidCreateTest {
    
    @Autowired
    ApplicationContext ctx;
    
    @Test
    public void testGetConnWithDruid()  {
        
        DruidDataSource ds = ctx.getBean("ds", DruidDataSource.class);
        Connection connection = null;
        try {
            connection = ds.getConnection();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(connection);
    }
}

輸出

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容