@Configuration 標註在類上,啟動 Spring 會自動掃描@Configuration註解的類,將其註冊到IOC容器並實例化bean對象。如果在@Configuration註解的類中使用@Bean註解某個類對象的方法,Spring也會自動將註解了@Bean的方法註冊到IOC容器,併進行 ...
@Configuration
標註在類上,啟動 Spring 會自動掃描@Configuration
註解的類,將其註冊到IOC容器並實例化bean對象。如果在@Configuration
註解的類中使用@Bean
註解某個類對象的方法,Spring也會自動將註解了@Bean
的方法註冊到IOC容器,併進行實例化。
註解源碼
@Configuration
註解本質上是個 @Component
註解,所以被 @Configuration
標註的類會被註冊到IOC,且可以被 @ComponentScan
註解掃描到。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/**
* 存入到Spring IOC容器中的ID
*/
@AliasFor(annotation = Component.class)
String value() default "";
/**
* 表示被@Configuration註解的類是否被代理,以及配置類中被@Bean註解的方法生成的Bean
* 在IOC容器中是否為單例對象
*
* true:full全局模式(預設)
* false:lite輕量級模式
*
* full全局模式,被@Configuration註解的配置類會被代理(CGLIB實現),配置類中被@Bean
* 註解的方法生成的Bean在IOC容器中是單例模式。也就是說,無論調用多少次被@Bean標註的
* 方法,返回的都是同一個bean對象。
*
* lite輕量級模式,被@Configuration註解的配置類不會被代理,配置類中被@Bean註解的方法
* 生成的Bean在IOC容器中也不是單例模式。也就是說,每次調用被@Bean註解標註的方法時,都會
* 返回一個新的Bean對象。
*
* @since 5.2(Spring 5.2版本加入)
*/
boolean proxyBeanMethods() default true;
/**
* 表示使用@Bean註解標註的方法是否需要唯一的方法名。
*
* true:使用@Bean註解標註的方法具有唯一方法名稱,且方法名稱不會重疊
* false:使用@Bean註解標註的方法不唯一,存在重疊風險
*
* 預設為true。
*
* @since 6.0(Spring 6.0版本加入)
*/
boolean enforceUniqueMethods() default true;
}
使用場景
當某個類被@Configuration
註解標註時,說明這個類是配置類。可以在這個類中,使用@Bean
註解,向IOC容器中註入Bean對象;也可以使用 @Autowrite
、@Resource
、@Inject
等註解來註入所需要的Bean對象。
另外,在使用 AnnotationConfigApplicationContext
類創建IOC容器事,需要註意兩點:
- 如果使用傳入 Class 入參的構造函數,則傳入Class的配置類上的
@Configuration
可以省略,但是如果省略@Configuration
,每次調用配置類中被@Bean
標註的方法時,都會返回不同的 Bean 實例對象。 - 如果使用傳入 String 入參的構造函數,表示傳入應用程式的包名來創建 IOC容器,則配置類上的
@Configuration
不可以省略。
兩種構造方法如下:
/**
* Create a new AnnotationConfigApplicationContext, deriving bean definitions
* from the given component classes and automatically refreshing the context.
* @param componentClasses one or more component classes — for example,
* {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
/**
* Create a new AnnotationConfigApplicationContext, scanning for components
* in the given packages, registering bean definitions for those components,
* and automatically refreshing the context.
* @param basePackages the packages to scan for component classes
*/
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
使用案例
準備代碼
- 一個用於註冊到IOC的類:
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 配置類
@Configuration
public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}
- 啟動類
public class ConfigurationAnnotationTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationAnnotationTest.class);
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationAnnotationConfig.class);
ConfigurationAnnotationConfig config = context.getBean(ConfigurationAnnotationConfig.class);
Person person1 = config.person();
Person person2 = config.person();
LOGGER.info("person1 是否等於 person2 ===>> {}", (person1 == person2));
}
}
proxyBeanMethods的使用
在之前提到,proxyBeanMethods
配置表示用 @Bean
註解的方法在IOC容器中是否為單例對象,預設為true。
預設情況下,列印出結果如下:
person1 是否等於 person2 ===>> true
修改一下proxyBeanMethods的值為false:
@Configuration(proxyBeanMethods = false)
public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}
列印結果如下:
person1 是否等於 person2 ===>> false
從輸出結果可以看出,當@Configuration
中的proxyBeanMethods
屬性為false時,每次調用@Configuration
註解標註類中被@Bean標註的方法時,都會返回不同的Bean實例對象。
創建IOC容器
傳入配置類
調用AnnotationConfigApplicationContext
類的構造方法傳入配置類的Class對象創建IOC容器時,可以省略配置類上的@Configuration
註解,如下:
public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}
輸出結果:
person1 是否等於 person2 ===>> false
可以看到,若省略配置類上的@Configuration
註解,則每次調用配置類中被@Bean註解標註的方法時,都會返回不同的Bean實例對象,與@Configuration中設置proxyBeanMethods
的屬性為false的效果相同。
傳入包
調用AnnotationConfigApplicationContext
類的構造方法傳入包名創建IOC容器時,不能省略配置類上的@Configuration
註解:
public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}
執行函數:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("io.binghe.spring.annotation.chapter01.configuration");
ConfigurationAnnotationConfig config = context.getBean(ConfigurationAnnotationConfig.class);
Person person1 = config.person();
Person person2 = config.person();
LOGGER.info("person1 是否等於 person2 ===>> {}", (person1 == person2));
}
此時運行main方法,會發生報錯:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.binghe.spring.annotation.chapter01.configuration.config.ConfigurationAnnotationConfig' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1148)
at io.binghe.spring.annotation.chapter01.configuration.ConfigurationAnnotationTest.main(ConfigurationAnnotationTest.java:36)
添加上@Configuration
註解則程式執行正常。
擴展知識
AnnotationConfigApplicationContext
Spring在 BeanFactory
的基礎上提供一些具體容器的實現。AnnotationConfigApplicationContext
就是一個用來管理註解 Bean 的容器。如下結構圖:
從圖中可以看到,AnnotationConfigApplicationContext
繼承GenericApplicationContext
(通用應用上下文),而GenericApplicationContext
又實現了BeanDefinitionRegistry
介面,所以可以通過AnnotationConfigApplicationContext
實例類註冊BeanDefintion
,然後調用refresh()
方法來初始化上下文。AnnotationConfigApplicationContext
還繼承了AbstractApplicationContext
,而AbstractApplicationContext
提供了ApplicationContext
的抽象實現