0
点赞
收藏
分享

微信扫一扫

【Spring源码】@Configuration和@Component的区别

梦想家们 2021-09-18 阅读 60

@Configuration

该注解派生自@Component,和@Component注解有相同的功能

相同点:
  1. 可以标识该类实例被Spring-ioc容器管理
  2. 类中含有@Bean的方法,可以创建bean
不同点:
  1. 如果是由@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对象

  1. newEnhancer方法创建一个增强器,并设置拦截器数组,并且设置代理类实现接口EnhancedConfiguration,EnhancedConfiguration又继承了BeanFactoryAware,用来在代理对象中持有BeanFactory对象

拦截器数组Filter会持有拦截器数组,

这里有两个试用的拦截器

  1. BeanMethodInterceptor:用来增强@Bean的方法
  2. BeanFactoryAwareMethodInterceptor用来设置BeanFactory


具体使用哪里拦截器进行增强,由拦截器数组的accept方法来决定。

具体的代理逻辑

1. 拦截器的选择ConditionalCallbackFilter.accept方法


遍历所有的拦截器,调用match方法,返回支持增强该方法的拦截器下标

1.1. BeanFactoryAwareMethodInterceptor.match

match返回true的条件为 :

  1. 方法名为setBeanFactory
  2. 参数列表长度为1
  3. 唯一的一个参数为BeanFactory类型
  4. 方法的类为BeanFactoryAware实现类

可以看出,该拦截器的就是为了增强实现自BeanFactoryAware的setBeanFactory(BeanFactory beanFactory) 方法

1.2. BeanMethodInterceptor.match方法

match返回true的条件为 :

  1. 不是Object类的方法
  2. 不是SetBeanFactory方法
  3. 并且方法上有@Bean注解

可以看出BeanMethodInterceptor就是为了增强@BeanMethod方法

那么接下来具体的增强逻辑肯定是在两个拦截器的intercept方法中了

增强
1. BeanMethodInterceptor.match :对setBeanFactory方法的增强
  1. 用cglib为代理对象生成一个$$beanFactory属性,并将setBeanFactory(BeanFactory beanFactory)的参数反射设置给这个属性
  2. 如果被代理类已经是BeanFactoryAware实现类,那么直接调用setBeanFactory(BeanFactory beanFactory)方法

2.BeanMethodInterceptor:对@Bean的方法增强
  1. 获取BeanFactory属性,方法的返回值
  2. 获取@Bean方法定义的beanName,要么是注解中的name,要么是方法名首字母小写

  1. 如果beanName指向的bean是FactoryBean类型,那么这里很有可能会再生成一次代理

总结 : 如果该@Bean方法的beanName对应的bean实现了Factory接口,会再次返回一个代理对象,根据类和getObjct方法是否final以及方法返回值是否是FactoryBean接口选择代理方法,是选择jdk,否则cglib,增强的逻辑都是在执行getObject方法的时候从bean工厂返回单例bean.

  1. 判断是否是spring容器使用@Bean方法创建bean,是的话,直接调被代理类(@Configuration类)的@Bean方法
  1. 如果手动调用@Configuration的bean的@Bean方法,会走这里
举报

相关推荐

0 条评论