天天看點

spring系統學習之控制反轉 ioc

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

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

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

  第一步:

    導入maven包:

spring系統學習之控制反轉 ioc

       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();  隻要它能滿足: 單例,生命周期跟程序一樣長。  那麼他就是目前程式的上下文了。上下文的本質是記憶體。

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

spring系統學習之控制反轉 ioc

         其中屬于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中就提供了兩個它的實作:

spring系統學習之控制反轉 ioc
spring系統學習之控制反轉 ioc

   但是它實際應該是有三個實作的,還有一個是XmlWebApplicationContext。  它是與web項目緊密結合的,是以它的實作位于:

spring系統學習之控制反轉 ioc

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

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

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

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

   先看ClassPathXmlApplicationContext的用法:

spring系統學習之控制反轉 ioc

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

    再看看FileSystemXmlApplicationContext的用法:

spring系統學習之控制反轉 ioc

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

     我本次練習使用的是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());

        

    }
}      

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

spring系統學習之控制反轉 ioc

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

  一,bean工廠獲得執行個體的方法:

          1,   使用構造器的方法:

spring系統學習之控制反轉 ioc

           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;
    }
    
}      
spring系統學習之控制反轉 ioc

      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,"李四");
    }
}      
spring系統學習之控制反轉 ioc

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

        1,property的方式:

spring系統學習之控制反轉 ioc

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

spring系統學習之控制反轉 ioc
spring系統學習之控制反轉 ioc

   當參數類型沖突的時候,需将二者結合使用。

         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();
    }
}      
spring系統學習之控制反轉 ioc

   預設的自動注入的方式是按類型。  是以當我們有多個接口的實作類并且以接口的方式注入的話,就不能用@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,開啟注解掃描的功能