Spring系列之Spring的魅力在哪

Spring 現在毫無疑問是 Java 中最受歡迎的框架。Spring 已經成爲事實上的 Java 企業級開發的標準。Spring 爲何有這麼大的魅力,經過這篇文章來簡單聊聊。java

本文基於 Spring5程序員

爲何須要 Spring

在 Java 在早期用來作企業級開發很困難,各種開發框架及其繁瑣複雜,很差管理。而後 Spring 拯救了 Java,由於 Spring 能夠簡化 Java 開發。傳統 Java 開發的困難之一在於代碼的耦合度很高,各個組件的侵入性很強。在這裏耦合度高和侵入性表達的是一樣的問題。看下這個簡單的例子,士兵和武器的關係:spring

public class Soldier {
    private Weapon weapon;

    public void Soldier() {
        weapon = new Knife();
    }
}
public class Knife implements Weapon {
}
public interface Weapon {
}
複製代碼

上面的代碼壞在哪裏?沒錯,就是代碼的耦合度過高了,也能夠說 Weapon 的侵入性太強。這樣一來 Soldier 只能持有 Knife,想換一把武器就得修改一次代碼,這對於運行的系統來講確定是不能被接收了。實際要解決這個問題其實也很簡單,只要作一點小小的改動:編程

public class Soldier {
    private Weapon weapon;
    
    public void Soldier(Weapon weapon) {
        this.weapon = weapon;
    }
}
public class Gun implements Weapon{
}

Weapon gun = new Gun();
Soldier s = new Soldier(gun);
複製代碼

作了一點小小的改動以後,好多了,這下 Soldier 想用什麼武器就用什麼武器,而不用換武器都須要修改 Solider 的代碼。這個其實就是 Spring 的核心特性之一:依賴注入。但這樣仍是挺麻煩,每次都須要手動的去 new 一個對象,而後做爲一個參數傳進去,這個問題天然也有解決的辦法,咱們後面再講。再來講一下引發代碼複雜度的另外一個緣由。安全

有時候除了寫業務代碼以外,咱們還須要作不少的其餘的工做,好比安全檢查,打日誌等等,若是按照正常咱們經常使用的寫代碼的方式,以下:微信

public class Soldier {
    private Weapon weapon;

    public void Soldier(Weapon weapon) {
        this.weapon = weapon;
    }
    public void fight() {
        FightLog.fightLogBefore();
        weapon.attack();
        FightLog.fightLogAfter();
    }
}
public class FightLog {
    public static void fightLogBefore() {
        System.out.println("Fight begin");
    }
    public static void fightLogAfter() {
        System.out.println("Fight after");
    }
}
複製代碼

若是咱們要記錄 Soldier 打鬥的全過程,就不得不在打鬥開始和結束的地方進行記錄。這些記錄打鬥日誌的代碼會出如今系統的各個地方,並且和實際的業務邏輯無關,因此這樣就會使代碼變得很混亂,並且也有不少重複的代碼。Spring 爲這個問題也準備了一個解決方法:面向切面編程(AOP)架構

這兩個問題也是平常開發中最容易遇到的問題,Spring 的核心目標就是爲了解決這兩個問題,以此來簡化 Java 的開發,並且 Spring 也作到了。框架

什麼是 Spring 容器

很難精確的使用語言去描述什麼是 Spring 容器。Spring 容器就像是一臺機器,你須要往裏放兩種原料,而後這臺機器就能把你想要的系統給你生產出來。兩種原料一種是業務類(POJOs)配置數據。就像下面這個圖所示:ide

POJO: Plain old Java Object,表明的就是普通的 Java 類 配置數據: 配置各個 POJO 之間的依賴關係函數

Spring 容器說究竟是裝東西的,裏面裝的是 Spring Bean,也就是 POJO,這些 Bean 的生命週期都由 Spring 來管理。

Spring 容器的實現

在 Spring 中,容器的實現不止一種。每種不一樣的容器都實現了 BeanFactory。這麼多的容器實現是爲了應對不一樣的狀況,好比 Web 開發就須要一些額外的特性。還有爲了方便不一樣的配置文件,也實現了不一樣的容器,這些容器最核心的目標都是用來管理 Bean。容器的繼承關係很複雜,就不完整列出了。下面是其中幾個比較關鍵的容器實現:

上面的每個容器均可以直接使用,可是不多會直接使用 BeanFactory,通常都會使用 ApplicationContext 及其子類。ApplicationContext 及其子類提供了更多的服務,好比從配置文件中加載信息等等,能夠構建不一樣類型的 Spring 應用。

以 ClassPathXmlApplicationContext 容器配置爲例:

<?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
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="gun" class="cn.rayjun.springdemo.springcontainer.Gun">
    </bean>

    <bean id="solider" class="cn.rayjun.springdemo.springcontainer.Soldier">
        <constructor-arg name="weapon" ref="gun"/>
    </bean>
</beans>
複製代碼

上面咱們說到了 Spring 容器須要兩種原料,上面這個 xml 配置文件就是配置數據,Soldier 以及 Weapon 和其子類就是 POJOs。在這個文件中,咱們將各個 POJO 之間的依賴關係配置好。而後 Spring 容器就會根據這些配置生成一個可用的系統。配置數據除了使用 xml 以外,還可使用註解和 Java 代碼來進行配置,這部分後續再詳聊。

這些容器也都提供了獲取 Bean 的 API,以下:

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Soldier s = context.getBean("soldier");
複製代碼

可是永遠不要用上面那種方式來獲取 Bean,容器原本就是用來解耦的,若是用上面那種方式,根本就沒有起到解耦的目的。具體使用方式在後續文章中詳解。

Spring Bean 的生命週期。

Spring Bean 是容器管理的基本單位。Spring Bean 與普通的 Java 對象沒有什麼區別。

每個 Java 對象都有其生命週期,從對象被建立出來,到最後被回收,Spring Bean 天然也不例外,只是 Spring Bean 的生命週期要比普通的類稍微複製一點,但這些複雜的過程都是由容器來進行管理的,不須要開發者作額外的事情。並且開發者能夠經過配置來決定一個 Bean 的生命週期。

其中一些陌生的類名不知道不要緊,這裏也不須要關注,後續分析源碼的時候咱們再聊,在這裏咱們須要關注兩個地方,就是在 Bean 初始化和回收的時候,能夠調用咱們自定義的初始化和銷燬方法,這個仍是很是有用的。

Spring 的兩大特性

Spring 有兩大特性:依賴注入(DI)面向切面編程(AOP)

上面其實已經聊到了,Spring 的這兩大特性其實就是用來解決 Java 編程的兩大痛點。

依賴注入的目的是代碼解耦。固然徹底的解耦是不可能的,類之間沒有交互,程序就沒法跑起來了。

依賴注入的實現方式很簡單,上文中將 Weapon 的實例經過構造函數傳入就是依賴注入的一種實現。另外一種注入的方式就是經過 Setter 方法來進行注入。依賴注入自己除了名字比較高端,其實比較簡單,可是有一點須要注意,依賴注入常常會和面向接口編程一塊兒出現,也就是說依賴注入須要使用多態的特性來使得解耦更加完全。假如不使用接口編程,上面的代碼會編程什麼樣呢:

// 用刀的 Soldier
public class Soldier {
    private Knife knife;
    public void Soldier(Knife knife) {
        this.knife = knife;
    }
    public void fight() {
        knife.attack();
    }
}
複製代碼

假如說我要把刀換成槍,只能經過該代碼來完成。

// 用槍的 Soldier
public class Soldier {
    private Gun gun;
    public void Soldier(Gun gun) {
        this.gun = gun;
    }
    public void fight() {
        gun.attack();
    }
}
複製代碼

也就是說,若是不使用多態,依賴注入的威力頓時沒了 90%。

在 Spring 中,注入這個操做是 Spring 容器來完成的,須要開發者將各個 Bean 之間的依賴關係在配置中定義好,代碼中基本不會出現 new 這個關鍵字,可是整個程序卻跑起來了。

依賴注入常常會和控制反轉一塊兒出現,這兩個概念之間有什麼關係嗎?實際上能夠說依賴注入是控制反轉的一種實現。在沒有 Spring 以前,若是須要對象,咱們都須要手動 new 一個,而如今,全部的對象都不要本身來 new 了,這些事情都交給 Spring 容器來作了,也就是把生成對象的權利交給了 Spring 容器,因此 Spring 容器也稱之爲 IOC 容器。

Spring 的另外一大特性叫面向切面編程(AOP)。DI 的目標是使得代碼之間的耦合度更低 (低耦合),那麼 AOP 的目標就是使代碼的職責更加單一,提升代碼的可複用性 (高內聚)

AOP 中引入了一個新的概念:切面切點。咱們能夠把每個 Bean 都配置爲一個切面,Bean 中的每個方法均可以配置爲一個切點。

FightLog 類改造以下,那麼在 Soldier 在 fight 方法執行以後,戰鬥的過程也會被記錄下來。這就是 AOP 的威力,不須要修改代碼,就能夠增長切面的方式來使代碼變得整潔。

@Aspect
public class FightLog {
    @Before("execution(* cn.rayjun.springdemo.springcontainer.Soldier.fight(..))")
    public static void fightLogBefore() {
        System.out.println("Fight begin");
    }

    @After("execution(* cn.rayjun.springdemo.springcontainer.Soldier.fight(..))")
    public static void fightLogAfter() {
        System.out.println("Fight after");
    }
}

public class Soldier {
    private Weapon weapon;

    public void Soldier(Weapon weapon) {
        this.weapon = weapon;
    }

    public void fight() {
        weapon.attack();
    }
}
複製代碼

若是不使用 AOP,這些與業務無關的代碼會處處都是,並且會破壞代碼職責單一性。常見的場景就是日誌,我須要給某些點打日誌,可是又不想代碼中處處都是日誌代碼,那麼使用 AOP 就是一個比較好的選擇了,具體的原理後續的文章中再詳細說。

Spring的這兩大特性將面向對象的思想實踐的淋漓盡致。學會了 Spring,也就真懂面向對象編程了。

Spring 架構

Spring 已經演化到了第 5 個大版本了,因此 Spring 遠遠不僅有容器了。實際上,Spring 已經爲建設企業級應用的各方面都給出瞭解決方案,能夠用 Spring 來建設多種類型的應用。

Spring 的核心架構圖:

好比爲了讓開發者使用 Spring 更加便利,SpringBoot 就誕生了,SpringBoot 並非一個新的技術,而是官方對 Spring 框架作了一個封裝。

Spring 框架強大,使用簡單,能夠應對各種應用。在面對一個複雜的系統時,Spring 的依賴注入和 AOP 特性能夠簡化系統架構,讓普通程序員也能夠構建出不差的系統。

原文

關注微信公衆號,聊點其餘的

相關文章
相關標籤/搜索