一句话概括 便是 @Configuration 中所有带 @Bean 注解的办法都会被动态署理,因而调用该办法回来的都是同一个实例。

了解 调用 @Configuration 类中的 @Bean 注解的办法,回来的是同一个实例;而调用@Component 类中的 @Bean 注解的办法,回来的是一个新的实例。

注意: 上面说的调用,而不是从 Spring 容器中获取! 见最下面的 示例一 及 示例二

下面看看实现的细节:

@Configuration 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public@interfaceConfiguration{  
Stringvalue()default"";  
}

从界说来看,@Configuration 注解本质上还是 @Component,因而 <context:component-scan/>或者@ComponentScan都能处理 @Configuration 注解的类。

@Configuration 标记的类有必要契合下面的要求:

  • 装备类有必要以类的形式供给(不能是工厂办法回来的实例),允许经过生成子类在运行时增强(cglib动态署理);
  • 装备类不能是 final 类(无法动态署理);
  • 装备注解通常为了经过 @Bean 注解生成 Spring 容器办理的类;
  • 装备类有必要对错本地的(即不能在办法中声明,不能是 private)
  • 任何嵌套装备类都有必要声明为 static;
  • @Bean 办法或许不会反过来创建进一步的装备类(也便是回来的 bean 假如带有 @Configuration,也不会被特殊处理,只会作为一般的 bean)。

@Bean 注解办法履行战略

先给一个简单的示例代码:

@Configuration
publicclassMyBeanConfig{  
@Bean  
publicCountrycountry(){  
returnnewCountry();  
}  
@Bean  
publicUserInfouserInfo(){  
returnnewUserInfo(country());  
}  
}

相信大多数人第一次看到上面 userInfo() 中调用 country() 时,会以为这里的 Country 和 上面 @Bean 办法回来的 Country 或许不是同一个目标,因而或许会经过下面的办法来代替这种办法:

  • @Resource private Country country;

实际上不需求这么做(后面会给出需求这样做的场景),直接调用 country() 办法回来的是同一个实例。

@Component 注解

@Component 注解并没有经过 cglib 来署理 @Bean 办法的调用,因而像下面这样装备时,便是两个不同的 country。

@Component
publicclassMyBeanConfig{    
@Bean  
publicCountrycountry(){  
returnnewCountry();  
}  
@Bean  
publicUserInfouserInfo(){  
returnnewUserInfo(country());  
}
}

有些特殊情况下,我们不希望 MyBeanConfig 被署理(署理后会变成 WebMvcConfig$$EnhancerBySpringCGLIB$$8bef3235293)时,就需求运用 @Component,这种情况下,上面的写法就需求改成下面这样:

@Component
publicclassMyBeanConfig{  
@Autowired  
privateCountrycountry;  
@Bean  
publicCountrycountry(){  
returnnewCountry();  
}  
@Bean  
publicUserInfouserInfo(){  
returnnewUserInfo(country);  
}  
}

这种办法能够确保运用的是同一个 Country 实例。

示例一:调用 @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 ? "是同一个目标" : "不是同一个目标");
    }
}

发动项目,检查输出成果

面试官:说说 @Configuration 和 @Component 的区别

测验类 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

面试官:说说 @Configuration 和 @Component 的区别

示例二:调用 @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();
    }
}

测验成果,浏览器同样回来“不是同一个目标”:

面试官:说说 @Configuration 和 @Component 的区别

控制台和浏览器呼应成果,均契合预期。