Spring IoC註解式開發無敵詳細(細節豐富)

Rainbow-Sea發表於2024-05-19

1. Spring IoC註解式開發無敵詳細(細節豐富)

@

目錄
  • 1. Spring IoC註解式開發無敵詳細(細節豐富)
  • 每博一文案
  • 2. 註解回顧
  • 3. Spring 宣告Bean的註解
    • 3.1 Spring註解的使用
      • 3.1.1 特別的:如果要掃描的是多個包
      • 3.1.2 Spring 選擇性例項化Bean物件
    • 3.2 透過註解實現“Spring的注入”
      • 3.2.1 @Value 註解的 Bean 賦值
      • 3.2.2 @Autowired 與 @Qualifier
      • 3.2.3 @Resource 註解實現賦值操作
    • 3.3 Spring 全註解式開發
  • 4. 總結:
  • 5. 疑問:
  • 6. 最後:


每博一文案

讀書,它可以在我生活順遂的時候,看見更大的世界,
在生活不順的時候,依然可以有心態去仰望星空。
無論如果都不要將愛,轉為成了某種恨
就是我過的不好,也希望你過的好,就算我過的不好,我也依舊會幫你過的好
不管你一天,經歷了什麼,天黑了,我帶你回家。
生命是有光的,在我熄滅已前,能夠照亮你一點便是,我所有能做的了。

2. 註解回顧

既然我們要學習:”Spring IoC註解式開發“,自然就是要是用上註解 的,為了方便後面的學習,這裡我們簡單回顧一下註解的內容。更多關於註解方面的內容大家可以移步至:✏️✏️✏️ Java “框架 = 註解 + 反射 + 設計模式” 之 註解詳解-CSDN部落格

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}

以上是自定義了一個註解:Component
該註解上面修飾的註解包括:Target註解和Retention註解,這兩個註解被稱為元註解。
Target註解用來設定Component註解可以出現的位置,以上代表表示Component註解只能用在類和介面上。
Retention註解用來設定Component註解的保持性策略,以上代表Component註解可以被反射機制讀取。
String value(); 是Component註解中的一個屬性。該屬性型別String,屬性名是value。

註解賦值 ——》語法格式:@註解型別名(屬性名=屬性值, 屬性名=屬性值, 屬性名=屬性值......)
userBean為什麼使用雙引號括起來,因為value屬性是String型別,字串。
另外如果屬性名是value,則在使用的時候可以省略屬性名。

為了進一步,運用註解,這裡我們看看下面這個需求。

目前只知道一個包com.rianbowsea 的名字,掃描這個包下所有的類,當這個類上有@Compoent 註解的時候,例項化該物件,然後放到Map集合中。

在這裡插入圖片描述
在這裡插入圖片描述

在這裡插入圖片描述

package com.rainbowsea;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 使用某個註解的時候,如果註解的屬性值是陣列,並且陣列中只有一個元素,大括號可以省略。
@Target(ElementType.TYPE)
// @Retention 也是一個元註解,用來標註@Component 註解最終保留在class 檔案當中,並且可以被反射機制讀取
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

    // 定義註解的屬性
    // String 是屬性型別
    // value 是屬性名
    String value();

}

package com.rainbowsea;


//
//@Component(value = "userBean",屬性名 = 屬性值,屬性名 = 屬性值,屬性名 = 屬性值...)
//@Component(value = "userBean")
// 如果屬性名是 value,value 可以省略
@Component("userBean")
public class User {
}

package com.rainbowsea;


@Component("orderBean")
public class Order {
}

在這裡插入圖片描述
在這裡插入圖片描述

package com.rainbowsea;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class ComponentScan {

    /*
    目前只知道一個包的名字,掃描這個包下所有的類,當這個類上有@Compoent 註解的時候,
    例項化該物件,然後放到Map集合中
     */
    public static void main(String[] args) {

        //定義一個集合儲存其中的例項化物件
        Map<String,Object> beanMap = new HashMap<>();

        String packageName = "com.rainbowsea";
        // 開始掃描程式
        // .這個正規表示式表示任意字母,這裡的“.” 必須是一個普通的"."字元,不能是正規表示式中的“.”
        // 在正規表示式當中怎麼表示一個普通的"." 字元呢?使用"\",在Java當中一個“/” 要用兩個“//” 表示
        String packagePath = packageName.replaceAll("\\.", "/");

        //System.out.println(packagePath);
        // com 是在類的根路徑下的一個目錄
        //  url 是一個絕對路徑
        URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
        String path = url.getPath();
        //System.out.println(path);

        // 獲取一個絕對路徑下的所有檔案
        File file = new File(path);
        File[] files = file.listFiles();

        Arrays.stream(files).forEach(file1 -> {
            //System.out.println(file1);
            //System.out.println(file1.getName().split("\\.")[0]);
            // 拼接成:全限定類名
            String className = packageName + "." + file1.getName().split("\\.")[0];
            // 再透過反射機制,解析註解
            Class<?> aClass = null;
            try {
                aClass = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }

            // 判斷該類是否含有該 Component.class 註解
            if(aClass.isAnnotationPresent(Component.class)) {
                // 獲取註解
                Component annotation = aClass.getAnnotation(Component.class);
                // 獲取到該註解的值
                String id = annotation.value();
                // 有這個註解的都要建立物件
                try {
                    Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
                    Object obj  = declaredConstructor.newInstance();
                    // 將建立好的例項化物件儲存到 Map 當前去。
                    beanMap.put(id,obj);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }

        });


        System.out.println(beanMap);
    }
}

執行結果:

在這裡插入圖片描述

3. Spring 宣告Bean的註解

註解的存在主要是為了簡化XML的配置。Spring6倡導全註解開發

在Spring 當中,負責宣告 Bean 的註解的,常見的有如下四個:

  • @Compoent

在這裡插入圖片描述

  • @Controller

在這裡插入圖片描述

  • @Service

在這裡插入圖片描述

  • @Repository

在這裡插入圖片描述

透過原始碼可以看到,@Controller、@Service、@Repository 這三個註解都是@Component註解的別名。換句話說:這四個註解的功能都一樣。用哪個都可以。
只是為了增強程式的可讀性,建議:

  • 控制器類上使用:Controller

  • service類上使用:Service

  • dao類上使用:Repository

他們都是隻有一個value屬性。value屬性用來指定bean的id,也就是bean的名字

在這裡插入圖片描述

3.1 Spring註解的使用

如何使用以上的註解呢?

    • 第一步:加入aop的依賴
    • 第二步:在配置檔案中新增context名稱空間
    • 第三步:在配置檔案中指定掃描的包
    • 第四步:在Bean類上使用註解

第一步:加入aop的依賴

還是第一步,透過Maven 匯入相關的 jar 包,在 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>

    <groupId>com.rainbowsea.reflect</groupId>
    <artifactId>spring6-oo8-anotation-blog</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.11</version>
        </dependency>


        <!-- junit4 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>
</project>

我們可以看到當加入spring-context依賴之後,會關聯加入aop的依賴。所以這一步不用做。

在這裡插入圖片描述

第二步:在配置檔案中新增context名稱空間

注意:這裡所說的配置檔案是指,我們配置 bean 物件的那個配置.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"
       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 http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

第三步:在配置檔案中指定掃描的包

注意:這裡所說的配置檔案是指,我們配置 bean 物件的那個配置.xml 的配置檔案資訊。如下:

指定掃描的包: 是指明Spring 在那個包路徑下,可以找到要例項化的 Bean 物件。

在這裡插入圖片描述
在這裡插入圖片描述

<!--    指定掃描的包,-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean"></context:component-scan>
<?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  http://www.springframework.org/schema/context/spring-context.xsd">

<!--    指定掃描的包,-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean"></context:component-scan>
</beans>

第四步:在Bean類上使用註解

如下:上面四個註解(@Controller、@Service、@Repository @Component),我們都使用測試上,看看能否例項化成功。

在這裡插入圖片描述

在這裡插入圖片描述

package com.rainbowsea.reflect.sprint.bean;


import org.springframework.stereotype.Service;

//@Service(value = "orderBean")
@Service("os")
public class Order {
}

package com.rainbowsea.reflect.sprint.bean;


import org.springframework.stereotype.Repository;



@Repository("studentBean")
public class Student {

}

/**
 * 以上的 @Repository就相當域以下的配置資訊
 * <bean id ="student" class="com.ranbowsea.spring.bean.Student"></bean>
 */

package com.rainbowsea.reflect.sprint.bean;


import org.springframework.stereotype.Component;

@Component(value = "userBean")
public class User {

}

package com.rainbowsea.reflect.sprint.bean;


import org.springframework.stereotype.Controller;

@Controller(value = "vipBean")
public class Vip {
}

在這裡插入圖片描述

package com.rainbowsea.test;

import com.rainbowsea.reflect.sprint.bean.Order;
import com.rainbowsea.reflect.sprint.bean.Student;
import com.rainbowsea.reflect.sprint.bean.User;
import com.rainbowsea.reflect.sprint.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IoCAnnotationTest {

    @Test
    public void testIoCAnnotation() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Order os = applicationContext.getBean("os", Order.class);
        System.out.println(os);


        Student studentBean = applicationContext.getBean("studentBean", Student.class);
        System.out.println(studentBean);

        User userBean = applicationContext.getBean("userBean", User.class);
        System.out.println(userBean);

        Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
        System.out.println(vipBean);
    }
}

3.1.1 特別的:如果要掃描的是多個包

特別的: 如果要掃描的是多個包

  • 如果你要配置,掃描多個包下的檔案可以使用逗號分隔開來
  • 或者是上一級一些,不過,可能會犧牲一點效率,查詢的時間上多一些。在這裡插入圖片描述
<!--    如果要掃描的是多個包,使用逗號隔開-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean,
                                          com.rainbowsea.reflect.sprint.bean2"></context:component-scan>
<?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  http://www.springframework.org/schema/context/spring-context.xsd">

    <!--    如果要掃描的是多個包,使用逗號隔開-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean,
                                          com.rainbowsea.reflect.sprint.bean2"></context:component-scan>
</beans>

測試:

在這裡插入圖片描述
在這裡插入圖片描述
或者是上一級一些,不過,可能會犧牲一點效率,查詢的時間上多一些。如下:

在這裡插入圖片描述


    <!--    或者是上一級一些,不過,可能會犧牲一點效率,查詢的時間上多一些-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint"></context:component-scan>
<?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  http://www.springframework.org/schema/context/spring-context.xsd">

    <!--    或者是上一級一些,不過,可能會犧牲一點效率,查詢的時間上多一些-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint"></context:component-scan>
</beans>

在這裡插入圖片描述


3.1.2 Spring 選擇性例項化Bean物件

假設在某個包下有很多Bean,有的Bean上標註了@Component,有的標註了@Controller,有的標註了@Service,有的標註了@Repository,現在由於某種特殊業務的需要,只允許其中所有的Controller參與Bean管理,其他的都不例項化。這應該怎麼辦呢?

我們可以有一下兩種方案:

第一種方案:

在掃描檔案的:<context:component-scan> 的標籤當中新增上:use-default-filters=" 屬性,並該屬性指為 false 。表示該表明的包下的所有帶有宣告Bean (@Component,@Controller,@Service,@Repository)的註解全部失效不會例項化該包下的 bean 物件。

  • 在這裡插入圖片描述

而只有在 <context:component-scan> 的標籤下的,指明的:<context:include-filter> 說明的註解才會生效,才會例項化該包下的 Bean 物件。注意其中的值是:include-filter,type="annotation",expression="org.springframework.stereotype.Controller" 注意:這個包名路徑不要錯了。org.springframework.stereotype.Controller

在這裡插入圖片描述

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

    <!--    <context:component-scan base-package="com.rainbowsea.sprint.bean2" use-default-filters="false">-->
    <!--        只有@Repository獲取其他的註解被包含進來了,才生效  注意是:include-filter -->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

測試:這裡我們讓註解是:@Service,@Controller 的這個兩個有效,例項化Bean 物件,其他的註解失效,不例項化Bean 物件。

在這裡插入圖片描述
在這裡插入圖片描述

    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="false" >
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
<?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  http://www.springframework.org/schema/context/spring-context.xsd">

    <!--    <context:component-scan base-package="com.rainbowsea.sprint.bean2" use-default-filters="false">-->
    <!--        只有@Repository獲取其他的註解被包含進來了,才生效  注意是:include-filter -->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
</beans>

執行測試:

在這裡插入圖片描述

第二種方案:

use-default-filters=" 屬性,並該屬性指為 true (為true 值是預設的,可以省略不寫)。表示該表明的包下的所有帶有宣告Bean (@Component,@Controller,@Service,@Repository)的註解全部生效例項化該包下的 bean 物件。

在這裡插入圖片描述

而在 <context:component-scan> 的標籤下的,指明的:<context:exclude-filter 說明的註解會失效不會例項 化 Bean 物件,注意其中的值是:exclude-filter type="annotation" expression="org.springframework.stereotype.Component" 注意:這個包名路徑不要錯了。org.springframework.stereotype.Controller,同時注意:這裡是 exclude-filter 了。

在這裡插入圖片描述

測試:這裡我們讓註解是:@Service,@Controller 的這個兩個失效不能 例項化Bean 物件,其他的註解可以例項化Bean 物件。

在這裡插入圖片描述

在這裡插入圖片描述

<!--    use-default-filters="true" 如果這個屬性值是true,表示 com.rainbowsea.sprint.bean2-->
<!--    下的所有帶有宣告Bean (@Component,@Controller,@Service,@Repository)的註解全部生效-->
<!--    use-default-filters="true"  預設值就是 true,不用寫-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="true" >
        <!--        如果為 true ,下面宣告瞭對應的註解表示該,註解下的 Bean 失效,例如這裡的:該
           "com.rainbowsea.reflect.sprint.bean 包下的註解為:@Service,@Controller 的Bean失效-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller "/>
    </context:component-scan>
<?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 http://www.springframework.org/schema/context/spring-context.xsd">

<!--    use-default-filters="true" 如果這個屬性值是true,表示 com.rainbowsea.sprint.bean2-->
<!--    下的所有帶有宣告Bean (@Component,@Controller,@Service,@Repository)的註解全部生效-->
<!--    use-default-filters="true"  預設值就是 true,不用寫-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean" use-default-filters="true" >
        <!--        如果為 true ,下面宣告瞭對應的註解表示該,註解下的 Bean 失效,例如這裡的:該
           "com.rainbowsea.reflect.sprint.bean 包下的註解為:@Service,@Controller 的Bean失效-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller "/>
    </context:component-scan>
</beans>

執行測試:

在這裡插入圖片描述

3.2 透過註解實現“Spring的注入”

@Controller、@Service、@Repository @Component註解的 宣告後這些Bean將被例項化。接下來我們就需要對這些已經例項化的 Bean 物件進行屬性上的賦值操作了。如何給Bean的屬性賦值。給Bean屬性賦值需要用到這些註解:

  • @Value

  • @Autowired

  • @Qualifier

  • @Resource

3.2.1 @Value 註解的 Bean 賦值

當屬性的型別是簡單型別時,可以使用@Value註解進行注入。

注意:是Spring 認為的簡單型別才可以用 @Value 註解實現 bean 賦值,複雜型別(ref) 是不可以用 @Value 進行bean的賦值操作的,會出錯。

在這裡插入圖片描述

@Value註解可以出現在屬性上、setter方法上、以及構造方法的形參上。可見Spring給我們提供了多樣化的注入。

使用上: 就是註解上的簡單使用,格式即可。如下:測試

@Value 出現在屬性上,完成賦值操作

定義一個 Bean 物件,用於測試;

在這裡插入圖片描述
在相關的 spring.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"
       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 http://www.springframework.org/schema/context/spring-context.xsd">

<!--    配置掃描檔案包-->
    <context:component-scan base-package="com.rainbowsea.reflect.sprint.bean"></context:component-scan>
</beans>

執行測試:

在這裡插入圖片描述

package com.rainbowsea.test;

import com.rainbowsea.reflect.sprint.bean.Order;
import com.rainbowsea.reflect.sprint.bean.Student;
import com.rainbowsea.reflect.sprint.bean.User;
import com.rainbowsea.reflect.sprint.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IoCAnnotationTest {

    @Test
    public void testIoCAnnotation() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2.xml");
        User userBean = applicationContext.getBean("userBean", User.class);
        System.out.println(userBean);


    }
}

@Value 出現在構造方法的形參上,完成賦值操作

在這裡插入圖片描述

    public User(@Value(value = "李華") String name, @Value("21") int age, Vip vip) {
        this.name = name;
        this.age = age;
        this.vip = vip;
    }

執行測試:

說明:這個 Vip 複雜型別也賦值上了,是因為@Autowired 的自動裝配機制,當對應的 Bean 只有一個構造方法時,會自動裝配。後面會說明的。

在這裡插入圖片描述
@Value 出現在set() 方法上,完成賦值操作

在這裡插入圖片描述
執行測試: 在這裡插入圖片描述
注意:是Spring 認為的簡單型別才可以用 @Value 註解實現 bean 賦值,複雜型別(ref) 是不可以用 @Value 進行bean的賦值操作的,會出錯。

在這裡插入圖片描述
在這裡插入圖片描述

3.2.2 @Autowired 與 @Qualifier

@Autowired註解可以用來注入非簡單型別。被翻譯為:自動連線的,或者自動裝配。注意是非簡單型別的,賦值操作。
單獨使用@Autowired註解,預設根據型別裝配。【預設是byType】
看一下它的原始碼:

在這裡插入圖片描述
原始碼中有兩處需要注意:

  • 第一處:該註解可以標註在哪裡?

    • 構造方法上;方法上;形參上;屬性上;註解上
      
    • 第二處:該註解有一個required屬性,預設值是true,表示在注入的時候要求被注入的Bean必須是存在的,如果不存在則報錯。如果required屬性設定為false,表示注入的Bean存在或者不存在都沒關係,存在的話就注入,不存在的話,也不報錯。

測試:準備工作: 在這裡插入圖片描述

package com.rainbowsea.reflect.sprint.dao;


import org.springframework.stereotype.Controller;

@Controller(value = "orderDao")
public interface OrderDao {

    void insert();
}

package com.rainbowsea.reflect.sprint.dao;


import org.springframework.stereotype.Repository;

@Repository(value = "orderDaoImplForMySQL")
public class OrderDaoImplForMySQL implements OrderDao{

    @Override
    public void insert() {
        System.out.println("MySQL資料庫正在儲存訂單資訊");
    }
}

package com.rainbowsea.reflect.sprint.dao;



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service(value = "orderService")
public class OrderService {


    private OrderDao orderDao ;


    @Autowired
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void generate() {
        orderDao.insert();
    }

}

在屬性上使用@Autowired註解:

先配置一下,配置的掃描包,因為這裡,我們上面的測試 Bean 是在一個新的包下,建立的。

在這裡插入圖片描述
在這裡插入圖片描述

在這裡插入圖片描述
在set() 方法上使用@Autowired註解自動裝配:

在這裡插入圖片描述

    @Autowired // set () 方法上 @Autowired 自動裝配
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

同樣也沒有問題

在這裡插入圖片描述

在無引數構造方法上的引數上,使用@Autowired註解自動裝配:

在這裡插入圖片描述

    public OrderService(@Autowired OrderDao orderDao) {
        this.orderDao = orderDao;
    }

在這裡插入圖片描述
當有引數的構造方法只有一個時,@Autowired註解可以省略,建議不要用這種方式

在這裡插入圖片描述
在這裡插入圖片描述

當然,如果有多個構造方法,@Autowired肯定是不能省略的,會報錯。

比如:這裡我們多新增上一個無引數構造方法 。這樣就有兩個構造方法了。所以是會報錯的。

在這裡插入圖片描述

在這裡插入圖片描述
到此為止,我們已經清楚@Autowired註解可以出現在哪些位置了。
@Autowired註解預設是byType進行注入的,也就是說根據型別注入的,如果以上程式中,UserDao介面還有另外一個實現類,會出現問題嗎?會的,

在這裡插入圖片描述

@Autowired 自動裝配

根本不知道,要裝配的是哪個實現類,因為這裡有兩個類都

實現了 OrderDao 介面,而 Autowired 自動裝配是無法根據

名稱進行指定的。所以報錯。

在這裡插入圖片描述

錯誤資訊中說:不能裝配,UserDao這個Bean的數量大於1.
怎麼解決這個問題呢?當然要byName,根據名稱進行裝配了。
@Autowired註解和@Qualifier註解聯合起來才可以根據名稱進行裝配,透過在@Qualifier註解中指定Bean名稱。 如下:

在這裡插入圖片描述
@Autowired註解和@Qualifier註解聯合起來在屬性上實現名稱裝配 在這裡插入圖片描述


    @Qualifier(value = "orderDaoImplForOracle")
    @Autowired
    private OrderDao orderDao ;

執行測試:

在這裡插入圖片描述
@Autowired註解和@Qualifier註解聯合起來在 set()方法上實現名稱裝配

在這裡插入圖片描述

@Qualifier(value = "orderDaoImplForOracle")
    @Autowired
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

執行測試: 在這裡插入圖片描述


3.2.3 @Resource 註解實現賦值操作

@Resource 註解也可以完成非簡單型別 注入。那它和@Autowired 註解有什麼區別?

  1. @Resource 註解是JDK擴充套件包中的,換句話說屬於JDK的一部分。所以該註解是標準註解,更加具有通用性。(JSR-250標準中制定的註解型別,JSR是Java規範。)
  2. @Autowired 註解是Spring 框架自己的
  3. @Resource 註解預設根據名稱裝配byName,未指定name時,使用屬性名作為 name,透過name 找不到的話會自動啟動透過型別byType裝配。
  4. @Autowired 註解預設根據型別裝配byType,如果想根據名稱裝配,需要配合@Qualifier 註解一起用。
  5. @Resource 註解用在屬性上,setter 方法上。
  6. @Autowired 註解用在屬性上,setter 方法上,構造方法上,構造方法引數上。

@Resource 註解屬性JDK擴充套件包,所以不再JDK當中,需要額外引入以下依賴:如果是JDK8的話,不需要額外引入依賴,高於JDK11或低於JDK8 需要引入以下依賴 。這裡,因為要學習Spring6,因為Spring6最低支援的是JDK17,所以我這邊的是 JDK17,需要引入這個jar包。

在這裡插入圖片描述

如何上述的,@Autowried,@Qualifier,@Resource 註解,都不能額外新增有非簡單型別引數的構造方法,不然,編譯無法透過,具體原因是什麼,我目前正在探索中。如有知道的盆友,評論留言,非常非常感謝

<dependency>
  <groupId>jakarta.annotation</groupId>
  <artifactId>jakarta.annotation-api</artifactId>
  <version>2.1.1</version>
</dependency>

說明點:一定要注意:如果你用Spring6,要知道Spring6不再支援JavaEE,它支援的是JakartaEE9。(Oracle把JavaEE貢獻給Apache了,Apache把JavaEE的名字改成JakartaEE了,大家之前所接觸的所有的 javax. 包名統一修改為 jakarta.包名了。),想要了解更多的話,可以移步至:✏️✏️✏️ javaEE Web(Tomcat)深度理解 和 Servlet的本質_eelwxb-CSDN部落格 。如果大家用的是Spring6,就用上面按上面的那個 xml 配置就可以了,而JDK8不需要額外引入依賴。

@Resource註解的原始碼如下:

在這裡插入圖片描述
將 @Resource 運用在類的屬性上進行賦值操作:

在這裡插入圖片描述

 	@Resource(name = "orderDaoImplForMySQL")
    private OrderDao orderDao ;

在這裡插入圖片描述
將 @Resource 運用在類的方法set ()上進行賦值操作:

在這裡插入圖片描述

@Resource(name = "orderDaoImplForOracle")
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

在這裡插入圖片描述
注意:

  • 當@Resource註解使用在屬性上時,沒有指定name的時候,還是根據name進行查詢,這個name預設是是屬性名。
  • 當@Resource註解使用在set() 方法上時,沒有指定name的時候,還是根據name進行查詢,這個name預設是是去掉set的屬性名,其實還是屬性名。

在這裡插入圖片描述

小總結:

    1. 透過註解實現“Spring的注入” @Value 註解是簡單型別上 實現對屬性上的賦值
    2. @Autowired自動裝配(不可根據名稱裝配配合@Qualifier 可以實現根據名稱裝配)是對非簡單型別上的賦值操作。
    3. @Resource 註解也是對非簡單型別 上的屬性賦值操作,需要匯入特定的jar包,注意不同版本 JDK上的匯入的包的不同。@Resource 註解屬性JDK擴充套件包,所以不再JDK當中,需要額外引入以下依賴:如果是JDK8的話,不需要額外引入依賴,高於JDK11或低於JDK8 需要引入以下依賴 。這裡,因為要學習Spring6,因為Spring6最低支援的是JDK17,所以我這邊的是 JDK17,需要引入這個jar包。
    4. @Resource註解是JDK擴充套件包中的,也就是說屬於JDK的一部分。所以該註解是標準註解,更加具有通用性。所以非簡單型別 的賦值使用,@Resource 註解。
    <dependency>
      <groupId>jakarta.annotation</groupId>
      <artifactId>jakarta.annotation-api</artifactId>
      <version>2.1.1</version>
    </dependency>
    

3.3 Spring 全註解式開發

所謂的全註解開發就是不再使用spring.xml配置檔案了。透過寫一個配置類來代替配置檔案。

透過,下面兩個註解資訊的配置,來代替配置檔案。

  • @Configuration
  • @ComponentScan()(用spring.xml配置檔案指明上面我們的掃描包上的內容。)

在這裡插入圖片描述
在這裡插入圖片描述
使用上:@Configuration,@ComponentScan() 註解編寫配置類,來代替代替配置檔案

在這裡插入圖片描述

package com.rainbowsea.reflect;


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

@Configuration
@ComponentScan(value = {"com.rainbowsea.reflect.sprint.bean","com.rainbowsea.reflect.sprint.dao"})
public class Spring6Configuration {
}

編寫測試程式:因為,這裡我們已經透過:@Configuration,@ComponentScan() 註解編寫配置類,來代替代替配置檔案所以就不能再使用,new ClassPathXmlApplicationContext()物件的方式了。

要用 new AnnotationConfigApplicationContext(配置類.class) 這個來獲取配置類上的資訊。同樣的該類也是實現了。ApplicationContext 介面的。

在這裡插入圖片描述

編寫測試程式:

在這裡插入圖片描述

 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
package com.rainbowsea.test;

import com.rainbowsea.reflect.Spring6Configuration;
import com.rainbowsea.reflect.sprint.dao.OrderService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IoCAnnotationTest {
    public void testIoCAnnotation() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
        orderService.generate();
    }

}

在這裡插入圖片描述

4. 總結:

  1. 註解回顧:Target註解和Retention註解,這兩個註解被稱為元註解。
    Target註解用來設定Component註解可以出現的位置,以上代表表示Component註解只能用在類和介面上。Retention註解用來設定Component註解的保持性策略,以上代表Component註解可以被反射機制讀取。String value(); 是Component註解中的一個屬性。該屬性型別String,屬性名是value。

  2. 註解的存在主要是為了簡化XML的配置。Spring6倡導全註解開發

    1. Spring 宣告註解的使用:

      • 第一步:加入aop的依賴
      • 第二步:在配置檔案中新增context名稱空間
      • 第三步:在配置檔案中指定掃描的包(如果要掃描的是多個包,使用逗號隔開,或者是上一級的父級(查詢效率上,會慢一些))
      • 第四步:在Bean類上使用註解

      • @Controller、@Service、@Repository 這三個註解都是@Component註解的別名。換句話說:這四個註解的功能都一樣。用哪個都可以。
        只是為了增強程式的可讀性,建議:

        • 控制器類上使用:Controller

        • service類上使用:Service

        • dao類上使用:Repository

        他們都是隻有一個value屬性。value屬性用來指定bean的id,也就是bean的名字

    2. Spring 選擇性例項化Bean物件。兩個相反的方案可以實現。

    3. 透過註解實現“Spring的注入” @Value 註解是簡單型別上 實現對屬性上的賦值,@Autowired自動裝配(不可根據名稱裝配配合@Qualifier 可以實現根據名稱裝配)是對非簡單型別上的賦值操作。@Resource 註解也是對非簡單型別 上的屬性賦值操作,需要匯入特定的jar包,注意不同版本 JDK上的匯入的包的不同。@Resource註解是JDK擴充套件包中的,也就是說屬於JDK的一部分。所以該註解是標準註解,更加具有通用性。

    4. @Autowried,@Qualifier,@Resource 註解,都不能額外新增有非簡單型別引數的構造方法,不然,編譯無法透過,具體原因是什麼,我目前正在探索中。

    5. Spring 全註解式開發:所謂的全註解開發就是不再使用spring.xml配置檔案了。透過寫一個配置類來代替配置檔案。

      透過,下面兩個註解資訊的配置,來代替配置檔案。

      • @Configuration
      • @ComponentScan()(用spring.xml配置檔案指明上面我們的掃描包上的內容。)

5. 疑問:

在學習Spring 註解開發的過程中,我發現了一個問題,我目前並沒有搞清楚其中的由於:希望廣大朋友可以,幫我解我的疑惑,非常感謝。

疑問:就是為什麼如下,我想要用@Autowired註解和@Qualifier註解聯合起來在 set()方法上實現名稱裝配 。但是我在該裝配的類當中,新增了一個構造方法後,執行時,發現無法編譯透過,而去了,這個構造方法,就可以編譯透過了。

在這裡插入圖片描述
如下是完整的報錯資訊:

在這裡插入圖片描述

5月 05, 2024 2:36:49 下午 org.springframework.core.LocalVariableTableParameterNameDiscoverer inspectClass
警告: Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: com.rainbowsea.reflect.sprint.dao.OrderService
5月 05, 2024 2:36:49 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderService' defined in file [E:\Java\Spring6\spring6-oo8-anotation-blog\target\classes\com\rainbowsea\reflect\sprint\dao\OrderService.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.rainbowsea.reflect.sprint.dao.OrderDao' available: expected single matching bean but found 2: orderDaoImplForMySQL,orderDaoImplForOracle

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderService' defined in file [E:\Java\Spring6\spring6-oo8-anotation-blog\target\classes\com\rainbowsea\reflect\sprint\dao\OrderService.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.rainbowsea.reflect.sprint.dao.OrderDao' available: expected single matching bean but found 2: orderDaoImplForMySQL,orderDaoImplForOracle


Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.rainbowsea.reflect.sprint.dao.OrderDao' available: expected single matching bean but found 2: orderDaoImplForMySQL,orderDaoImplForOracle
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:218)

同樣,使用 @Resource 註解進行一個set() 方法注入

賦值,如果新增了,對應 bean 有引數的構造

方法,同樣也是會報錯,編譯無法透過。

在這裡插入圖片描述

在這裡插入圖片描述

6. 最後:

“在這個最後的篇章中,我要表達我對每一位讀者的感激之情。你們的關注和回覆是我創作的動力源泉,我從你們身上吸取了無盡的靈感與勇氣。我會將你們的鼓勵留在心底,繼續在其他的領域奮鬥。感謝你們,我們總會在某個時刻再次相遇。”

在這裡插入圖片描述

相關文章