1. 程式人生 > >深入理解spring註解之@Bean註解

深入理解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