原始 singleton Bean 加载过程
从 ScopedProxyUtils#createScopedProxy 方法中可以得知原始 Bean scope 属性是 “” 值,而目标 Bean scope 属性继承了原始 Bean 未改变之前的值,也就是 refresh
完成 Bean 单例 Bean 加载的过程是在 DefaultListableBeanFactory#preInstantiateSingletons 方法中完成的,该方法中部分源码如下:
// 当前方法的判别表明了原始 Bean 就是单例的,而目标 Bean 是非单例的 public boolean isSingleton() { return "singleton".equals(this.scope) || "".equals(this.scope); } // 非抽象的、单例的、非懒加载的,只有满足这三个条件才会继续往下面去 getBean->doGetBean->createBean->doCreateBean } while(bd.isAbstract()); } while(!bd.isSingleton()); } while(bd.isLazyInit()); // 此处原始 Bean 就是一个 FactoryBean 了,所以先 getBean 创建 FactoryBean 接口的实例 if (this.isFactoryBean(beanName)) { bean = this.getBean("&" + beanName); break; } // 这里就会去触发调用 FactoryBean#getObject 方法获取里面的实例 this.getBean(beanName);
在以上中为什么说原始 Bean 已经是一个 FactoryBean 对象了呢?
因为在前面执行
GenericScope#postProcessBeanDefinitionRegistry
时,为原始 Bean 重新赋值了 beanClass 属性,对应的值就是 LockedScopedProxyFactoryBean
查看 LockedScopedProxyFactoryBean 类图即可明白,如下所示:
从继承关系来看,当前类就实现了 FactoryBean 接口,同时它也实现了 MethodInterceptor 接口(这里埋一个引子,后续生成代理对象以后就会调用 LockedScopedProxyFactoryBean#invoke 方法)
说到了 FactoryBean,那必然就需要看它的 getObject、isSingleton 方法,但是在调用 getObject 方法在实际应用时是通过手动去调用的,在这边创建先要创建 FactoryBean 实例,它必然会经过填充属性(populateBean)->初始化 Bean(initializeBean)
这个过程的
再观察类图,它实现了 BeanFactoryAware 方法, 同时在初始化 Bean(initializeBean)
会调用 invokeAwareMethods 方法,它会处理三个 Aware 接口的方法:BeanNameAware#setBeanName、BeanClassLoaderAware#setBeanClassLoader、BeanFactoryAware#setBeanFactory
,在这里只有一个 Aware 方法满足,所以接下来观察它的 setBeanFactory 作了什么处理
// LockedScopedProxyFactoryBean.java public void setBeanFactory(BeanFactory beanFactory) { // 调用父类的 setBeanFactory 方法,主要的处理也是在它的父方法中 super.setBeanFactory(beanFactory); // 获取父类创建好的代理对象 Object proxy = getObject(); if (proxy instanceof Advised) { // 将父类创建好的代理对象被优先加载调用 Advised advised = (Advised) proxy; advised.addAdvice(0, this); } } // LockedScopedProxyFactoryBean 父类 ScopedProxyFactoryBean private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource(); public ScopedProxyFactoryBean() { // proxyTargetClass=true this.setProxyTargetClass(true); } public void setTargetBeanName(String targetBeanName) { this.targetBeanName = targetBeanName; // 在填充属性阶段同时为 scopedTargetSource 设置了目标名称 this.scopedTargetSource.setTargetBeanName(targetBeanName); } public void setBeanFactory(BeanFactory beanFactory) { if (!(beanFactory instanceof ConfigurableBeanFactory)) { throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory); } else { ConfigurableBeanFactory cbf = (ConfigurableBeanFactory)beanFactory; this.scopedTargetSource.setBeanFactory(beanFactory); // 创建动态代理的核心类 ProxyFactory pf = new ProxyFactory(); pf.copyFrom(this); // 设置目前源对象类:SimpleBeanTargetSource pf.setTargetSource(this.scopedTargetSource); Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required"); Class<?> beanType = beanFactory.getType(this.targetBeanName); if (beanType == null) { throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName + "': Target type could not be determined at the time of proxy creation."); } else { // proxyTargetClass=true,无参构造方法设置过了 if (!this.isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) { pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader())); } // 准备好,后续拦截器会调用 SimpleBeanTargetSource#getTargetObject 方法 ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName()); pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject)); pf.addInterface(AopInfrastructureBean.class); this.proxy = pf.getProxy(cbf.getBeanClassLoader()); } } } // 子类手动调用 public Object getObject() { if (this.proxy == null) { throw new FactoryBeanNotInitializedException(); } else { return this.proxy; } }
这里先记录一下,后面阶段会调用此处:ProxyFactory 代理工厂类设置了目标源对象 SimpleBeanTargetSource,该目标源对象在填充属性阶段设置了 targetBeanName 值
但是你没想到吧,到这里,还是它动态刷新的准备工作,这里只是把原始 Bean 加载完成,目标 Bean 还没有进行处理呢!!!
目标 refresh Bean 加载过程
刚刚说到了,目标 Bean 是非单例的,它的 scope 属性值为 refresh,所以说在执行 DefaultListableBeanFactory#preInstantiateSingletons
是不会对它作任何处理工作,除非我们绕开这个方法,直接调用 getBean 方法就可以获取实例对象了,那么下面就看 spring-cloud 是如何应用 spring-boot 组件来完成这个骚操作的呢?
重要的点还是在 RefreshScope 类里面的方法中,这里再把它的类图贴出来:
发现了它实现了 ApplicationListener 接口,它肯定是监听了某个事件,事件名:ContextRefreshedEvent,并且它肯定实现了 onApplicationEvent 方法
// RefreshScope.java public void onApplicationEvent(ContextRefreshedEvent event) { start(event); } public void start(ContextRefreshedEvent event) { // eager 默认值就是 true if (event.getApplicationContext() == this.context && this.eager && this.registry != null) { eagerlyInitialize(); } } // 提前初始化 private void eagerlyInitialize() { for (String name : this.context.getBeanDefinitionNames()) { BeanDefinition definition = this.registry.getBeanDefinition(name); // scope 属性值=当前 name 也就是 refresh、lazyInit 属性为设置的话默认就是 false if (this.getName().equals(definition.getScope()) && !definition.isLazyInit()) { // 此处就是直接绕过了,调用 getBean 方法 Object bean = this.context.getBean(name); if (bean != null) { bean.getClass(); } } } }
先整理一下 spring-boot 是在什么发送 ContextRefreshedEvent 这个事件的呢?
其实就是在执行完 DefaultListableBeanFactory#preInstantiateSingletons 方法以后,马上就调用 finishRefresh 方法随即就发送事件啦!!
getBean->doGetBean 方法,该方法内有这么个调用逻辑,贴上部分 doGetBean 方法的源码:
// isSingleton & isPrototype 都不满足,所以就只需要关注 else 分支的逻辑了 if (mbd.isSingleton()) { ..... } else if (mbd.isPrototype() { ..... } else { String scopeName = mbd.getScope(); if (!StringUtils.hasLength(scopeName)) { throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'"); } // scopeName=refresh,scopes 集合中在之前 GenericScope#postProcessBeanFactory 方法已经添加进去了 Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { // 核心方法就是这个了 Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } }
Scope#get 方法,Scope 是一个顶级接口,当然是找它的子类 GenericScope、RefreshScope 了,而 RefreshScope 并没有实现这个方法,所以直接定位到 GenericScope#get 方法,源码如下:
public Object get(String name, ObjectFactory<?> objectFactory) { // 存入 ScopeCache#cache 集合中 BeanLifecycleWrapper value = this.cache.put(name, new BeanLifecycleWrapper(name, objectFactory)); this.locks.putIfAbsent(name, new ReentrantReadWriteLock()); try { // 这里调用的是上面 lambda 表达式中的 createBean 方法 return value.getBean(); } catch (RuntimeException e) { this.errors.put(name, e); throw e; } }
BeanLifecycleWrapper 是用于存储缓存的,cache 是一个 ConcurrentMap 结构,创建的这个对象实例就是一个普通的对象,不会做任何的代理增强处理
Refresh 动态刷新监听器
结合前言中提到:@RefreshScope 所在的 package 包名,在该模块下会自动装配下两个核心类:RefreshScope、RefreshEventListener
,前面的内容都是准备工作,而且只介绍了核心类 RefreshScope 作用以及提前准备好的 scope=refresh 实例对象;RefreshEventListener 未介绍,它是一个监听器,那么肯定有地方会发布这个事件,所以这边又要介绍另外一个自动装配进来的配置类了.
介绍 spring-cloud-alibaba-config 模块内会自动装配进来 NacosContextRefresher 核心类,它实现了 ApplicationListener<ApplicationReadyEvent> 接口
,而它是在执行完之前所有的准备工作以后,发起了应用准备就绪事件,随即 NacosContextRefresher#onApplicationEvent 方法就会接受到事件进行调用,到这里,就弄清楚它的来龙去脉,继续往下走,看该方法处理做了什么样的操作!
private AtomicBoolean ready = new AtomicBoolean(false); public void onApplicationEvent(ApplicationReadyEvent event) { // CAS 操作来确保只能加载一次 if (this.ready.compareAndSet(false, true)) { this.registerNacosListenersForApplications(); } }