博客 / 詳情

返回

spring6-註解式開發

spring框架創建bean就是利用反射機制

反射機制的代碼如下:

public static void main(String[] args) throws Exception {
    System.out.println("Hello, World!");
    // 使用反射機制調用方法
    // 獲取類
    Class<?> clazz = Class.forName("com.ali.bean2.SomeService");
    // 獲取方法
    Method method = clazz.getMethod("doSomething", String.class, int.class);
    // 獲取對象
    Object obj = clazz.newInstance();
    // 方法調用
    // obj: 哪個對象調用這個方法
    // "hello", 42: 方法參數
    // hello: 方法的返回值
    Object hello = method.invoke(obj, "hello", 42);
}

spring IoC註解式開發

註解主要是為了簡化xml配置。

註解怎麼定義

新建Java Class 時選擇Annoation 類型的文件,這樣就創建了一個新的註解

// 自定義註解
// @Target 標註註解的註解,叫做元註解
// ElementType.TYPE: 表示可以標註在類上
// ElementType.FIELD: 表示可以標註在屬性上
// 使用某個註解的時候,如果註解的屬性名是value,可以省略屬性名
// 使用某個註解的時候,如果註解的屬性值是數組,並且數組中只有一個值,可以省略大括號
@Target(value = {ElementType.TYPE,ElementType.FIELD})
// @Retention 標註註解的生命週期,叫做元註解,表示最終保留在class文件中,並且可以被反射機制讀取
// RetentionPolicy.SOURCE: 註解只在源碼中存在,編譯成字節碼後就不存在了
// RetentionPolicy.CLASS: 註解在源碼和字節碼中都存在,運行時不存在(默認值),也就是不能被反射機制讀取
// RetentionPolicy.RUNTIME: 註解在源碼、字節碼和運行時都存在
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value();
}

怎麼通過反射機制讀取註解

假設user類被註解@Component修飾

@Component("userBean")
public class User {
}
public static void main(String[] args) throws Exception {
    // 使用反射機制讀取註解
    // 獲取類
    Class<?> userClazz = Class.forName("com.ali.component.User");
    // 判斷類上是否有某個註解
    boolean hasAnnotation = userClazz.isAnnotationPresent(Component.class);
    if (hasAnnotation) {
        // 獲取類上的註解對象
        Component component = userClazz.getAnnotation(Component.class);
        // 訪問註解的屬性
        System.out.println("component value: " + component.value());
    }
}

組件掃描原理

主要是通過反射機制實例化註解標記的類的對象。

public static void main(String[] args) {
    // 根據一個包名,掃描這個包下面的所有類,當這個類有@Component註解時,實例化這個類,key是@Component註解的value,value是實例化的對象
    String packageName = "com.ali.component";
    // 將包名中的“.”替換成“/”
    String path = packageName.replaceAll("\\.", "/");
    // 包是在系統恨路徑下的一個目錄。獲取它的絕對路徑
    String absolutePath = ClassLoader.getSystemClassLoader().getResource(path).getPath();
    // 獲取包下面的所有類文件
    File dir = new File(absolutePath);
    File[] files = dir.listFiles();
    Arrays.stream(files).forEach(file -> {
        // 獲取類名
        String classname = packageName + "." + file.getName().replace(".class", "");
        // 通過反射機制解析註解
        try {
            Class<?> clazz = Class.forName(classname);
            // 判斷類上是否有某個註解
            boolean hasAnnotation = clazz.isAnnotationPresent(Component.class);
            if (hasAnnotation) {
                // 獲取類上的註解對象
                Component component = clazz.getAnnotation(Component.class);
                // 訪問註解的屬性
                System.out.println("component value: " + component.value());
                // 實例化這個類
                Object obj = clazz.getDeclaredConstructor().newInstance();
                System.out.println("實例化對象: " + obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

聲明bean的註解

聲明bean的註解有:@Component 、@Controller、 @Service 、@Repository。

實際上 @Controller @Service @Repository 這3個都是@Component 的別名,本質上就是一個註解。主要是為了增強代碼的可讀性。

當這4個註解標註在類上是,如果沒有指定value值(也就是沒有指定bean名稱),那麼bean名稱默認成類名的首字母小寫。

選擇性實例化bean

假如有一個需求:只需要@Controller參與bean的管理。其他三個註解都不參與實例化,這種情況要怎麼處理?

注意:別忘了添加配置文件的命名空間。

   <!-- 第一種方案:使用@ComponentScan自動掃描bean
    use-default-filters="false" 表示不使用默認的過濾器(即所有聲明bean的註解全部失效)
    @Component 、@Controller、 @Service 、@Repository 全部失效-->
    <context:component-scan base-package="com.ali.bean2" use-default-filters="false">
        <!--    自定義過濾器,指定只掃描帶有@Component註解的類
            只有@Component生效-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
<!--            如果想讓@Service生效,可以添加下面這一行-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>

<!--    第二種方案:use-default-filters="true" 或者不寫該屬性,表示 @Component 、@Controller、 @Service 、@Repository 全部生效-->
    <context:component-scan base-package="com.ali.bean2" >
        <!--   排除@Controller註解的類
            只有@Controller生效-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <!--            如果想讓@Repository失效,可以添加下面這一行-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>

負責注入的註解

@value

@value:使用@value注入的話,屬性可以不提供setter方法,負責注入簡單類型

public class User {

    @Value("Alice")
    private String name;
    @Value("22")
    private int age;
}

@value註解也可以加在setter方法上

public class User {
    private String name;
    private int age;

    @Value("Alice")
    public void setName(String name) {
        this.name = name;
    }
    @Value("22")
    public void setAge(int age) {
        this.age = age;
    }
}

@value註解也可以加在構造方法上

public class User {

    private String name;
    private int age;

    public User(@Value("Alice") String name,@Value("22") int age) {
        this.name = name;
        this.age = age;
    }
}

@Autowired

@Autowired可以用來注入非簡單類型,翻譯為:自動裝配。

單獨使用 @Autowired註解,默認根據類型裝配【默認是byType】

注意:假如一個接口被2個或以上的類實現,那麼這個接口對象能被 @Autowired注入嗎?這當然不行,因為 @Autowired是根據類型進行裝配的。

那怎麼解決這個問題呢?

可以將@Autowired和@Qualifier聯合使用

@Autowired
// 這裏指定bean的名稱,説明是根據名稱進行自動裝配的
@Qualifier("someServiceBeanForMysql") 
private SomeService someService;

@Autowired可以標註在屬性上、setter方法上、構造方法的參數上。

當一個類中的構造方法只有一個,並且構造方法的參數和屬性能對應上,@Autowired 可以省略 (不建議這樣用)

@Resource

@Resource註解也能完成非簡單類型的注入,那麼他和 @Autowired有什麼區別呢?

  • @Resource是jdk擴展包中的,屬於jdk的一部分,所以該註解是標準註解,更加具有通用性。
  • @Autowired是spring框架自己的
  • @Resource默認根據名稱自動裝配,未指定name時,使用屬性名作為name,如果通過name找不到的話。會啓動通過類型自動裝配。
  • @Autowired時根據類型自動裝配。如果要根據名字裝配,需要@Qualifier配合使用
  • @Resource用在屬性上、setter方法上
  • @Autowired用在屬性上、setter方法上、構造方法上、構造方法參數上。

使用@Resource時先加入相關依賴

<!--        @Resource註解的依賴-->
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
        </dependency>
// 進行屬性注入
@Resource(name = "someServiceBean")
private SomeService someService;
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.