# 一,问题描述
在项目中,通常都会配置一个或者多个加了 @Configuration 注解的配置类,那么 @Configuration 这个注解到底有神马作用勒?看下面案例
Config 类
@ComponentScan("com") | |
public class SpringMVCConfig { | |
// 该方法返回是一个 bean 所以需要加上 @Bean 注解 | |
@Bean | |
public BookDao getBookDao(){ | |
return new BookDao(); | |
} | |
} |
Dao 类
public class BookDao { | |
public BookDao(){ | |
System.out.println("bookDao constructor runing..."); | |
} | |
} |
test 测试代码
public class SpringTest { | |
@Test | |
public void test(){ | |
// 获取 IoC 容器 | |
AnnotationConfigApplicationContext a = new AnnotationConfigApplicationContext(SpringMVCConfig.class); | |
} | |
} | |
//--------------------------Result-------------------------- | |
bookDao constructor runing... |
执行上面的代码,我们会发现当我们不加 @Configuration 这个注解的时候我们的 BookDao 这个类还是还是会被实例化,也会打印 bookDao constructor runing...。我们的 spring 环境也可以正常运行。
再看下面案例:
config 类
public class SpringMVCConfig { | |
@Bean | |
public BookDao getBookDao(){ | |
return new BookDao(); | |
} | |
@Bean | |
public BookDao1 getBookDao1(){ | |
getBookDao(); | |
return new BookDao1(); | |
} | |
} |
Dao 和 Dao1 类
//BookDao | |
public class BookDao { | |
public BookDao(){ | |
System.out.println("bookDao constructor runing..."); | |
} | |
} | |
//BookDao1 | |
public class BookDao1 { | |
public BookDao1(){ | |
System.out.println("BookDao1 constructor runing..."); | |
} | |
} |
test 测试代码
public class SpringTest { | |
@Test | |
public void test(){ | |
AnnotationConfigApplicationContext a = new AnnotationConfigApplicationContext(SpringMVCConfig.class); | |
} | |
} | |
//-------------------------Result------------------------- | |
bookDao constructor runing... | |
bookDao constructor runing... | |
BookDao1 constructor runing... |
不加 @Configuration 打印的结果 bookDao 打印了两次,那我们加上 @Configuration 看情况
@Configuration | |
public class SpringMVCConfig { | |
@Bean | |
public BookDao getBookDao(){ | |
return new BookDao(); | |
} | |
@Bean | |
public BookDao1 getBookDao1(){ | |
getBookDao(); | |
return new BookDao1(); | |
} | |
} | |
//----------------------------Result---------------------------- | |
bookDao constructor runing... | |
BookDao1 constructor runing... |
加上 @Configuration 后 bookDao 只打印了一次
# 分析
从表面来看,当我们不加 @Configuration 注解的时候,我们的 BookDao 会被实例化两次,这违背了我们 spring 默认单例的设计原则,当加上我们的 @Configuration 注解的时候,BookDao 只被实例化了一次。那么其底层到底做了什么,让我们来深追一下 spring 源码吧。
当我们解析 beanAppcofig 的时候,会给它的一个属性标识为 Full,表明它是一个全注解类。
然后在我们调用 ConfigurationClassPostProcessor.postProcessBeanFactory () 方法的时候会去判断我们的 bean 工厂当中是否有 bean 需要进行 cglib 代理。
然后遍历 configBeanDefs 这个 map
cglib 代理主要是对我们的方法进行拦截增强;当我们执行 AppConfig 中的方法的时候会去执行 cglib 代理类中的代理方法,主要就是 callBacks 中的方法。
isCurrentlyInvokedFactoryMethod (beanMethod)) 会判断我们的执行方法和我们的调用方法是否是同一个;如果是同一个就调用父类的方法进行 new;如果不是就调用 $$beanFactory.getBean () 获取。
# 总结
加上 @Configuration 注解主要是给我们的类加上了 cglib 代理。在执行我们的配置类的方法时,会执行 cglib 代理类中的方法,其中有一个非常重要的判断,当我们的执行方法和我们的调用方法是同一个方法时,会执行父类的方法 new(cglib 代理基于继承);当执行方法和调用方法不是同一个方法时会调用 beanFactory.getBean 获取。