1. 程式人生 > 實用技巧 >Spring-01-初識Spring與IoC

Spring-01-初識Spring與IoC

Spring5

一、Spring概述

1.1 什麼是Spring?

  • Spring是一個開放原始碼的設計層面框架,它解決的是業務邏輯層和其他各層的鬆耦合問題,它將面向介面的程式設計思想貫穿整個系統應用。
  • 關鍵詞
    • 輕量、控制反轉(IoC)、面向切面(AOP)、容器、框架、MVC

1.2 Spring元件

  • Spring 框架是一個分層架構,由 7 個定義良好的模組組成。Spring 模組構建在核心容器之上,核心容器定義了建立、配置和管理 bean 的方式。組成 Spring 框架的每個模組(或元件)都可以單獨存在,或者與其他一個或多個模組聯合實現。

  • 核心容器:
    • 核心容器提供 Spring 框架的基本功能。核心容器的主要元件是 BeanFactory ,它是工廠模式的實現。BeanFactory 使用控制反轉(IOC) 模式將應用程式的配置和依賴性規範與實際的應用程式程式碼分開。
  • Spring 上下文:
    • Spring 上下文是一個配置檔案,向 Spring 框架提供上下文資訊。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和排程功能。
  • Spring AOP
    • 通過配置管理特性,Spring AOP 模組直接將面向切面的程式設計功能 , 整合到了 Spring框架中。所以,可以很容易地使 Spring 框架管理任何支援 AOP的物件。
    • Spring AOP 模組為基於Spring 的應用程式中的物件提供了事務管理服務。通過使用 Spring AOP,不用依賴元件,就可以將宣告性事務管理整合到應用程式中。
  • Spring DAO:
    • JDBC DAO 抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不同資料庫供應商丟擲的錯誤訊息。
    • 異常層次結構簡化了錯誤處理,並且極大地降低了需要編寫的異常程式碼數量(例如開啟和關閉連線)。Spring DAO 的面向 JDBC 的異常遵從通用的 DAO 異常層次結構。
  • Spring ORM:
    • Spring 框架插入了若干個 ORM 框架,從而提供了 ORM 的物件關係工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有這些都遵從 Spring 的通用事務和 DAO 異常層次結構。
  • Spring Web 模組:
    • Web 上下文模組建立在應用程式上下文模組之上,為基於 Web 的應用程式提供了上下文。所以,Spring 框架支援與 Jakarta Struts 的整合。
    • Web 模組還簡化了處理多部分請求以及將請求引數繫結到域物件的工作。
  • Spring MVC 框架:
    • MVC 框架是一個全功能的構建 Web 應用程式的 MVC 實現。通過策略介面,MVC 框架變成為高度可配置的,MVC 容納了大量檢視技術,其中包括 JSP、Velocity、Tiles、iText和 POI。

二、IoC——控制反轉

  • IoC其實是一種思想上的表現,核心概念就是將程式中建立物件的權利由程式猿變更給了使用者
    • KEY物件從程式猿建立變成了使用者建立
  • 為了更好的理解什麼是IoC,下面有三個例子,分別演示了3種不同的建立物件的方式

2.1 傳統方式

  • UserDao介面

    public interface UserDao {
        public void getUser();
    }
    
  • UserDao實現

    • 預設實現

      public class UserDaoImpl implements UserDao {
          @Override
          public void getUser() {
              System.out.println("UserDaoImpl :: getUser()");
          }
      }
      
    • MySQL實現

      public class UserDaoMysqlImpl implements UserDao {
          @Override
          public void getUser() {
              System.out.println("UserDaoMysqlImpl :: getUser()");
          }
      }
      
    • SQLServer實現

      public class UserDaoSqlserverImpl implements UserDao {
          @Override
          public void getUser() {
              System.out.println("UserDaoSqlserverImpl :: getUser()");
          }
      }
      
  • UserService介面

    public interface UserService {
        public void getUser();
    }
    
  • UserService實現

    public class UserServiceImpl implements UserService {
        private UserDao user = new UserDaoImpl();
        @Override
        public void getUser() {
            System.out.println("UserService :: getUser()");
            user.getUser();
        }
    }
    
  • 模擬使用者使用

    @Test
    public void useUser() {
        UserService userService = new UserServiceImpl();
        userService.getUser();
    }
    

    • 根據執行結果可以看到,這個使用者是預設實現。假設我們不想用預設實現,想要用mysql實現怎麼辦呢?
      • 需要修改UserService實現類中的程式碼,從 new UserDaoImpl() 改成 new UserDaoMysqlImpl() ,這樣就能使用mysql實現
  • 弊端:

    • 每次使用者變化需求,程式猿就要修改一次UserService實現類中的程式碼。如果說使用者需求變來變去或者說底層程式碼很複雜,那麼這對於開發來說是極其不友好的

2.2 通過setter方法注入(IoC的雛形)

  • 較傳統方式,極大減少了因需求變化而改變的程式碼量

  • 修改UserService實現類

    public class UserServiceImpl implements UserService {
        private UserDao user;
        
        public void setUser(UserDao user) {
            this.user = user;
        }
        
        @Override
        public void getUser() {
            System.out.println("UserService :: getUser()");
            user.getUser();
        }
    }
    
  • 模擬使用者使用

    @Test
    public void useUser() {
        UserServiceImpl userService = new UserServiceImpl();
        UserDao user = new UserDaoImpl();
        userService.setUser(user);
        userService.getUser();
    }
    
    • 通過set方法,我們只需要傳遞不同的UserDao實現類給UserService,就能使用不同的UserDao實現。這樣一來就方便了很多
  • 這就是IoC的雛形

2.3 通過Spring託管

  • 匯入spring依賴

    <!--使用這個依賴是因為spring-webmvc包含了大多數spring的元件,省去一個一個匯入的步驟-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
  • Spring容器核心配置檔案——beans.xml

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <bean id="mysql" class="com.pbx.dao.UserDaoMysqlImpl" />
        <bean id="default" class="com.pbx.dao.UserDaoImpl" />
        <bean id="sqlserver" class="com.pbx.dao.UserDaoSqlserverImpl" />
        
        <bean id="user" class="com.pbx.service.UserServiceImpl">
            <property name="user" ref="mysql"/>
        </bean>
    </beans>
    
  • 模擬使用者使用

    @Test
    public void springTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserService user = (UserService) context.getBean("user");
        user.getUser();
    }
    

  • 如果說我們需要呼叫不同的實現,只需要修改配置檔案中的相關引數即可

2.4 小結

  • 縱覽全部三種不同的實現方式
    • 傳統方式
      • 靈活性低,較為笨重,每次有新需求的時候都需要修改底層程式碼,工作量大
    • setter方式注入
      • 較為靈活,出現新需求時,無需修改底層程式碼,只需要根據需求的不同傳遞不同的引數即可。簡化了業務處理邏輯。
      • 假設我們服務的使用者是具有一定程式設計基礎的人群,通過setter方式進行注入的方式看起來也不是不可以。畢竟作為使用者來說,只需要傳遞不同型別的引數即可。
      • 看起來還是不錯的一種方案。但是若是業務處理時涉及到的其他元件時,這種方法依舊不好
    • Spring託管
      • Spring託管就完全避免了上述的弊端。
      • 因為需求的改動,完全不需要涉及原始碼層次的修改。使用者需要做的就是按照自身需求修改配置檔案
      • 使用者甚至不需要知道你底層是幹什麼的,只需要知道我這麼改就可滿足我的需求。而我們底層的實現中,又很好的遵守了一些規範,這樣,每當需要做不同的事情時,我們底層的程式碼修改量就很少了
  • IoC的核心思想,就是將某些具體實現類的物件的建立權利從開發人員手中交給使用的人。
    • 物件全部交給Spring託管,由Spring建立、管理和裝配
    • 本質:解耦

三、使用Spring管理物件

3.1 物件被建立的時間

  • 建立實體類

    public class User {
        private String name;
        private int id;
        private String nickname;
    }
    
  • 託管實體類

    <?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.pbx.pojo.User">
            <property name="id" value="1"/>
        </bean>
        
    </beans>
    
  • 測試

    @Test
    public void test() {
        System.out.println(1);
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        System.out.println(2);
        User user = (User) context.getBean("user");
        System.out.println(3);
        user.toString();
    }
    

  • 結論

    • 被Spring託管的物件,在ApplicationContext物件建立之後就已經被創建出來
    • 且通過配置檔案注入了相應的屬性值,然後等待呼叫。
    • 預設通過託管物件的無參構造器建立,但也可以通過有參構造器進行建立

3.2 建立物件的方式

  • Spring預設通過無參構造器建立物件,然後使用setter方法注入屬性值。若沒有無參構造器的話,Spring可以通過有參構造器建立物件

  • 建立實體類

    public class User {
        private int id;
        private String name;
        private String nickname;
    
        public User() {
            System.out.println("User::User()");
        }
    
        public User(int id, String name, String nickname) {
            System.out.println("User::User(String name, int id, String nickname)");
            this.id = id;
            this.name = name;
            this.nickname = nickname;
        }
        // 省略getter和setter方法
    }
    
  • 編寫配置

    <bean id="user1" class="com.pbx.pojo.User">
        <property name="id" value="1"/>
        <property name="name" value="張三"/>
        <property name="nickname" value="帥哥"/>
    </bean>
    
    <!--通過有參構造器的引數下標繫結引數-->
    <bean id="user2" class="com.pbx.pojo.User">
        <constructor-arg index="0" value="2" />
        <constructor-arg index="1" value="李四" />
        <constructor-arg index="2" value="小夥"/>
    </bean>
    
  • 測試

    @Test
    public void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user1 = (User) context.getBean("user1");
        System.out.println(user1.toString());
        User user2 = (User) context.getBean("user2");
        System.out.println(user2.toString());
    }
    

  • User1是通過預設方式(無參構造器)建立的,User2是通過有參構造器的方式建立的。二者都是被Spring託管的物件。使用有參構造器建立物件時,需要繫結引數,這裡會在DI的時候詳細說明

四、配置Spring

4.1 別名

alias標籤可以給被Spring託管的物件設定個別名

<alias name="user1" alias="u1"/>
<bean id="user1" class="com.pbx.pojo.User">
    <property name="id" value="1"/>
    <property name="name" value="張三"/>
    <property name="nickname" value="帥哥"/>
</bean>

4.2 Bean配置

<bean id="user1" class="com.pbx.pojo.User" name="user">
    <property name="id" value="1"/>
    <property name="name" value="張三"/>
    <property name="nickname" value="帥哥"/>
</bean>
  • name屬性下可以配置別名,而且可以配置多個別名

4.3 import

  • 用於和其他配置檔案進行合併的。會自動刪除重複資訊