深入理解spring注解之@Bean注解

本文主要从以下几个方面来学习一下spring的注解@Bean:

  • 基于xml方式bean使用回顾

  • 注解@Bean详细使用说明

  • 注解@Bean的源码解析

1,基于xml方式bean使用回顾

新建一个maven项目增加spring-context的jar包如下:

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>

定义一个user用户bean对象如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 定义一个用户对象bean
*
* @author zhangqh
* @date 2018年4月30日
*/
public class User {
/**
* 用户名
*/
private String userName;
/**
* 年龄
*/
private Integer age;
/**
* 省略 get set方法
*/
}

在src\main\resources目录下边新建一个beans.xml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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>

编写一个测试类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 测试类
* @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);
}
}

运行结果如下:

1
User [userName=zhangsan, age=26]

2,注解@Bean详细使用说明

以上的例子这边再用注解来实现一次,首先定义一个注解配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 定义一个注解配置文件 必须要加上@Configuration注解
*
* @author zhangqh
* @date 2018年4月30日
*/
@Configuration
public class MainConfig {
/**
* 定义一个bean对象
* @return
*/
@Bean
public User getUser(){
return new User("zhangsan",26);
}
}

在以上测试类中增加注解的测试代码如下:

1
2
3
4
// 使用AnnotationConfigApplicationContext获取spring容器ApplicationContext2
ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);
User bean2 = applicationContext2.getBean(User.class);
System.out.println(bean2);

运行结果如下:

1
User [userName=zhangsan, age=26]

到此我们用注解@Bean也同样实现了和xml一样bean的注册

3,注解@Bean的源码解析

首先我们看一下@Bean注解的源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
@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增加注解名称如下:

1
@Bean(value="user0",name="user0")

打印所有的bean名称如下:

1
2
3
4
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType) {
System.out.println("bean名称为==="+name);
}

bean名称为===mainConfig
bean名称为===user0

如果value和name内容不一样如:

1
@Bean(value="user0",name="user1")

则会报如下错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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中增加初始化和销毁的方法如下:

1
2
3
4
5
6
public void initUser(){
System.out.println("初始化用户bean之前执行");
}
public void destroyUser(){
System.out.println("bean销毁之后执行");
}

注解如下:

1
2
@Bean(value="user0",name="user0",initMethod="initUser",destroyMethod="destroyUser")

运行如下:

初始化用户bean之前执行

1
User [userName=张三, age=26]

发现销毁方法没有执行,原因是bean销魂之前程序已经结束了,可以手动close下如下:

1
2
3
4
5
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);
User bean2 = applicationContext2.getBean(User.class);
System.out.println(bean2);
// 手动执行close方法
applicationContext2.close();

运行结果如下:

初始化用户bean之前执行

1
2
User [userName=张三, age=26]
bean销毁之后执行

说明已经成功了,over