深入理解spring註解之@Bean註解
本文主要從以下幾個方面來學習一下spring的註解@Bean:
-
基於xml方式bean使用回顧
-
註解@Bean詳細使用說明
-
註解@Bean的原始碼解析
1,基於xml方式bean使用回顧
新建一個maven專案增加spring-context的jar包如下:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.16.RELEASE</version> </dependency>
定義一個user使用者bean物件如下:
/**
* 定義一個使用者物件bean
*
* @author zhangqh
* @date 2018年4月30日
*/
public class User {
/**
* 使用者名稱
*/
private String userName;
/**
* 年齡
*/
private Integer age;
/**
* 省略 get set方法
*/
}
在src\main\resources目錄下邊新建一個beans.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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <!-- 定義一個id為user的bean物件 --> <bean id="user" class="com.zhang.bean.User"> <property name="age" value="26"></property> <property name="userName" value="zhangsan"></property> </bean> </beans>
編寫一個測試類如下:
/**
* 測試類
* @author zhangqh
* @date 2018年4月30日
*/
public class ApplicationTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
// 使用ClassPathXmlApplicationContext獲取spring容器ApplicationContext
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// 根據bean id獲取bean物件
User bean = (User) applicationContext.getBean("user");
System.out.println(bean);
}
}
執行結果如下:
User [userName=zhangsan, age=26]
2,註解@Bean詳細使用說明
以上的例子這邊再用註解來實現一次,首先定義一個註解配置檔案如下:
/**
* 定義一個註解配置檔案 必須要加上@Configuration註解
*
* @author zhangqh
* @date 2018年4月30日
*/
@Configuration
public class MainConfig {
/**
* 定義一個bean物件
* @return
*/
@Bean
public User getUser(){
return new User("zhangsan",26);
}
}
在以上測試類中增加註解的測試程式碼如下:
// 使用AnnotationConfigApplicationContext獲取spring容器ApplicationContext2
ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);
User bean2 = applicationContext2.getBean(User.class);
System.out.println(bean2);
執行結果如下:
User [userName=zhangsan, age=26]
到此我們用註解@Bean也同樣實現了和xml一樣bean的註冊
3,註解@Bean的原始碼解析
首先我們看一下@Bean註解的原始碼如下:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
Autowire autowire() default Autowire.NO;
String initMethod() default "";
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
我們發現@baen註解的@Target是ElementType.METHOD,ElementType.ANNOTATION_TYPE也就說@Bean註解可以在使用在方法上,以及一個註釋型別宣告,具體註解詳細說明可以檢視之前寫的一篇文章深入理解java註解的實現原理
具體引數如下:
value -- bean別名和name是相互依賴關聯的,value,name如果都使用的話值必須要一致 name -- bean名稱,如果不寫會預設為註解的方法名稱 autowire -- 自定裝配預設是不開啟的,建議儘量不要開啟,因為自動裝配不能裝配基本資料型別、字串、陣列等,這是自動裝配設計的侷限性,以及自動裝配不如顯示依賴注入精確 initMethod -- bean的初始化之前的執行方法,該引數一般不怎麼用,因為可以完全可以在程式碼中實現 destroyMethod -- bean銷燬執行的方法
下面來演示幾個配置:為user使用者bean增加註解名稱如下:
@Bean(value="user0",name="user0")
列印所有的bean名稱如下:
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType) {
System.out.println("bean名稱為==="+name);
}
bean名稱為===mainConfig
bean名稱為===user0
如果value和name內容不一樣如:
@Bean(value="user0",name="user1")
則會報如下錯誤:
Exception in thread "main" org.springframework.core.annotation.AnnotationConfigurationException: In AnnotationAttributes for annotation [org.springframework.context.annotation.Bean] declared on public com.zhang.bean.User com.zhang.config.MainConfig.getUser(), attribute 'name' and its alias 'value' are declared with values of [{user1}] and [{user0}], but only one is permitted.
at org.springframework.core.annotation.AnnotationUtils.postProcessAnnotationAttributes(AnnotationUtils.java:1301)
at org.springframework.core.annotation.AnnotatedElementUtils.getMergedAnnotationAttributes(AnnotatedElementUtils.java:394)
at org.springframework.core.type.StandardMethodMetadata.getAnnotationAttributes(StandardMethodMetadata.java:124)
at org.springframework.context.annotation.AnnotationConfigUtils.attributesFor(AnnotationConfigUtils.java:280)
at org.springframework.context.annotation.AnnotationConfigUtils.attributesFor(AnnotationConfigUtils.java:276)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:187)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:140)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:116)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:320)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:272)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:92)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
at com.zhang.ApplicationTest.main(ApplicationTest.java:26)
在使用者bean中增加初始化和銷燬的方法如下:
public void initUser(){
System.out.println("初始化使用者bean之前執行");
}
public void destroyUser(){
System.out.println("bean銷燬之後執行");
}
註解如下:
@Bean(value="user0",name="user0",initMethod="initUser",destroyMethod="destroyUser")
執行如下:
初始化使用者bean之前執行
User [userName=張三, age=26]
發現銷燬方法沒有執行,原因是bean銷魂之前程式已經結束了,可以手動close下如下:
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);
User bean2 = applicationContext2.getBean(User.class);
System.out.println(bean2);
// 手動執行close方法
applicationContext2.close();
執行結果如下:
初始化使用者bean之前執行
User [userName=張三, age=26]
bean銷燬之後執行
說明已經成功了,over