1. 程式人生 > >spring系統學習之控制反轉 ioc

spring系統學習之控制反轉 ioc

  這學期的課程安排,我們可以系統的學習spring框架了。  相比較自己看部落格筆記,系統的在課堂上學習還是很有必要。

  spring是一個開源框架,是為了解決企業應用程式開發複雜性而建立的。 框架的主要優勢之一就是利用其分層架構。 分層架構允許選擇使用哪一個元件,同時為了J2EE 應用程式開發提供整合的框架。

  spring 的核心是 IOC,  Inverse of control, 控制反轉,  和 AOP  , Aspect oriented programming,圍繞切面的程式設計。

  第一步:

    匯入maven包:

       

       spring含有7個邏輯元件,我們有必要對每個元件的作用進行相應的瞭解,因為只有瞭解了每個元件的作用以及它相關的依賴關係,我們在使用時才能左到心裡有數,才算得上“學習”吧。

       我在早些時間對spring的4個包進行了原始碼的遍歷,雖然意義不大,但是糾正了我的一些誤區。  比如  :  spring-beans,提供了很多的功能,但是它並不是spring提出的邏輯元件。  就像默默無聞的科技工作者一下,雖不被人所知,但是發揮著它強有力的作用!   此外,spring-beans 位於spring-aop邏輯元件中。  但是spring-aop即作為了一個邏輯元件的名稱,同時也是一個包。  可謂雙贏。  就像 李彥巨集,馬克扎克伯格之類。  同時spring-context也是被自己遍歷過,所以它裡面的每個類我都應該不是第一次照面。

         先看書本的理論:   spring-context即spring上下文是一個配置檔案,向spring框架提供上下文資訊。  spring上下文包括企業服務,如: JNDI,EJB,電子郵件,國際化,檢驗和排程。 

        理解這個概念的關鍵在於一個詞語:  上下文!   何謂上下文?  

        有道釋義:context: 上下文,語境。     

         理解上下文的含義對於初次接觸的人來說並不輕鬆。   先說自己所接觸過的上下文吧:   在學習javaweb的時候,首次聽說了上下文的概念---->  之後接觸到了spring框架,再次接觸到了 上下文的說法 ------> 直到最後學習了安卓,又接觸到了上下文,並且在裡面摸爬滾打了好一陣。   總算是對上下文有一些感性上的認識。

         就我的理解來看,我們直到一個程式要被執行,它需要經過這樣一些流程:  原始碼(二進位制串,被儲存在硬碟上)--->被載入進記憶體--> 執行。  當然它實際上還經歷了很多複雜的過程如連結資原始檔,記憶體重定位,許可權驗證等等。廣範一點來說,上下文就是整個程式二進位制串在記憶體中的長度所代表的所有內容。  狹義一點的說,它是程式使用者指定的當前程式的某一段記憶體長度。(不一定是連貫的,但是它們在邏輯一般具有連貫關係)。   上下文就是這樣的一種存在,能夠在你當前的環境(程序中)得到你想要得到的。   

         對上下文最簡單的一種實現: public static final Map context = new HashMap();  只要它能滿足: 單例,生命週期跟程序一樣長。  那麼他就是當前程式的上下文了。上下文的本質是記憶體。

          繼續回到練習:當我們匯入改包了之後,它提供給我們的包結構有:

         其中屬於core邏輯元件的是:  core,logging;  屬於aop元件的是:core,beans,aop;  屬於expression元件的是:core,beans,aop,expression,context。  總之,就是如果我們用的產品,都要依賴邏輯元件: spring-core,spring-aop,spring-context。 也就是要包含這裡的依賴。  當然,如果我們是要基於spring來開發產品,那麼依賴可能就會另算。

             ok,那還有個問題,就是我們為什麼必需得用到上下文的時候才能IOC呢?

              通過學習的理論知識我們知道,spring提供容器的介面是: BeanFactory,它位於核心元件: spring-aop下,(spring-beans包下)但是我們都知道,介面不能被我們例項化。  或者說只能以全實現的方式實現一個介面,那樣的話介面存在的意義就不大了。    我對介面的理解就是,它具有發散性,抽閒類具有內聚性,當我們設計的時候,判斷某個邏輯塊將來具有發散的趨勢就用介面,具有內聚的趨勢就用抽象類。   因此,這個BeanFactory不能被我們直接使用,或者不能被我們方便的使用。因此我們需要尋找它的實現類。

             在spring-context中就提供了兩個它的實現:

   但是它實際應該是有三個實現的,還有一個是XmlWebApplicationContext。  它是與web專案緊密結合的,因此它的實現位於:

     這裡順便提一下,web專案本身有一個上下文。  是由伺服器容器提供的,spring-web核心元件提供了相關的api使得這兩個容器在某種程度上能夠共享。  應該是共享而不是拷貝,因為無論從效能方面還是設計方面拷貝都不佔什麼優勢,只是在多執行緒併發上面可能有一點好處。

    嗯嗯 ,它的三個實現類我知道的都寫在上面了。  但是去看Beanfactory的實現結構的時候,發現還是有一些非抽象類也實現了它,或許也可以用吧。  等以後去探究探究。

 ----------------------

   到目前為止,我們已經實現了環境的準備。下面就開始使用吧。

   先看ClassPathXmlApplicationContext的用法:

  它可以通過指定一個父類上下文(相當於父類上下文的一個子類,類似於拷貝吧),單個配置檔案,多個配置檔案,以及單個配置檔案的路徑或者多個配置檔案的路徑。  嗯嗯算是所有的使用方式了呢!

    再看看FileSystemXmlApplicationContext的用法:

     它能夠通過父類上下文,單個配置檔案,多個配置檔案來進行配置。  它們的使用還是大同小異的。

     我本次練習使用的是ClassPath的實現。

   先定義好一個實體類:

package com.automannn.practice.entity;

/**
 * @author [email protected]
 * @time 2018/9/19 9:44
 */
public class User {

    private Long id;
    private String username;

    public User() {
    }

    public User(Long id, String username) {
        this.id = id;
        this.username = username;
    }
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                '}';
    }
}

    然後建一個配置檔案:配置檔案的名稱隨意。  但是必須引入shema的xml約束。  寫法再spirng.io可以查到。 但是其實不寫這個shema應該也是可以,只是sping官方強制要我們寫,不然報錯。  因為這樣可以提高命中率,不會由於你的xml結果的問題而導致程式異常退出。  因為遊戲規則是別人制定的,並且遵守規則對大家都有好處,因此我們按照它的shema來就是。

<?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="user" class="com.automannn.practice.entity.User" />
    
</beans>

最後就是使用了,最簡單的就是新建一個還有main入口方法的函式:

package com.automannn.practice;


import com.automannn.practice.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


/**
 * @author [email protected]
 * @time 2018/9/19 9:48
 */
public class Main {
    public static void main(String[] args) {
        ApplicationContext context=null;
        //推薦使用
        context= new ClassPathXmlApplicationContext("beans.xml");
        //String[] configs = {"beans1.xml","beans2.xml","beans3.xml"};
        //context = new ClassPathXmlApplicationContext(configs);

        //推薦用於 使用spring 框架的獨立的應用程式種
        //context = new FileSystemXmlApplicationContext("配置檔案的全路徑");
        //String[] configss = {"path1","path2","path3"};
        //context = new FileSystemXmlApplicationContext(configss);

        //spring-web 提供的一個用於 web工程量身定製的一個實現類
        //該靜態方法可支援 在jsp 和servlet  種 根據 ServletContext 取得 IOC容器的引用
        //context = WebApplicationContextUtils.getRequiredWebApplicationContext(null);

        User u = (User) context.getBean("user");
        System.out.println(u.toString());

        

    }
}

最後一步,就是將程式啟動起來了:

   通過截圖,我們發現雖然列印了出來我們要的資訊,但是這幾乎不是我們要的結果。   因為我們用的時候都是:   new User("屬性1","屬性2");  或者:User u=  new User();  u.setShuxing1("屬性1");  u.setShuxing2("屬性2");  因此我們有必要了解bean 容器配置檔案的相關用法。

  一,bean工廠獲得例項的方法:

          1,   使用構造器的方法:

                    

           2,使用靜態工廠例項化的方法:

package com.automannn.practice.entity;

/**
 * @author [email protected]
 * @time 2018/9/19 10:19
 */
public class UserFactory {
    private static final User user =new User(1L,"小二郎");

    private UserFactory(){}

    public static User getInstance(){
        return user;
    }
    
}

   

      3,使用例項工廠例項化的方法:

package com.automannn.practice.entity;

/**
 * @author [email protected]
 * @time 2018/9/19 10:19
 */
public class UserFactory {
    private static final User user =new User(1L,"小二郎");

    private UserFactory(){}

    public static User getInstance(){
        return user;
    }

    public User getUser(){
        return  new User(2L,"李四");
    }
}

      

二,依賴注入的配置方法:

        1,property的方式:

                  

          2,有參構造方法的方式:

   當引數型別衝突的時候,需將二者結合使用。

         3.autowire,自動裝配的方式:

package com.automannn.practice.entity;

/**
 * @author [email protected]
 * @time 2018/10/9 12:42
 */
public class UserComponent {
    User user;

    public User getUser() {
        return user;
    }

    @Override
    public String toString() {
        return user.toString();
    }
}

   預設的自動注入的方式是按型別。  因此當我們有多個介面的實現類並且以介面的方式注入的話,就不能用@Autowire,而用@Resource(name="").

   當然這裡說到了註解,那麼註解又是spring框架提供給我們的另一個強大功能!也就是基於註解的spring容器。

基於註解的spring容器的學習:

     在學習之前我們有必要回顧一些歷史:

           java的註解與1.5引入,時間在2004年前後,同時,java1.5之後,Java就成了一門成熟的開發語言,算是正式在語言市場確定自己江湖霸主的地位。雖不能算上武林盟主,但怎麼也能算的是華山派啊,少林派啊這等有頭有臉的大門派了。

           也就是04年左右,spring框架勢如破竹,在江湖上展露頭角,並且頗受大宗族的喜愛。一路勢如破竹,遂成為現今武林世界不容小覷的一股力量!  當然14年左右springboot也展露了頭角,它的未來必定又是一方雄霸。

           因此,spring也好,springboot也好,它們的發展都是與java的脈絡發展息息相關的。  同時我們有必要知道,註解並不是spring所帶來的功能,而是Java自身的一個功能。 理解這點對我們學習很有必要!

     下面就正式進入註解的學習:

        為什麼使用註解?  因為它很好的滿足了我們程式開發的兩個要求:高內聚,低耦合。就是說,我們希望開發的時候,與一個邏輯相關的所有程式碼都儘量的放在一起,越緊密越方便自己找到,就能更輕鬆的實現業務邏輯;但是對於邏輯聯絡不強烈,或者說將來有很大可能性要修改的邏輯塊,我希望它們就最好一點關係都沒有,將來修改的時候不會導致牽一髮而動全身!

        註解本身不給程式改變,它只是提供了一種新型的方式程式設計。   就spring的ioc註解而言,它的邏輯任然是一樣的,它主要就是以註解去代替了xml配置檔案!   注意是代替,所以它們的邏輯功能是一毛一樣的。 因為當配置檔案大起來,多起來的時候,配合複雜的業務邏輯,足夠讓我們感到煩躁了!

        bean容器的註解,實際上就一個: @Component

        但是它派生了一些:@Controller,@Service,@Repository

        自動裝配的註解:  @Autowire;  此外要注意,自動裝配的方式是通過setter訪問器寫入的,而非使用直接使用反射的方式去設定屬性。  大概是因為開銷的原因吧。  因此,當我們用@Resource(name="")的時候,即可以寫在屬性上面(前提是必須是非靜態屬性,並且有標準的setter訪問器),也可以寫在setter訪問器上的原因。

        使用註解的步驟: 

             spring1.x,需要我們手動的開啟註解的功能: 

<context:annotation-config/>

        配置掃描的包路徑:

 <context:component-scan base-package="com.automannn.practice"/>

   如果是spirng2.x及以後的版本,則只需要指定掃描的包路徑就可以了。

----最後總結一下,spring容器的使用:

一,基於配置檔案

        1,引入spirng-context的依賴

        2,定義一個實體

        3,新建一個配置檔案

        4,使用spring容器

二,基於註解:

         1,開啟註解掃描的功能

         2,指定指定掃描的路徑

         3,使用