上篇文章有说到,spring对于全配置类进行了代理,保证了bean作用域的正确性,那spring是如何保证的?这篇文章我们会继续进行说明。

我们继上篇讲的全配置类和半配置类部分的源码,即以ConfigurationClassPostProcessor#


postProcessBeanFactory方法里面的enhanceConfigurationClasses方法为入口:继续进入改行代码:


spring ConfigurationProperties乱码问题 spring @configure_拦截器

进入该类:

spring ConfigurationProperties乱码问题 spring @configure_java_02

 可以看到起构造方法,大概意思如下:

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
		Enhancer enhancer = new Enhancer();
		//设置配置类
		enhancer.setSuperclass(configSuperClass);
		//是否已被代理过
		enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
		//是否使用工程
		enhancer.setUseFactory(false);
		//生成代理对象名称的命名策略
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		//给生成的代理对象中增加一个BeanFactory属性对象(使用该属性调用getBean方法获取bean)
		enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
		//增强过滤器(过滤不需要被增强的,只有符合过滤条件的方法才会被增强)(注意过滤的不是类,是方法)
		// (主要过滤@Bean方法和setBeanFactory方法)
		enhancer.setCallbackFilter(CALLBACK_FILTER);
		enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
		return enhancer;
	}

这里的过滤器主要是对setBeanFactory方法、加了@Bean注解方法做增强。我们可以点开CALLBACK_FILTER看看实例化了哪些过滤条件:

private static final Callback[] CALLBACKS = new Callback[] {

			//增强加了@Bean注解的方法
			new BeanMethodInterceptor(),

			//如果代理对象有setBeanFactory方法,该方法就是做setBeanFactory方法的回调
			//cglib动态代理类会实现一个EnhancedConfiguration接口,该接口又继承了BeanFactoryAware接口
			//BeanFactoryAware接口有个方法setBeanFactory。代理是spring会去重写这个方法。
			// 在这个方法中去调用BeanFactoryAwareMethodInterceptor实现的intercept方法
			//在intercept方法中会对代理类的属性BeanFactory对象赋初始值
			new BeanFactoryAwareMethodInterceptor(),

			NoOp.INSTANCE
	};

	private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

 这里实例化了三个,我们主要看前两个,第一个BeanMethodInterceptor主要用于增强加了@Bean

注解的方法,第二个是用于增强setBeanFactory方法(这个方法是cglib自己动态生成的,入参为BeanFactory)

我们想不着急看着两个拦截器,可以先看看ConditionalCallbackFilter 类是什么样子的:

/**
	 * 实现了CallbackFilter接口,实现方法accept,该方法又调用了isMatch方法
	 * */
	private static class ConditionalCallbackFilter implements CallbackFilter {

		private final Callback[] callbacks;

		private final Class<?>[] callbackTypes;

		public ConditionalCallbackFilter(Callback[] callbacks) {
			this.callbacks = callbacks;
			this.callbackTypes = new Class<?>[callbacks.length];
			for (int i = 0; i < callbacks.length; i++) {
				this.callbackTypes[i] = callbacks[i].getClass();
			}
		}

		@Override
		public int accept(Method method) {
			//遍历callbacks
			for (int i = 0; i < this.callbacks.length; i++) {
				Callback callback = this.callbacks[i];

				//new BeanMethodInterceptor(),new BeanFactoryAwareMethodInterceptor()都是实现了ConditionalCallback
				//调用isMatch进行过滤操作
				if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
					return i;
				}
			}
			throw new IllegalStateException("No callback available for method " + method.getName());
		}

		public Class<?>[] getCallbackTypes() {
			return this.callbackTypes;
		}
	}

可以看到,初始化的三个拦截器传进入后会进行遍历,并在回调方法accept中进行了过滤操作

我们此时点开iSMatch看看是如果进行匹配的:

此时发现有两个实现类,分别是我们之前提过的两个拦截器:

spring ConfigurationProperties乱码问题 spring @configure_spring_03

我们先点开:BeanMethodInterceptor看看其匹配条件。可以看到如果

该方法不是不bject类的自带方法 && 不是setBeanFactory方法 && 是否加了@Bean注解

则会进行增强。

@Override
		public boolean isMatch(Method candidateMethod) {
			//该方法不是不bject类的自带方法 && 不是setBeanFactory方法 && 是否加了@Bean注解
			return (candidateMethod.getDeclaringClass() != Object.class &&
					!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
					BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
		}

 再看看第二个:

@Override
		public boolean isMatch(Method candidateMethod) {
			return isSetBeanFactory(candidateMethod);
		}

		public static boolean isSetBeanFactory(Method candidateMethod) {
			//是否是setBeanFactory方法 && 方法是否只有一个参数 && 第一个参数是BeanFactory类型 && 方法所在类实现了BeanFactoryAware
			return (candidateMethod.getName().equals("setBeanFactory") &&
					candidateMethod.getParameterCount() == 1 &&
					BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
					BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
		}
	}

这也就验证了我们之前的说法:

第一个BeanMethodInterceptor主要用于增强加了@Bean注解的方法,第二个是用于增强setBeanFactory方法

第一个BeanMethodInterceptor用于增强@Bean注解,我们可以清楚了看出,但是第二个用于增强setBeanFactory方法,大概流程和作用?调用时机是什么时候?

第一个问题:第二个用于增强setBeanFactory方法,大概流程和作用?

使用cglib进行代理时,会在代理类中添加一个BeanFactory属性(后面需要用BeanFactory去获取bean),然而这个对象需要进行依赖注入,此时cglib就又加了一个
setBeanFactory(BeanFactory BeanFactory)方法,对该属性进行注入。

而增强setBeanFactory方法的逻辑就是获取到BeanFactory对象并注入给代理类中BeanFactory属性。

(也可以理解为增强后会通过 反射去填充$$beanFactory属性;从而使代理对象当中具备了getBean的可能;)

BeanFactory属性添加代码:

spring ConfigurationProperties乱码问题 spring @configure_后端_04

是因为我们生成的代理类实现了EnhancedConfifiguration接口,而


EnhancedConfifiguration 接口继承了 BeanFactoryAware , BeanFactoryAware 当中有一个唯一的方法 setBeanFactory。



第二个问题:调用时机?



我们会到最开始的入口,即

ConfigurationClassPostProcessor#postProcessBeanFactory





spring ConfigurationProperties乱码问题 spring @configure_spring_05


 这行代码可以知道,当我们动态代理完成后,会往容器中加入一个ImportAwareBeanPostProcessor对象。

我们看看该类的构造方法:

private static class ImportAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

		private final BeanFactory beanFactory;

		public ImportAwareBeanPostProcessor(BeanFactory beanFactory) {
			this.beanFactory = beanFactory;
		}

		@Override
		public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) {
			// Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
			// postProcessProperties method attempts to autowire other configuration beans.
			//完成对@Configuration注解时需要注入一个beanFactory
			if (bean instanceof EnhancedConfiguration) {
				((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
			}
			return pvs;
		}

		@Override
		public Object postProcessBeforeInitialization(Object bean, String beanName) {
			if (bean instanceof ImportAware) {
				ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
				AnnotationMetadata importingClass = ir.getImportingClassFor(ClassUtils.getUserClass(bean).getName());
				if (importingClass != null) {
					((ImportAware) bean).setImportMetadata(importingClass);
				}
			}
			return bean;
		}
	}

可以看到其中的回调方法postProcessProperties执行了对setBeanFactory方法的回调。

(BeanPostProcessor的执行时机是在refresh--》finishBeanFactoryInitialization方法中完成执行,即setBeanFactory方法会在这个配置类的bean生命周期的属性填充这一步完成调用;属性填充的时候会 调用ImportAwareBeanPostProcessor#postProcessProperties方法(该方法会去判断bean是否被cglib代 理了,如果代理了则调用setBeanFactory方法))


而对于 finishBeanFactoryInitialization方法的具体执行逻辑,我们后面再做解析。



此时我们可以总结spring使用cglib对使用了@Configuration进行代理:


例如一个类:


@Configuration
@ComponentScan(value = "com.spring.demo.cglib")
public class ContextConfig {
}

代理出来的类大概会变成这个样子(注意:使用伪代码进行说明的):

public class ContextConfig extends EnhancedConfifiguration{


   //该名称由名字生成策略生成
    BeanFactory $$beanFactory;

    @Overide
    setBeanFactory (BeanFactory beanFactory){
      //回调该拦截器的回调方法进行$$beanFactory属性填充
      BeanFactoryAwareMethodInterceptor.intercept(beanFactory);
}

}

至此,我们知道加了@Configuration的类会生成一个BeanFactory属性+一个setBeanFactory方法,这个方法会被一个 BeanFactoryAwareMethodInterceptor的isMatch方法匹配到(        enhancer.setCallbackFilter(CALLBACK_FILTER)代码行加入了该过滤器)

,然后在配置类的bean生命周期的属性填充这一步完成调用setBeanFactory方法的回调(该方法又调用BeanFactoryAwareMethodInterceptor.intercept(beanFactory)方法),并在其中通过反射获取对象填充到BeanFactory属性中,使得获取getBean能力。

而对于spring如何使用获取到的代理对象(加@Configuration类生成的),去使得@Bean的bean作用域正确性我们还没讲。下面我们开始看看是怎么保证的:

我们可以拿到上篇文章的例子:(这里的配置类改为用@Configuration)

spring ConfigurationProperties乱码问题 spring @configure_后端_06

 


此时被cglib代理出来的大概代码如下:

public class FullOrLiteConfig extends  EnhancedConfifiguration{

	BeanFactory $$beanFactory;

	@Bean
	public Scan_D scan_d(){
		//@Bean方法的增强方法
		Scan_D scan_D = BeanMethodInterceptor.intercept();
		return scan_D;
	}

	@Bean
	public Scan_E scan_e(){
		//@Bean方法的增强方法
		BeanMethodInterceptor.intercept();
		scan_d();
		return new Scan_E();
	}


	@Overide
	void setBeanFactory (BeanFactory beanFactory){
		//回调该拦截器的回调方法进行$$beanFactory属性填充
		BeanFactoryAwareMethodInterceptor.intercept(beanFactory);
	}
}

此时spring在refresh--》finishBeanFactoryInitialization中使用代理对象回调了scan_d方法和scan_e方法,由于这两个方法加了@Bean注解,所以会被BeanMethodInterceptor.intercept();做增强。我们可以看看大概的增强逻辑:

@Override
		@Nullable
		public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
					MethodProxy cglibMethodProxy) throws Throwable {

			ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
			String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

			// Determine whether this bean is a scoped-proxy
			if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
				String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
				if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
					beanName = scopedBeanName;
				}
			}

			// To handle the case of an inter-bean method reference, we must explicitly check the
			// container for already cached instances.

			// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
			// proxy that intercepts calls to getObject() and returns any cached bean instance.
			// This ensures that the semantics of calling a FactoryBean from within @Bean methods
			// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
			if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
					factoryContainsBean(beanFactory, beanName)) {
				Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
				if (factoryBean instanceof ScopedProxyFactoryBean) {
					// Scoped proxy factory beans are a special case and should not be further proxied
				}
				else {
					// It is a candidate FactoryBean - go ahead with enhancement
					return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
				}
			}

			//对@Bean的bean作用域正确性保证在这里处理:
			//看是否和ThreadLocal的当前方法匹配
			if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
				// The factory is calling the bean method in order to instantiate and register the bean
				// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
				// create the bean instance.
				if (logger.isInfoEnabled() &&
						BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
					logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
									"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
									"result in a failure to process annotations such as @Autowired, " +
									"@Resource and @PostConstruct within the method's declaring " +
									"@Configuration class. Add the 'static' modifier to this method to avoid " +
									"these container lifecycle issues; see @Bean javadoc for complete details.",
							beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
				}
                //调用当前方法
				return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
			}
			//不匹配,则执行该方法,通过getBean从单例池拿取bean,而不是重新去实例化bean
			return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
		}

spring每次回调BeanMethodInterceptor.intercept();方法时,都会讲当前处理方法放进一个ThreadLocal中,执行完进行remove。当执行的scan_e时,会往ThreadLocal存进一个scan_e(),

然后执行增强方法,该增强方法符合上面源码isCurrentlyInvokedFactoryMethod,则进入cglibMethodProxy.invokeSuper()调用当前方法。

然后例子中的代码会去调用scan_d方法,此时scan_d回去调用增强方法。

但是由于此时scan_e方法还没执行完,ThreadLocal里面还是scan_e方法,那么scan_d执行增强方法的时候去匹配ThreadLocal里面的方法就为f,此时进入resolveBeanReference方法。

该方法会去spring中getBean获取单例对象(这里就用到了我们之前@Configuration注入的BeanFactory对象去getBean),而不会重新去实例化bean对象。

说到底,@Bean能够保证作用域的正确性,都是依靠@Configuration进行cglib代理出一个代理对象,该代理对象执行增强方法增加一个属性BeanFactory并进行填充(setBeanFactory),然后在调用@Bean方法时,每次获取bean对象都会使用BeanFactory.getBean获取对象,而不会去重新实例化对象。

至此我们从mybatis-spring讲了动态注入BeanDefinition、spring扫描等等,下面我们将回到spring的全貌,讲讲spring的bean生命周期。