之前我们都是通过xml的方式定义bean,里面会写很多bean元素,然后spring启动的时候,就会读取bean.xml配置文件,然后解析这些配置,然后会将这些bean注册到spring容器中,供使用者使用。
jdk1.5里面有了注解的功能,spring就将注解加了进来,让我们可以通过注解的方式来定义bean,用起来能达到xml中定义bean一样的效果,并且更简洁一些,这里面需要用到的注解就@Configuration注解和@Bean注解。
1,@Configuration注解
@Configuration这个注解可以加在类上,让这个类的功能等同于一个bean.xml配置文件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
使用:
@Configuration
public class config {
}
相当于如下的bean.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>
使用bean.xml时我们都是使用ClassPathXmlApplicationContext 来加载bean.xml
String beanXml = "bean.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
使用@Configuration我们使用AnnotationConfigApplicationContext来加载
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(config.class);
}
}
上面的config类中没有任何内容,相当于一个空的xml配置文件,此时我们要在config类中注册bean,那么我们就要用到@Bean注解。
小结:
- 在类上使用@Configuration注解
- 通过AnnotationConfigApplicationContext容器来加@Configuration注解修饰的类
2,@Bean注解
这个注解类似于bean.xml配置文件中的bean元素,用来在spring容器中注册一个bean。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
/** @deprecated */
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}
@Bean注解用在方法上,表示通过方法来定义一个bean,默认将方法名称作为bean名称,将方法返回值作为bean对象,注册到spring容器中。
来个案列:
@Configuration
public class Config {
//bean名称为方法默认值:car
@Bean
public Car car(){
return new Car();
}
//bean名称通过value指定了:carBean
@Bean("carBean")
public Car car2(){
return new Car();
}
//bean名称为:car3Bean,2个别名:["car3BeanAlias1", "car3BeanAlias2"]
@Bean({"car3Bean", "car3BeanAlias1", "car3BeanAlias2"})
public Car Car3() {
return new Car();
}
}
测试输出:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String name : context.getBeanDefinitionNames()) {
String[] aliases = context.getAliases(name);
System.out.println(String.format("bean名称:%s,别名:%s,bean对象:%s",
name,
Arrays.asList(aliases),
context.getBean(name)));
}
}
}
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@64c87930
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@400cff1a
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@275710fc
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@525f1e4e
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@75f9eccc
bean名称:config,别名:[],bean对象:com.chen.Config$$EnhancerBySpringCGLIB$$fa7a250c@52aa2946
bean名称:car,别名:[],bean对象:com.chen.Car@4de5031f
bean名称:carBean,别名:[],bean对象:com.chen.Car@67e2d983
bean名称:car3Bean,别名:[car3BeanAlias1, car3BeanAlias2],bean对象:com.chen.Car@5d47c63f
可以看出,有个名称为config的bean,是Config这个类型,所以被@Configuration修饰的类,也被注册到spring容器中了
3,去掉@Configuration会怎么样
我们把Config的@Configuration去掉
public class Config {
//bean名称为方法默认值:car
@Bean
public Car car(){
return new Car();
}
//bean名称通过value指定了:carBean
@Bean("carBean")
public Car car2(){
return new Car();
}
//bean名称为:car3Bean,2个别名:["car3BeanAlias1", "car3BeanAlias2"]
@Bean({"car3Bean", "car3BeanAlias1", "car3BeanAlias2"})
public Car Car3() {
return new Car();
}
}
直接运行和加了@Configuration对比:
加了@Configuration:
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@64c87930
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@400cff1a
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@275710fc
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@525f1e4e
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@75f9eccc
bean名称:config,别名:[],bean对象:com.chen.Config$$EnhancerBySpringCGLIB$$fa7a250c@52aa2946
bean名称:car,别名:[],bean对象:com.chen.Car@4de5031f
bean名称:carBean,别名:[],bean对象:com.chen.Car@67e2d983
bean名称:car3Bean,别名:[car3BeanAlias1, car3BeanAlias2],bean对象:com.chen.Car@5d47c63f
不加@Configuration:
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@23fe1d71
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@28ac3dc3
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@32eebfca
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@4e718207
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@1d371b2d
bean名称:config,别名:[],bean对象:com.chen.Config@543c6f6d
bean名称:car,别名:[],bean对象:com.chen.Car@13eb8acf
bean名称:carBean,别名:[],bean对象:com.chen.Car@51c8530f
bean名称:car3Bean,别名:[car3BeanAlias1, car3BeanAlias2],bean对象:com.chen.Car@7403c468
可以发现我们的Config配置类不一样了:
加了@Configuration:
bean名称:config,别名:[],bean对象:com.chen.Config$$EnhancerBySpringCGLIB$$fa7a250c@52aa2946
不加@Configuration:
bean名称:config,别名:[],bean对象:com.chen.Config@543c6f6d
- 可以看出:有没有@Configuration注解,@Bean都会起效,都会将@Bean修饰的方法作为bean注册到容器中
- 被@Configuration修饰的bean最后输出的时候带有EnhancerBySpringCGLIB的字样,而没有@Configuration注解的bean没有Cglib的字样;有EnhancerBySpringCGLIB字样的说明这个bean被cglib处理过的,变成了一个代理对象。
4,加不加@Configuration的区别
通常情况下,bean之间是有依赖关系的,创建个有依赖关系的bean,通过这个案例可以看出根本的区别。
public class Server1 {
}
public class Server2 {
private Server1 server1;
public Server2(Server1 server1){
this.server1 = server1;
}
@Override
public String toString() {
return "Server2{" +
"server1=" + server1 +
'}';
}
}
@Configuration
public class Config {
@Bean
public Server1 server1() {
System.out.println("创建Server1"); //@0
return new Server1();
}
@Bean
Server2 server2_1() {
System.out.println("创建Server2_1");
Server1 serviceA = this.server1(); //@1
return new Server2(serviceA);
}
@Bean
Server2 server2_2() {
System.out.println("创建server2_2");
Server1 serviceA = this.server1(); //@2
return new Server2(serviceA);
}
}
测试输出:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String name : context.getBeanDefinitionNames()) {
//别名
String[] aliases = context.getAliases(name);
System.out.println(String.format("bean名称:%s,别名:%s,bean对象:%s",
name,
Arrays.asList(aliases),
context.getBean(name)));
}
}
}
创建Server1
创建Server2_1
创建server2_2
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@55040f2f
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@64c87930
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@400cff1a
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@275710fc
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@525f1e4e
bean名称:config,别名:[],bean对象:com.chen.Config$$EnhancerBySpringCGLIB$$f7271c@75f9eccc
bean名称:server1,别名:[],bean对象:com.chen.Server1@52aa2946
bean名称:server2_1,别名:[],bean对象:Server2{server1=com.chen.Server1@52aa2946}
bean名称:server2_2,别名:[],bean对象:Server2{server1=com.chen.Server1@52aa2946}
- 前三行可以看出,被@Bean修饰的方法都只被调用了一次。
- 最后三行中可以看出都是同一个server1对象,都是Server1@52aa2946这个实例
被@Configuration修饰的类,spring容器中会通过cglib给这个类创建一个代理,代理会拦截所有被@Bean修饰的方法,默认情况(bean为单例)下确保这些方法只被调用一次,从而确保这些bean是同一个bean,即单例的。
我们把@Configuration去掉再运行输出:
创建Server1
创建Server2_1
创建Server1
创建server2_2
创建Server1
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@74a10858
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@23fe1d71
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@28ac3dc3
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@32eebfca
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@4e718207
bean名称:config,别名:[],bean对象:com.chen.Config@1d371b2d
bean名称:server1,别名:[],bean对象:com.chen.Server1@543c6f6d
bean名称:server2_1,别名:[],bean对象:Server2{server1=com.chen.Server1@13eb8acf}
bean名称:server2_2,别名:[],bean对象:Server2{server1=com.chen.Server1@51c8530f}
可以看到
- server1()方法被调用了3次
- Config这个bean没有代理效果了
- 最后3行可以看出,几个server1对象都是不一样的
总结
- @Configuration注解修饰的类,会被spring通过cglib做增强处理,通过cglib会生成一个代理对象,代理会拦截所有被@Bean注解修饰的方法,可以确保一些bean是单例的
- 不管@Bean所在的类上是否有@Configuration注解,都可以将@Bean修饰的方法作为一个bean注册到spring容器中