【Spring源码】@Configuration和@Compon
@Configuration
image该注解派生自@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();
}
可以看到输出结果是同一个对象
image如果是@Component中的类调用@Bean修饰的方法,每次都会创建并返回不同的对象
源码解析
ConfigurationClassPostProcessor.postProcessBeanFactory(beanFactory)
1.对容器中已有的BeanDefinition 解析@Component / @ComponentScan / @Import / @ImportResource /@Bean
image这里对上述注解的解析在其他笔记里有,其中和处理@Configuration有关的地方
先判断类上是否有@Component / @ComponentScan / @Import / @ImportResource,获取方法上有@Bean注解,判断通过才会走下面的解析流程
image其中如果有@Configuration注解,在设置BD的这个属性为full
image其他注解则会设置该属性为lite.
2. 处理@Configuration修饰的类
image首先取出容器中所有的BeanDefinition,并遍历每一个,获取上一步中设置的那个属性,如果是@Configuration,那么这个属性值则为full
可以看到测试代码中的ConfigurationBean的beanDefinition的该属性值为full
image那么会把这个bean装到一个放类上有@Configuration修饰的beanDefinition的map中
image循环该map容器,为每一个类上有@Configuration修饰的beanDefinition对应的类型创建代理类,并设置到beanDefinition的BeanClass属性中,那么在spring实例化该bean的时候,就会创建出一个代理对象。
image返回单例对象的逻辑则是通过代理的方式实现的.看看代理的逻辑
@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对象
image image- newEnhancer方法创建一个增强器,并设置拦截器数组,并且设置代理类实现接口EnhancedConfiguration,EnhancedConfiguration又继承了BeanFactoryAware,用来在代理对象中持有BeanFactory对象
拦截器数组Filter会持有拦截器数组,
image这里有两个试用的拦截器
- BeanMethodInterceptor:用来增强@Bean的方法
-
BeanFactoryAwareMethodInterceptor用来设置BeanFactory
image
具体使用哪里拦截器进行增强,由拦截器数组的accept方法来决定。
具体的代理逻辑
1. 拦截器的选择ConditionalCallbackFilter.accept方法
image遍历所有的拦截器,调用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类型,那么这里很有可能会再生成一次代理
判断条件:如果能以&+beanName,beanName都能拿bean工厂拿到实例则说明,说明该@Bean方法返回的是一个实现了FactoryBean接口的类,是的话要返回代理对象,对getObject方法进行代理,以保证返回的是同一个对象
imageimage
- 首先判断实现FactoryBean接口的bean类是否是final,或者getObject是否是final,如果是的话,会选择jdk动态代理(cglib无法代理final类,无法增强final修饰的方法,因为是使用继承被代理类的方式),并且要求返回的是一个接口,这个接口可以认为是FactoryBean接口,因为jdk是采用实现被代理类的同一个接口的方式来对被代理类实现接口的方法进行代理。这里被代理类实现的是FactoryBean接口。
示例代码:
@Configuration public class ConfigurationBean { @Bean public FactoryBean myFactoryBean(){ // MyFactoryBean实现了FactoryBean接口 return new MyFactoryBean(); } public final class MyFactoryBean implements FactoryBean { @Override public final Object getObject() { return new Man(); } @Override public Class<?> getObjectType() { return Man.class; } } public void printMyFactoryBeanGetObject() throws Exception { System.out.println("ConfigurationBean手动调用MyFactoryBean.getObject方法:" + myFactoryBean().getObject()); System.out.println("ConfigurationBean手动调用MyFactoryBean.getObject方法:" + myFactoryBean().getObject()); } }
这样的写法会使用jdk动态代理,代理类会同样会实现FactoryBean接口,增强getObject方法 : 从bean工厂返回单例对象,不会多次创建实例
imageimage image
- 否则会使用cglib代理
其中的拦截类是一个匿名对象,代理的逻辑同样是bean工厂返回单例对象
image
总结 : 如果该@Bean方法的beanName对应的bean实现了Factory接口,会再次返回一个代理对象,根据类和getObjct方法是否final以及方法返回值是否是FactoryBean接口选择代理方法,是选择jdk,否则cglib,增强的逻辑都是在执行getObject方法的时候从bean工厂返回单例bean.
- 判断是否是spring容器使用@Bean方法创建bean,是的话,直接调被代理类(@Configuration类)的@Bean方法
image判断是否是spring容器创建bean而调用@Bean方法
imageimage image
- 从ThreadLocal中拿当前正在用@Bean(FactoryMethod)方式创建实例的Method,ioc创建bean,如果是使用FactoryMethod,会在调用FactoryMethod之前往ThreadLocal放FactoryMethod,反射调用完,remove
这里反射调用就有可能走进@Configuration的bean的对象对@Bean方法的增强,就是上面的BeanMethodInterceptor的intercept方法中,从而可以从ThreadLocal中拿到FactoryMethod,进行判断
image
- 判断ThreadLocal是否存在FactoryMethod且方法名称,参数列表是否一样
- 如果手动调用@Configuration的bean的@Bean方法,会走这里
image也是从BeanFactory中返回对象,以保证始终返回的是同一个对象
image