@Configuration
该注解派生自@Component,和@Component注解有相同的功能
相同点:
- 可以标识该类实例被Spring-ioc容器管理
- 类中含有@Bean的方法,可以创建bean
不同点:
- 如果是由@Configuration注解修饰的类,自身会生成一个cglib代理对象,在通过@Bean方式创建单例对象时,经过增强,会尝试从BeanFactory里返回对象,如果是第一次创建@Bean要生成的对象,才会反射调用被@Bean修饰的方法。不管是spring初次创建@Bean的对象,还是业务代码手动调用被@Bean方法修饰的方法,返回的永远是同一个被spring容器管理的对象。
实例代码
@Configuration修饰的类
@Configuration
public class ConfigurationBean {
@Bean
public Person person(){
return new Person();
}
public void printPerson(){
System.out.println(person());
}
}
测试
@Test
public void testImportAnno() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.lb.springboot_simple_starter.bean");
// 容器中获取person
System.out.println(applicationContext.getBean(Person.class));
// 手动调用ConfigurationBean.person()
ConfigurationBean configurationBean = applicationContext.getBean(ConfigurationBean.class);
configurationBean.printPerson();
}
可以看到输出结果是同一个对象
如果是@Component中的类调用@Bean修饰的方法,每次都会创建并返回不同的对象
源码解析
ConfigurationClassPostProcessor.postProcessBeanFactory(beanFactory)
1.对容器中已有的BeanDefinition 解析@Component / @ComponentScan / @Import / @ImportResource /@Bean
这里对上述注解的解析在其他笔记里有,其中和处理@Configuration有关的地方
先判断类上是否有@Component / @ComponentScan / @Import / @ImportResource,获取方法上有@Bean注解,判断通过才会走下面的解析流程
其中如果有@Configuration注解,在设置BD的这个属性为full
其他注解则会设置该属性为lite.
2. 处理@Configuration修饰的类
首先取出容器中所有的BeanDefinition,并遍历每一个,获取上一步中设置的那个属性,如果是@Configuration,那么这个属性值则为full
可以看到测试代码中的ConfigurationBean的beanDefinition的该属性值为full
那么会把这个bean装到一个放类上有@Configuration修饰的beanDefinition的map中
循环该map容器,为每一个类上有@Configuration修饰的beanDefinition对应的类型创建代理类,并设置到beanDefinition的BeanClass属性中,那么在spring实例化该bean的时候,就会创建出一个代理对象。
返回单例对象的逻辑则是通过代理的方式实现的.看看代理的逻辑
@Configuration代理类
Cglib api示例
public class CallbackFilterDemo {
public static void main(String[] args) {
// 拦截器数组
Callback[] callbacks = new Callback[] {
new MethodInterceptorImpl(), NoOp.INSTANCE
};
// 创建增强器
Enhancer enhancer = new Enhancer();
// 设置被代理类,cglib是通过继承被代理类来实现代理的
enhancer.setSuperclass(MyClass.class);
// 设置拦截类的数组
enhancer.setCallbacks(callbacks);
// 设置拦截器的Filter,该类的accept方法定义了选择哪个拦截器进行代理的逻辑
enhancer.setCallbackFilter(new CallbackFilterImpl());
// 创建代理对象
MyClass myClass = (MyClass) enhancer.create();
myClass.method();
myClass.method1();
}
private static class CallbackFilterImpl implements CallbackFilter {
// 这里会先被调到,返回拦截器数组的下标
@Override
public int accept(Method method) {
if (method.getName().equals("method"))
return 1;
else
return 0;
}
}
private static class MethodInterceptorImpl implements MethodInterceptor {
//增强逻辑
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.err.println("Before invoke " + method);
// 执行被代理方法
Object result = proxy.invokeSuper(obj, args);
System.err.println("After invoke" + method);
return result;
}
}
}
代理类的创建
Enhancer.enhance会创建出代理类的Class对象
- newEnhancer方法创建一个增强器,并设置拦截器数组,并且设置代理类实现接口EnhancedConfiguration,EnhancedConfiguration又继承了BeanFactoryAware,用来在代理对象中持有BeanFactory对象
拦截器数组Filter会持有拦截器数组,
这里有两个试用的拦截器
- BeanMethodInterceptor:用来增强@Bean的方法
-
BeanFactoryAwareMethodInterceptor用来设置BeanFactory
具体使用哪里拦截器进行增强,由拦截器数组的accept方法来决定。
具体的代理逻辑
1. 拦截器的选择ConditionalCallbackFilter.accept方法
遍历所有的拦截器,调用match方法,返回支持增强该方法的拦截器下标
1.1. BeanFactoryAwareMethodInterceptor.match
match返回true的条件为 :
- 方法名为setBeanFactory
- 参数列表长度为1
- 唯一的一个参数为BeanFactory类型
- 方法的类为BeanFactoryAware实现类
可以看出,该拦截器的就是为了增强实现自BeanFactoryAware的setBeanFactory(BeanFactory beanFactory) 方法
1.2. BeanMethodInterceptor.match方法
match返回true的条件为 :
- 不是Object类的方法
- 不是SetBeanFactory方法
- 并且方法上有@Bean注解
可以看出BeanMethodInterceptor就是为了增强@BeanMethod方法
那么接下来具体的增强逻辑肯定是在两个拦截器的intercept方法中了
增强
1. BeanMethodInterceptor.match :对setBeanFactory方法的增强
- 用cglib为代理对象生成一个$$beanFactory属性,并将setBeanFactory(BeanFactory beanFactory)的参数反射设置给这个属性
- 如果被代理类已经是BeanFactoryAware实现类,那么直接调用setBeanFactory(BeanFactory beanFactory)方法
2.BeanMethodInterceptor:对@Bean的方法增强
- 获取BeanFactory属性,方法的返回值
- 获取@Bean方法定义的beanName,要么是注解中的name,要么是方法名首字母小写
- 如果beanName指向的bean是FactoryBean类型,那么这里很有可能会再生成一次代理
总结 : 如果该@Bean方法的beanName对应的bean实现了Factory接口,会再次返回一个代理对象,根据类和getObjct方法是否final以及方法返回值是否是FactoryBean接口选择代理方法,是选择jdk,否则cglib,增强的逻辑都是在执行getObject方法的时候从bean工厂返回单例bean.
- 判断是否是spring容器使用@Bean方法创建bean,是的话,直接调被代理类(@Configuration类)的@Bean方法
- 如果手动调用@Configuration的bean的@Bean方法,会走这里