postProcessBeanFactory()
此方法的作用用一句话可概括为:为Full模式的Bean使用CGLIB做字节码提升,确保最终生成的是代理类实例放进容器内。
ConfigurationClassPostProcessor: @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { int factoryId = System.identityHashCode(beanFactory); // 防止重复处理 if (this.factoriesPostProcessed.contains(factoryId)) { throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory); } this.factoriesPostProcessed.add(factoryId); // 在执行postProcessBeanDefinitionRegistry方法的时就已经将 // 这个id添加到registriesPostProcessed集合中了 // 所以到这里就不会再重复执行配置类的解析了(解析@Import、@Bean等) if (!this.registriesPostProcessed.contains(factoryId)) { processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } // 从名字上看,这个方法应该就是为配置类创建代理用的喽 enhanceConfigurationClasses(beanFactory); // 添加了一个后置处理器 它是个SmartInstantiationAwareBeanPostProcessor // 它不是本文重点,略 beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); }
达到这一步之前,已经完成了bd的收集和标记(见上一步)。对bd进行实例化之前,针对于Full模式的配置类这步骤里会做增强处理,那就是enhanceConfigurationClasses(beanFactory)这个方法。
enhanceConfigurationClasses(beanFactory)
对一个BeanFactory进行增强,先查找配置类BeanDefinition,再根据Bean定义信息(元数据信息)来决定配置类是否应该被ConfigurationClassEnhancer增强。具体处理代码如下:
ConfigurationClassPostProcessor: public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) { // 最终需要做增强的Bean定义们 Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>(); for (String beanName : beanFactory.getBeanDefinitionNames()) { BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE); ... // 省略其它情况以及异常情况的处理代码 // 如果是Full模式,才会放进来 if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) { configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } if (configBeanDefs.isEmpty()) { // nothing to enhance -> return immediately return; } // ConfigurationClassEnhancer就是对配置类做增强操作的核心类,下面详解 ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(); // 对每个Full模式的配置类,一个个做enhance()增强处理 for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) { AbstractBeanDefinition beanDef = entry.getValue(); // 如果代理了@Configuration类,则始终代理目标类 // 该属性和自动代理时是相关的,具体参见Spring的自动代理章节描述 beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); // CGLIB是给父类生成子类对象的方式实现代理,所以这里指定“父类”类型 Class<?> configClass = beanDef.getBeanClass(); // 做增强处理,返回enhancedClass就是一个增强过的子类 Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader); // 不相等,证明代理成功,那就把实际类型设置进去 // 这样后面实例化配置类的实例时,实际实例化的就是增强子类喽 if (configClass != enhancedClass) { beanDef.setBeanClass(enhancedClass); } } }
值得注意的是,虽然此方法被设计为public的,但是只被一处使用到。Spring这么做是为了给提供钩子,方便容器开发者做扩展时使用
步骤总结:
- 从BeanFactory拿出所有的bd信息,一个个判断
- 如果是配置类并且是Full模式,就先存储起来,后面会对它做字节码提升。最终如果一个Full模式的配置类都木有,那直接return,此方法结束。否则继续
- 对收集到的每一个 Full模式的配置类,使用ConfigurationClassEnhancer增强器进行字节码提升,生成一个CGLIB子类型
- 小细节:此处显示标注了AOP自动代理为:始终代理目标类
- 把CGLIB生成的子类型设置到元数据里去:beanDef.setBeanClass(enhancedClass)。这样Spring在最后实例化Bean时,实际生成的是该代理类型的实例,从而达到代理/增强的目的
该方法执行完成后,执行“结果”我截了张图,供以参考:
这段代码是不难的,理解起来十分简单。但是,我们仍旧还只知道结果,并不清楚原因。凭它还无法解释上文中两个case的现象,所以我们应该端正态度继续深入,看看ConfigurationClassEnhancer增强器到底做了什么事。
在介绍ConfigurationClassEnhancer之前,希望你对CGLIB的使用有那么一定的了解,这样会轻松很多。当然不必过于深究(否则容易怀疑人生),但应该能知道如何使用Enhancer增强器去增强/代理目标类,如何写拦截器等。
因为之前文章介绍过了CGLIB的基本使用,限于篇幅,此处就不再啰嗦。
ConfigurationClassEnhancer源码分析
得打起精神了,因为接下来才是本文之精华,让你出彩的地方。
@since 3.0。通过生成一个CGLIB子类来增强@Configuration类与Spring容器进行交互,每个这样的@Bean方法都会被生成的子类所复写。这样子当遇到方法调用时,才有可能通过拦截从而把方法调用引回容器,通过名称获得相应的Bean。
建立在对CGLIB的使用有一定了解的基础上,再来阅读本文会变得轻松许多。该类有且仅有一个 public方法,如下所示:
ConfigurationClassEnhancer: public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) { // 如果已经实现了该接口,证明已经被代理过了,直接返回呗~ if (EnhancedConfiguration.class.isAssignableFrom(configClass)) { return configClass; } // 若没被代理过。就先调用newEnhancer()方法创建一个增强器Enhancer // 然后在使用这个增强器,生成代理类字节码Class对象 Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader)); return enhancedClass; }
巨简单有没有。
为何Spring的源码是开源软件的范本?因为它各种封装、设计模式用得都非常好,甚至对初学者都是友好的,所以说Spring易学难精。
该public方法的核心,在下面这两个个私有方法上。
newEnhancer()和createClass()
创建一个新的CGLIB Enhancer
实例,并且做好相应配置。
ConfigurationClassEnhancer: private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) { Enhancer enhancer = new Enhancer(); // 目标类型:会以这个作为父类型来生成字节码子类 enhancer.setSuperclass(configSuperClass); // 让代理类实现EnhancedConfiguration接口,这个接口继承了BeanFactoryAware接口 enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); // 设置生成的代理类不实现org.springframework.cglib.proxy.Factory接口 enhancer.setUseFactory(false); // 设置代理类名称的生成策略:Spring定义的一个生成策略 // 你名称中会有“BySpringCGLIB”字样 enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader)); // 设置拦截器/过滤器 enhancer.setCallbackFilter(CALLBACK_FILTER); enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes()); return enhancer; } // 使用增强器生,成代理类的字节码对象 private Class<?> createClass(Enhancer enhancer) { Class<?> subclass = enhancer.createClass(); Enhancer.registerStaticCallbacks(subclass, CALLBACKS); return subclass; }
Enhancer是CGLIB的最核心API,通过方法名应该基本清楚了每一步都有什么作用吧。
Enhancer属于CGLIB的核心API,但你发现它的包名是xxx.springframework.xxx 。这是因为CGLIB在Spring内太常用了(强依赖),因此Spring索性就自己fork了一份代码过来~