spring源码之 @Configuration 配置类处理 与 CGLIB代理

spring源码之 @Configuration 配置类处理 与 CGLIB代理

文章目录

    • spring源码之 @Configuration 配置类处理 与 CGLIB代理
      • 1.现象一
      • 2.现象二
      • 3.原因

先说结论:
Spring 环境中,若配置类添加了 @Configuration full 的全注解, Spring 则会对 该配置类做 cglib 的代理。

1.现象一

测试程序:

  public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext =
                new AnnotationConfigApplicationContext();
        applicationContext.register(AppConfig.class);
        applicationContext.refresh();
        Object appConfig = applicationContext.getBean("appConfig");

    }

AppConfig 类,此处加了两个注解,@Configuration @ComponentScan
其中涉及到 CONFIGURATION_CLASS_ATTRIBUTE : fulllite 的概念。

@Configuration
@ComponentScan("com.popcivilar.code.spring.mockaop")
public class AppConfig {
}

配置类保留 @Configuration
spring源码之 @Configuration 配置类处理 与 CGLIB代理_第1张图片
配置类移除 @Configuration

配置类交由 Spring管理后,会根据 CONFIGURATION_CLASS_ATTRIBUTE的 类别决定是否对该类进行cglib代理。

2.现象二

AppConfig 类中添加两个方法:在fruit() 中调用userDao2(),一般来说,容器在初始化时,会对加了@Bean注解的方法进行实例化生成beanuserDao2()方法应该会执行2次。

    @Bean
    public UserDao2 userDao2(){
        return new UserDao2();
    }

    @Bean
    public Fruit fruit(){
        userDao2();
        return new Apple();
    }
  • AppConfig 未添加 @Configuration,则该类不会被cglib代理,执行userDao2()方法,确实会被执行两次。
  • 相反,添加 @ConfigurationAppconfigcglib代理,第一次执行userDao2()被执行时,确实去new了一个对象,但在fruit()中再次调用userDao2(),则不会new出对象。

3.原因

当一个类上面加了注解 @Configuration 时候,初始化ApplicationContext 的时候, invokeBeanDefinitionRegistryPostProcessors 方法解析此类,则从map里面遍历 BeanDefinition ,判断当前BeanDefinitionmap里面的属性key -configurationClass 值是full还是lite,刚开始扫描时候为null,则继续解析,判断若果加了@Configuration configurationClass 的值设置为full

接下来 调用 invokeBeanFactoryPostPorcessors ,此方法遍历所有的BeanFactoryPostPorcessors(包括自定义的可以修改spring工厂) 在遍历中会调用到enhanceConfigurationClass ,此方法中获取regist里面的所有类,遍历判断 isfullConfigurationClass 是否为full,若为full,存到一个LinkedHashMap 中,为lite则不存,若mapnull ,则返回之后直接new 对象;若map 不为null,则去完成cglib代理的实现。

cglib代理的实现:调用enhance() ,判断该类是否实现EnhancedConfiguration ,完成代理则会让该类去实现此接口;若没有被代理则去实现代理 enhancer.create()创建代码对象。

cglib创建代理类时,具体代码如下:

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(configSuperClass); //将配置类设置成父类,cglib基于继承
		enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); //将其的接口设置为EnhancedConfiguration加强功能
		enhancer.setUseFactory(false);
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
		// 加强点 BeanMethodInterceptor   BeanFactoryAwareMethodInterceptor  内部执行 intercept(),在方法前后进行拦截
		enhancer.setCallbackFilter(CALLBACK_FILTER);
		enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
		return enhancer;
	}

CALLBACK_FILTER主要为 BeanMethodInterceptorBeanFactoryAwareMethodInterceptor,都是私有静态内部类。

private static final Callback[] CALLBACKS = new Callback[] {
		new BeanMethodInterceptor(),//逻辑
		new BeanFactoryAwareMethodInterceptor(),//设置属性 $$beanFactory
		NoOp.INSTANCE
};

BeanMethodInterceptor.intercept() 逻辑:
关键点:ConfigurationClassEnhancer.BeanMethodInterceptor.isCurrentlyInvokedFactoryMethod()
spring源码之 @Configuration 配置类处理 与 CGLIB代理_第2张图片

spring源码之 @Configuration 配置类处理 与 CGLIB代理_第3张图片

你可能感兴趣的:(Spring,Java)