天天看點

Spring-IOC快速入門Spring-IOC快速入門

Spring-IOC快速入門

Spring 是一個輕量級Java開發架構,而學習Spring架構的小夥伴們都知道Spring架構的核心是IOC容器和AOP子產品,其中IOC容器是Spring架構的底層基礎,負責管理對象的建立和對象依賴關系的維護。本文是一篇入門級别文章,主要介紹Spring-IOC的相關使用。

1. 基礎概念

1.1 IOC是什麼?

控制反轉即IOC(Inversion of Control), 可以了解為通過容器來實作對象元件的裝配和管理, 然後通過容器去擷取相關對象進行操作。

1.2 IOC有什麼作用?

  • 管理對象的建立和依賴關系的維護。
  • 解耦,由容器去維護具體的對象。
  • 托管了類的産生過程,比如動态代理。

1.3 IOC是怎麼分類的

控制反轉IOC其主要的實作方式由兩種:依賴注入和依賴查找

1.4 什麼是依賴注入(DI)

所謂的依賴注入(Dependency Injection)即元件之間的依賴關系由容器動态地将某種依賴關系的目标對象執行個體注入到容器的各個關聯的元件之中。

Spring提供以下幾種注入方式:

  • 接口注入(Interface Injection)
  • Setter 方法注入 (Setter Injection)
  • 構造器注入(Constructor Injection)

其中接口注入由于在靈活性和易用性比較差,從Spring4開始已被廢棄。

1.5 依賴查找

依賴查找(Dependency Lookup):容器提供回調接口和上下文環境給元件。EJB和Apache Avalon都使用這種方式。

2. Spring-IOC實戰

說了這麼多,那麼Spring-IOC到底要怎麼使用呢?本文作為一個入門級文章,主要關注實戰方面。

項目位址:https://github.com/Y-Aron/sprting-ioc

2.1 建立maven項目

Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <properties>
        <!-- Environment Settings -->
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring.version>5.2.6.RELEASE</spring.version>
    </properties>

    <groupId>indi.yangwj.spring</groupId>
    <artifactId>sprting-ioc</artifactId>
    <version>1.0.0</version>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>
</project>
           

2.2 建立Java對象

建立

indi.yangwj.spring.bean.User

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

//@Component
@Slf4j
@NoArgsConstructor
@Setter
@Getter
@ToString
public class User {
    private String username;
    private String password;
    private Role role;
    private List<String> interests;
    private Properties properties;
    private String[] array;
    private Map<String, String> map;
    private Set<String> set;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public User(String username, String password, Role role) {
        this.username = username;
        this.password = password;
        this.role = role;
    }
}
           

2.3 裝配Bean

2.3.1 在xml中裝配bean

建立 resources/spring-beans.xml 檔案,在xml中裝配bean主要有幾種方式:
  1. 無參構造函數:

    spring-beans.xml

  2. 靜态工廠裝配bean:

    spring-beans-static-factory.xml

  3. 執行個體工廠裝配bean:

    spring-beans-factory.xml

  4. 更多裝配方式:屬性注入
注意在一個應用程式中,bean的類名一定是唯一的,即IOC容器中不允許出現重複的類!是以以上三種方式一次隻能配置一種。
  • 無參構造函數:Bean類中必須提供無參數構造
<?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參數介紹:
        1. scope:
            prototype: 多例對象, 對象在IOC容器之前就已經建立
            singleton: 單例對象(預設值),對象在使用時才建立
        2. lazy-init:
            true: 對象在使用時建立,隻對單例對象有效(scope=singleton)
            false: 對象在IOC容器之前就已經建立 預設值
        3. init-method: 建立對象時初始化方法
        4. destroy-method: 執行ClassPathXmlApplicationContext.close()時執行的方法。注意當scope=prototype時,是不會執行destroy-method
    -->
    <bean id="user" class="indi.yangwj.spring.bean.User" />
</beans>
           
  • 靜态工廠:建立一個工廠類,在工廠類中提供一個static傳回bean對象的方法
<?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">

    <!-- 工廠的靜态方法建立對象,class: 工廠類,factory-method: 靜态方法 -->
    <bean id="user1" class="indi.yangwj.spring.factory.UserFactory" factory-method="createStaticUser" />

</beans>
           
  • 執行個體工廠:建立一個工廠類,在工廠類中提供一個非static的建立bean對象的方法,在配置檔案中需要将工廠配置,還需要配置bean
<?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 -->
    <bean id="userFactory" class="indi.yangwj.spring.factory.UserFactory"/>
    
    <!-- factory-bean:工廠類 factory-method: 非靜态方法 -->
    <bean id="user2" factory-bean="userFactory" factory-method="createUser"/>

</beans>
           

工廠類定義:

UserFactory.java

import indi.yangwj.spring.bean.User;

public class UserFactory {

    public static User createStaticUser() {
        return new User();
    }

    public User createUser() {
        return new User();
    }
}
           

2.3.2 Java代碼裝配bean

通過以下幾種注解可以定義一個bean類:@Component;@Service;@Controller;@[email protected]

注意使用注解的方式需要預先定義掃包路徑!

  • @Component;@Service;@Controller:在類定義相關注解即可
  • @[email protected]
import indi.yangwj.spring.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// @Configuration注解表明這個類是一個配置類,該類應該包含在Spring應用上下文中如何建立bean的細節。
@Configuration
public class UserConfig {

    // @Bean注解會告訴Spring這個方法将會傳回一個對象
    // 該對象要注冊為Spring應用上下文中的bean。方法體中包含了最終産生bean執行個體的邏輯。
    // 該方法傳回的對象類不允許被 @Component;@Service;@Controller這些注解修飾,否則會報錯!
    @Bean
    public User createUser() {
        return new User();
    }
}
           

2.4 建立IOC容器

Spring IOC容器的建立有以下幾種方式:
  1. XmlBeanFactory讀取xml檔案(已過時)
  2. ClassPathXmlApplicationContext讀取xml檔案
  3. AnnotationConfigApplicationContext注解方式

2.4.1 讀取xml建立IOC容器

  • XmlBeanFactory建立(已過時)
@Test
public void testBeanFactory() {
    Resource resource = new ClassPathResource("spring-beans.xml");
    BeanFactory factory = new XmlBeanFactory(resource);
    User user = factory.getBean(User.class);
    log.info("user: {}", user);
}
           
  • ClassPathXmlApplicationContext讀取xml檔案建立
@Test
public void testClassPathXml() {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
    User user = context.getBean(User.class);
    log.info("{}", user);
}
           

2.4.2 Java代碼建立IOC容器

  • 建立

    AppConfig.java

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
// 配置掃包路徑,否則擷取bean時會報org.springframework.beans.factory.NoSuchBeanDefinitionException異常
@ComponentScan("indi.yangwj.spring")
public class AppConfig {
}
           
  • 通過AnnotationConfigApplicationContext建立IOC容器
@Test
public void testAnnotationConfig() {
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    User user = context.getBean(User.class);
    log.info("{}", user);
}
           

2.5 屬性注入

2.5.1 構造器注入

建立

spring-beans-constructor.xml

必須保證bean類存在有參構造方法,并且參數要與配置完全一緻,否則解析異常!!

比如存在構造方法:

public User(String username, String password)

那麼

<constructor-arg />

必須要有兩個,一個是username,一個是password!否則解析會報錯!
<?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="role" class="indi.yangwj.spring.bean.Role" />
    <bean id="user4" class="indi.yangwj.spring.bean.User" >
        <!-- index: 參數在構造方法上的位置 -->
        <!-- name: 參數名稱 -->
        <!-- type: 參數類型 -->
        <!-- value: 參數值 -->
        <constructor-arg index="0" name="username" 
                         type="java.lang.String" value="user" />
        <constructor-arg index="1" name="password"
                         type="java.lang.String" value="passwd" />
        
        <!-- 當構造函數的值是一個對象,而不是一個普通類型的值時,使用ref屬性關聯bean對象 -->
        <constructor-arg index="2" name="role" 
                         type="indi.yangwj.spring.bean.Role" ref="role" />
    </bean>
</beans>
           

2.5.2 Setter注入

建立

spring-beans-setter.xml

相關屬性必須存在set方法,否則解析異常!
<?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="role1" class="indi.yangwj.spring.bean.Role" />
    <bean id="user5" class="indi.yangwj.spring.bean.User" >
        <!--普通值類型-->
        <property name="password" value="密碼"/>
        <!--特殊字元-->
        <property name="username">
            <value><![CDATA[測試]]></value>
        </property>
        <!--引用類型-->
        <property name="role" ref="role1" />
    </bean>
</beans>
           

2.5.3 集合屬性注入

建立

spring-beans-property.xml

集合屬性注入包括數組;List;Set;Map;Properties 等集合對象的注入,一般情況下很少使用…
<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="user7" class="indi.yangwj.spring.bean.User" >

        <!-- 使用<array></array>或<list></list>标簽注入數組 -->
        <property name="array">
            <list>
                <value>1</value>
                <value>2</value>
                <value>3</value>
            </list>
        </property>

        <!--使用<list></list>注入 `java.util.List`-->
        <property name="interests" >
            <list>
                <value>list aaa</value>
                <value>list bbb</value>
                <value>list ccc</value>
            </list>
        </property>
        <!--使用<set></set>注入 `java.util.Set`-->
        <property name="set" >
            <set>
                <value>set 123</value>
                <value>set 456</value>
                <value>set 789</value>
            </set>
        </property>

        <!-- 使用<map></map>注入 `java.util.Map`-->
        <property name="map">
            <map>
                <entry key="key1" value="value1" />
                <entry key="key2" value="value2" />
                <entry key="key3" value="value3" />
            </map>
        </property>
        <!--使用<props></props>注入 `java.util.Properties`-->
        <property name="properties">
            <props>
                <prop key="propKet1">propValue1</prop>
                <prop key="propKet2">propValue2</prop>
                <prop key="propKet3">propValue3</prop>
            </props>
        </property>
        <!-- 通過util命名空間配置集合類型的bean -->
        <property name="roleIds" ref="roleIds" />
    </bean>

    <!--
       util命名空間定義
       1. 導入util命名空間: <beans xmlns:util="http://www.springframework.org/schema/util"
               xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"></beans>
       2. Array/List/Set/Map/Properties等,用法與上述相同
    -->
    <util:list id="roleIds" value-type="java.lang.Integer">
        <value>1</value>
        <value>2</value>
    </util:list>

</beans>
           

2.5.4 c和p注入

建立

spring-beans-cp.xml

c-命名空間和p-命名空間是Spring3.引入,需要在xml頂部聲明其模式。
  1. c-命名空間實作構造器注入:簡化

    <constructor-arg />

    , 但是規則與其一緻!
  2. p-命名空間實作屬性注入
<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!--
        使用c名稱空間進行參數指派
        1. 導入c命名空間:<beans xmlns:c="http://www.springframework.org/schema/c"></beans>
        2. 使用c命名空間配置構造函數的參數
        3. c命名空間無法裝配集合
     -->
    <bean id="role2" class="indi.yangwj.spring.bean.Role" />

    <bean id="user6" class="indi.yangwj.spring.bean.User"
          c:password="passwd"
          c:role-ref="role2"
          c:username="admin"/>
    
    <!--
    p命名空間注入參數,
    1. 導入p命名空間: <beans xmlns:p="http://www.springframework.org/schema/p"></beans>
    2. 使用p:屬性完整注入
        |-值類型: p:屬性名="值"
        |-對象類型: p:屬性名-ref="bean名稱" -->
<bean id="sysadmin" class="org.aron.springTest.bean.Sysadmin"
      p:nickname="昵稱"
      p:role-ref="role" />
</beans>
           

2.5.5 使用外部配置檔案

  • 建立

    application.properties

jdbc.url=127.0.0.1
jdbc.username=root
jdbc.password=123456
           
  • 建立

    spring-application.xml

    ,通過加載外部配置檔案裝配 bean
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--
        1. 導入context命名空間: <beans xmlns:context="http://www.springframework.org/schema/context"
               xsi:schemaLocation="http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context.xsd">
        2. 加載外部屬性檔案
        3. 使用 ${} 擷取屬性
    -->
    <context:property-placeholder location="classpath:/application.properties" />

    <bean id="jdbcBean" class="indi.yangwj.spring.bean.JDBCBean">
        <constructor-arg name="username" value="${jdbc.username}"/>
        <constructor-arg name="url" value="${jdbc.url}" />
        <constructor-arg name="password" value="${jdbc.password}" />
    </bean>
</beans>
           

3. SPEL的使用

Spring 表達式語言(簡稱SpEL):是一個支援運作時查詢和操作對象圖的強大的表達式語言。

文法類似于 EL:SpEL 使用 #{…} 作為定界符 , 所有在大括号中的字元都将被認為是 SpEL , SpEL 為 bean 的屬性進行動态指派提供了便利。

3.1 定義字面量

  • 整數:

    <property name="count" value="#{5}" />

  • 小數:

    <property name="frequency" value="#{89.7}" />

  • 科學計算法:

    <property name="capacity" value="#{1e4}" />

  • string可以使用單引号或雙引号作為字元串的定界符号
<property name="name" value="#{'Chuck'}" />

<property name="name" value='#{"Chuck"}' />
           
  • Boolean:

    <property name="enabled" value="#{false}" />

  • Array/List/Set:

    <property name="name" value="#{{'set1', 'set2', 'set3'}}" />

  • Map/Properties:

    <property name="map" value="#{{'k1': 'v1', k2: 'v2'}}">

3.2 引用Bean/屬性/方法

  • 引用其他對象
<!-- 通過value屬性和spel配置bean之間的應用關系 -->
<property name="prefix" value="#{prefixGenerator}" />
           
  • 引用其他對象的屬性
<!-- 通過value屬性和spel配置suffix屬性值為另一個bean的suffix屬性值 -->
<property name="suffix" value="#{prefixGenerator.suffix}" />
           
  • 調用其他方法,還可以進行鍊式調用
<!-- 通過value屬性值和spel配置suffix屬性值為另一個bean的方法的傳回值 -->
<property name="suffix" value="#{prefixGenerator.toString()}" />

<!-- 方法連調 -->
<property name="suffix" value="#{prefixGenerator.toString().toUpperCase()}" />
           

3.3 支援的運算符号

  • 算數運算符: +, -, *, /, %, ^
<property name="adjustedAmount" value="#{counter.total + 42}" />
<property name="adjustedAmount" value="#{counter.total - 20}" />
<property name="circumference" value="#{2 * T(java.lang.Math).PI * circle.radius}" />
<property name="average" value="#{counter.total / counter.count}" />
<property name="remainder" value="#{counter.total % counter.count}" />
<property name="area" value="#{T(java.lang.Math).PI * circle.radius ^ 2}" />
           
  • 加号還可以用作字元串連接配接
  • 比較運算符: <, >, ==, <=, >=, lt, gt, eq, le, ge
<property name="equal" value="#{counter.total == 100}" />
<property name="hasCapacity" value="#{counter.total le 1000}" />
           
  • 邏輯運算符号: and, or, not, !
<property name="large" value="#{shape.kind == shape.perimeter}" />
<property name="outOfStock" value="#{!product.available}" />
<property name="outOfStock" value="#{not product.available}" />
           
  • if-else運算符: ?:(elvis)
  • if-else的變體
  • 正規表達式:matches
  • 調用靜态方法和靜态屬性:通過T()調用一個類的靜态方法,它将傳回一個Class Object,然後再調用相應的方法或屬性