上篇文章有说到,spring对于全配置类进行了代理,保证了bean作用域的正确性,那spring是如何保证的?这篇文章我们会继续进行说明。
我们继上篇讲的全配置类和半配置类部分的源码,即以ConfigurationClassPostProcessor#
postProcessBeanFactory方法里面的enhanceConfigurationClasses方法为入口:继续进入改行代码:
进入该类:
可以看到起构造方法,大概意思如下:
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看看是如果进行匹配的:
此时发现有两个实现类,分别是我们之前提过的两个拦截器:
我们先点开: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属性添加代码:
是因为我们生成的代理类实现了EnhancedConfifiguration接口,而
EnhancedConfifiguration 接口继承了 BeanFactoryAware , BeanFactoryAware 当中有一个唯一的方法 setBeanFactory。
第二个问题:调用时机?
我们会到最开始的入口,即
ConfigurationClassPostProcessor#postProcessBeanFactory
:
这行代码可以知道,当我们动态代理完成后,会往容器中加入一个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)
此时被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生命周期。