京东面试,这个问题让我与 50 万擦肩而过,帮忙看看,mysql 下载教程 window10
我:看着面试官,犹豫了 10 秒钟,说:不加 @Configuration 通过 @Bean 注解也可以注册 bean
面试官:你确定可以注册?
我:嗯。。。。嗯。。。。嗯,我确定可以注册
面试官:那加不加到底有什么区别呢?
我:好像没有什么区别啊
面试官:好像没区别。。。。先回去等通知吧!
结果可想而知,没戏了!
回去之后立即看 spring 的源码,终于搞清了这个问题,原来加不加 @Configuration 还是有相当大的区别的。
下面我们就来看看 @Configuration 注解到底是干什么的,加不加到底有什么区别,下次你们去面试的时候就可以给面试官吹吹了。
之前我们都是通过 xml 的方式定义 bean,里面会写很多 bean 元素,然后 spring 启动的时候,就会读取 bean xml 配置文件,然后解析这些配置,然后会将这些 bean 注册到 spring 容器中,供使用者使用。
jdk1.5 里面有了注解的功能,spring 也没闲着,觉得注解挺好用的,就将注解加了进来,让我们通过注解的方式来定义 bean,用起来能达到 xml 中定义 bean 一样的效果,并且更简洁一些,这里面需要用到的注解就有 @Configuration 注解和 @Bean 注解。
@Configuration 注解
用法
@Configuration 这个注解可以加在类上,让这个类的功能等同于一个 bean xml 配置文件,如下:
@Configuration
public?class?ConfigBean?{
}
上面代码类似于下面的 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
</beans>
通过
AnnotationConfigApplicationContext 来加载 @Configuration 修饰的类,如下:
AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(ConfigBean.class);
此时 ConfigBean 类中没有任何内容,相当于一个空的 xml 配置文件,此时我们要在 ConfigBean 类中注册 bean,那么我们就要用到 @Bean 注解了。
总结一下
@Configuration 使用步骤:
在类上使用 @Configuration 注解
通过 AnnotationConfigApplicationContext 容器来加 @Configuration 注解修饰的类
@Bean 注解
用法
这个注解类似于 bean xml 配置文件中的 bean 元素,用来在 spring 容器中注册一个 bean。
@Bean 注解用在方法上,表示通过方法来定义一个 bean,默认将方法名称作为 bean 名称,将方法返回值作为 bean 对象,注册到 spring 容器中。
如:
@Bean
public?User?user1()?{
return?new?User();
}
@Bean 注解还有很多属性,我们来看一下其源码:
@Target({ElementType.METHOD,?ElementType.ANNOTATION_TYPE})?//@1
@Retention(RetentionPolicy.RUNTIME)
@Documented
public?@interface?Bean?{
@AliasFor("name")
String[]?value()?default?{};
@AliasFor("value")
String[]?name()?default?{};
@Deprecated
Autowire?autowire()?default?Autowire.NO;
boolean?autowireCandidate()?default?true;
String?initMethod()?default?"";
String?destroyMethod()?default?AbstractBeanDefinition.INFER_METHOD;
}
@1:说明这个注解可以用在方法和注解类型上面。
每个参数含义:
value 和 name 是一样的,设置的时候,这 2 个参数只能选一个,原因是 @AliasFor 导致的
@AliasFor 这个注解不清楚的可以看这个文章:详解注解
value:字符串数组,第一个值作为 bean 的名称,其他值作为 bean 的别名
autowire:这个参数上面标注了 @Deprecated,表示已经过期了,不建议使用了
autowireCandidate:是否作为其他对象注入时候的
候选 bean,之前的文章中专门介绍过这个属性,不清楚的可以去看看:autowire-candidate 详解
initMethod:bean 初始化的方法,这个和生命周期有关,以后详解
destroyMethod:bean 销毁的方法,也是和生命周期相关的,以后详解
案例
User 类
package?com.javacode2018.lesson001.demo20;
public?class?User?{
}
Bean 配置类:ConfigBean
package?com.javacode2018.lesson001.demo20;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
@Configuration
public?class?ConfigBean?{
//bean 名称为方法默认值:user1
@Bean
public?User?user1()?{
return?new?User();
}
//bean 名称通过 value 指定了:user2Bean
@Bean("user2Bean")
public?User?user2()?{
return?new?User();
}
//bean 名称为:user3Bean,2 个别名:[user3BeanAlias1,user3BeanAlias2]
@Bean({"user3Bean",?"user3BeanAlias1",?"user3BeanAlias2"})
public?User?user3()?{
return?new?User();
}
}
上面通过 @Bean 注解定义了 3 个 bean,比较简单
来个测试类
package?com.javacode2018.lesson001.demo20;
import?org.junit.Test;
import?org.springframework.context.annotation.AnnotationConfigApplicationContext;
import?java.util.Arrays;
public?class?ConfigurationTest?{
@Test
public?void?test1()?{
AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(ConfigBean.class);//@1
for?(String?beanName?:?context.getBeanDefinitionNames())?{
//别名
String[]?aliases?=?context.getAliases(beanName);
System.out.println(String.format("bean 名称:%s,别名:%s,bean 对象:%s",
beanName,
Arrays.asList(aliases),
context.getBean(beanName)));
}
}
}
@1:通过
AnnotationConfigApplicationContext 来加载配置类 ConfigBean,会将配置类中所有的 bean 注册到 spring 容器中
for 循环中输出了 bean 名称、别名、bean 对象
运行 test1 方法输出
bean 名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean 对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@3bd82cf5
bean 名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean 对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@544fa968
bean 名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean 对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@247bddad
bean 名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean 对象:org.springframework.context.event.EventListenerMethodProcessor@d35dea7
bean 名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean 对象:org.springframework.context.event.DefaultEventListenerFactory@7770f470
bean 名称:configBean,别名:[],bean 对象:com.javacode2018.lesson001.demo20.ConfigBean
dde45976@5e5d171f
bean 名称:user1,别名:[],bean 对象:com.javacode2018.lesson001.demo20.User@24313fcc
bean 名称:user2Bean,别名:[],bean 对象:com.javacode2018.lesson001.demo20.User@7d20d0b
bean 名称:user3Bean,别名:[user3BeanAlias2,?user3BeanAlias1],bean 对象:com.javacode2018.lesson001.demo20.User@77f1baf5
上面的输出,我们主要关注与最后 4 行,前面的可以先忽略。
从输出中可以看出,有个名称为 configBean 的 bean,正是 ConfigBean 这个类型,可以得出,被 @Configuration 修饰的类,也被注册到 spring 容器中了
最后 3 行输出就是几个 User 的 bean 对象了。
上面的用法应该很多人都比较熟悉,下面的属于重点了。
去掉 @Configuration 会怎样?
我们来看一下没有 @Configuration 的时候,什么效果。
新建一个 ConfigBean1 类
内容和 ConfigBean 类一样,只是将 @Configuration 注解去掉了,如下:
public?class?ConfigBean1?{
//bean 名称为方法默认值:user1
@Bean
public?User?user1()?{
return?new?User();
}
//bean 名称通过 value 指定了:user2Bean
@Bean("user2Bean")
public?User?user2()?{
return?new?User();
}
//bean 名称为:user3Bean,2 个别名:[user3BeanAlias1,user3BeanAlias2]
@Bean({"user3Bean",?"user3BeanAlias1",?"user3BeanAlias2"})
public?User?user3()?{
return?new?User();
}
}
来个测试用例 test2
代码类似于 test1,给 spring 容器传递 ConfigBean1
@Test
public?void?test2()?{
AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(ConfigBean1.class);
for?(String?beanName?:?context.getBeanDefinitionNames())?{
//别名
String[]?aliases?=?context.getAliases(beanName);
System.out.println(String.format("bean 名称:%s,别名:%s,bean 对象:%s",
beanName,
Arrays.asList(aliases),
context.getBean(beanName)));
}
}
运行输出
bean 名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean 对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@333291e3
bean 名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean 对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@479d31f3
bean 名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean 对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@40ef3420
bean 名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean 对象:org.springframework.context.event.EventListenerMethodProcessor@498d318c
bean 名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean 对象:org.springframework.context.event.DefaultEventListenerFactory@6e171cd7
bean 名称:configBean1,别名:[],bean 对象:com.javacode2018.lesson001.demo20.ConfigBean1@402bba4f
bean 名称:user1,别名:[],bean 对象:com.javacode2018.lesson001.demo20.User@795cd85e
bean 名称:user2Bean,别名:[],bean 对象:com.javacode2018.lesson001.demo20.User@59fd97a8
bean 名称:user3Bean,别名:[user3BeanAlias2,?user3BeanAlias1],bean 对象:com.javacode2018.lesson001.demo20.User@f5ac9e4
分析结果
我们将 2 个输出的最后 4 行拿来对比一下:
有 @Configuration 注解的
bean 名称:configBean,别名:[],bean 对象:com.javacode2018.lesson001.demo20.ConfigBean
dde45976@5e5d171f
bean 名称:user1,别名:[],bean 对象:com.javacode2018.lesson001.demo20.User@24313fcc
bean 名称:user2Bean,别名:[],bean 对象:com.javacode2018.lesson001.demo20.User@7d20d0b
bean 名称:user3Bean,别名:[user3BeanAlias2,?user3BeanAlias1],bean 对象:com.javacode2018.lesson001.demo20.User@77f1baf5
评论