終於搞懂了 -Configuration 和 -Component 的區別 !

來源:blog.csdn.net/qq_29025955/article/details/128818957

一句話概括就是 @Configuration 中所有帶 @Bean 註解的方法都會被動態代理,因此調用該方法返回的都是同一個實例。

理解:調用@Configuration類中的 @Bean 註解的方法,返回的是同一個示例;而調用@Component類中的@Bean註解的方法,返回的是一個新的實例。

注意:上面說的調用,而不是從 spring 容器中獲取! 見最下面的示例 1 及 示例 2

下面看看實現的細節。

@Configuration 註解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

從定義來看, @Configuration註解本質上還是@Component,因此 <context:component-scan/> 或者 @ComponentScan 都能處理@Configuration註解的類。

@Configuration標記的類必須符合下面的要求:

@Bean 註解方法執行策略

先給一個簡單的示例代碼:

@Configuration
public class MyBeanConfig {

    @Bean
    public Country country(){
        return new Country();
    }

    @Bean
    public UserInfo userInfo(){
        return new UserInfo(country());
    }

}

相信大多數人第一次看到上面 userInfo() 中調用 country()時,會認爲這裏的 Country 和上面 @Bean方法返回的 Country 可能不是同一個對象,因此可能會通過下面的方式來替代這種方式:

實際上不需要這麼做(後面會給出需要這樣做的場景),直接調用country() 方法返回的是同一個實例。

@Component 註解

@Component註解並沒有通過 cglib 來代理@Bean 方法的調用,因此像下面這樣配置時,就是兩個不同的 country

@Component
public class MyBeanConfig {

    @Bean
    public Country country(){
        return new Country();
    }

    @Bean
    public UserInfo userInfo(){
        return new UserInfo(country());
    }

}

有些特殊情況下,我們不希望 MyBeanConfig被代理(代理後會變成WebMvcConfig$$EnhancerBySpringCGLIB$$8bef3235293)時,就得用 @Component,這種情況下,上面的寫法就需要改成下面這樣:

@Component
public class MyBeanConfig {

    @Autowired
    private Country country;

    @Bean
    public Country country(){
        return new Country();
    }

    @Bean
    public UserInfo userInfo(){
        return new UserInfo(country);
    }

}

這種方式可以保證使用的同一個 Country 實例。

示例 1:調用 @Configuration 類中的 @Bean 註解的方法,返回的是同一個示例

第一個 bean 類

package com.xl.test.logtest.utils;

public class Child {
 private String name = "the child";

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

第二個 bean 類

package com.xl.test.logtest.utils;

public class Woman {
 
 private String name = "the woman";
 
 private Child child;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public Child getChild() {
  return child;
 }

 public void setChild(Child child) {
  this.child = child;
 }
}

@Configuration

package com.xl.test.logtest.utils;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Configuration
//@Component
public class Human {
 
 @Bean
 public Woman getWomanBean() {
  Woman woman = new Woman();
  woman.setChild(getChildBean()); // 直接調用@Bean註解的方法方法getChildBean()
  return woman;
 }
 
 @Bean
 public Child getChildBean() {
  return new Child();
 }
}

測試類 I

本測試類爲一個配置類,這樣啓動項目是就可以看到測試效果,更加快捷;也可以使用其他方式測試見下面的測試類 II

package com.xl.test.logtest.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Man {
 
 @Autowired
 public Man(Woman wn, Child child) {
  System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
  System.out.println(wn.getChild() == child ? "是同一個對象":"不是同一個對象");
 }
}

啓動項目,查看輸出結果:

測試類 II

package com.xl.test.logtest.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.xl.test.logtest.utils.Child;
import com.xl.test.logtest.utils.Woman;

@RestController
public class LogTestController {
 @Autowired
 Woman woman ;
 
 @Autowired
 Child child;
 
 @GetMapping("/log")
 public String log() {
  return woman.getChild() == child ? "是同一個對象":"不是同一個對象";
 }
}

瀏覽器訪問項目,查看結果;輸入localhost:8080/log

示例 2 :調用 @Component 類中的 @Bean 註解的方法,返回的是一個新的實例。

測試代碼,只需要將@Configuration改爲@Component即可!其他的均不變

package com.xl.test.logtest.utils;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

//@Configuration
@Component
public class Human {
 
 @Bean
 public Woman getWomanBean() {
  Woman woman = new Woman();
  woman.setChild(getChildBean()); // 直接調用@Bean註解的方法方法getChildBean()
  return woman;
 }
 
 @Bean
 public Child getChildBean() {
  return new Child();
 }
}

測試 :

控制檯和瀏覽器展示,均符合預期!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/zfCt7tmRXBj_PqcwYsdHnA