从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被ConfigurationClassPostProcessor类进行扫描,并用于构建bean定义,初始化Spring容器。
我们先来看一下@Configuration注解的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
}
可以看到@Configuration内部又有一个@Component注解,所以,其作用和@Component注解一样,都能将一个标有@Configuration的注解类,通过Spring包扫描和解析,最终形成Spring的bean。不过Spring在内部对于加了@Configuration注解的配和加了@Component注解的类,处理起来是由差别的,如果有同学想要探究Spring对于@Configuration注解的类和其他@Component注解的类的不同,可以查看:Spring中@Configuration源码深度解析(一)和Spring中@Configuration源码深度解析(二),深入学习@Configuration注解。
Spring从3.0开始引入@Configuration是为了替代XML形式的配置文件,当你在做ssm项目的时候,使用的应该是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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd default-lazy-init="false">
<context:component-scan base-package="com.luban.factory" />
<bean id="school" class="com.luban.factory.School" >
<property name = "student" ref ="student"></property>
</bean>
<bean id="student" class="com.luban.factory.Student"/>
</beans>
使用@Configuration来替换xml配置类如下:
@Configuration
@ComponentScan("com.luban.factory") //对应xml里面的context:component-scan标签
public class Config {
@Bean //对应xml里面的Bean标签
public School school(){
School school = new School();
//给school对象设置属性
school.setStudent(student());
return school;
}
@Bean
public Student student(){
return new Student();
}
}
他们所完成的事情是相同的,在方法上使用@Bean就相当于xml里面的bean标签,@ComponentScan用来指定需要扫描的包的全路径,会把路径下在类上面标注了@Component、@Service、@Configuration等注入到容器中。
由于是作为配置类来使用,有的人该说了,既然@Configuration底层就是使用@Component注解,那么能不能不用了@Configuration了,直接用@Component注解,那下面我们来实验一下,把@Configuration换成@Component试一下:
@Component //本次使用的是@Component
@ComponentScan("com.luban.factory")
public class Config {
@Bean
public School school(){
School school = new School();
//给school对象设置属性
school.setStudent(student());
return school;
}
@Bean
public Student student(){
return new Student();
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
System.out.println(context.getBean("school"));
System.out.println(context.getBean("student"));
}
}
//打印结果:
//com.luban.factory.School@41a4555e
//com.luban.factory.Student@3830f1c0
//com.luban.factory.Student@39ed3c8d
并且该配置类能够注入到容器里面,并且也可以完成对@Bean的解析,并把@Bean标注的方法变成Bean注入到容器中。
我们再将@Component换成@Configuration试一下:
@Configuration//本次使用的是@Configuration
@ComponentScan("com.luban.factory")
public class Config {
@Bean
public School school(){
School school = new School();
//给school对象设置属性
school.setStudent(student());
return school;
}
@Bean
public Student student(){
return new Student();
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
System.out.println(context.getBean("school"));
System.out.println(context.getBean("student"));
}
}
//其打印结果
//com.luban.factory.School@6debcae2
//com.luban.factory.Student@5ba23b66
//com.luban.factory.Student@5ba23b66
发现@Component和@Configuration没什么区别啊,都能完成对Spring中Bean的管理,不过呢,你仔细看打印结果,多看两遍后你会发现,使用@Configuration注解之后,student对象是同一个,而使用@Component注解的话,student对象不是同一个,为什么加了@Configuration注解之后,student对象就是同一个呢,就只是改了一个注解而已,这是你可以知道了,肯定是加了@Configuration注解之后,Spring对其做了不同的处理,其实就是这样的,能够完成这样的工作,肯定是使用了代理,又因为这个配置类没有接口,所以可以断定,加了@Configuration之后,会使用cglib对其进行增强,然后进行方法拦截。如果想深入了解Spring对@Configuration的处理过程,请查看Spring中@Configuration源码深度解析(一)和Spring中@Configuration源码深度解析(二),深入学习@Configuration注解。
关于@Configuration有了一定的了解,就要看一下@Bean注解的使用,基本上@Configuration注解类里面都是定义@Bean注解的方法,以这样的方式往容器中注入bean,
我们来查看一下@Bean的源码:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
//指定bean的名称
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
//属性的注入模型
Autowire autowire() default Autowire.NO;
//指定init方法
String initMethod() default "";
//指定destroy方法
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
所以使用@Bean注解可以写成这样的格式:
@Bean(name = "sch",initMethod = "init" ,destroyMethod = "destroy")
public School school(){
School school = new School();
//给school对象设置属性
school.setStudent(student());
return school;
}
public class School {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public void init(){
System.out.println("执行init方法");
}
public void destroy(){
System.out.println("执行destroy方法");
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
School school = (School) context.getBean("sch"); //此时取的是sch
System.out.println(school);
context.destroy();
}
}
//其打印结果:
//执行init方法
//com.luban.factory.School@41a4555e
//执行destroy方法
注意这里指定initMethod和destroyMethod的方法,需要是School对象的方法,不能是其他对象的。
关于@Configuration的总结:
1.@Configuration标注的类会被cglib代理,从而进行增强
2.@Configuration标注的类不能使用final关键字修饰(cglib是使用继承增强需要代理的类的)
3.@Configuration标注的类在@Bean方法内部调用其他标有@Bean的方法时,会被拦截,然后直接直接从容器中进行获取,不用再次创建(就因为有这一点,所以配置类要是有@Configuration,使用其他的可能会带来不必要的错误)
4.@Configuration标注的类会被标注为full类型的,用来区分@Component、@Service等注解,增强的时候就是根据是不是full进行的。