@RefreshScope annotation
When spring starts, doScan() in the class will be called to scan all classes under the package path to get the definition of the bean, and at the same time, the bean's @RefreshScope (@Scope's parent class) is processed.
- Get the @RefreshScope (@Scope's parent class) annotation metadata that is applied to the class, encapsulate it into a ScopeMetadata object, and declare the scope of the bean (());
- According to scopeMetadata, @Scope, the bean is proxyed, and the bean definition of the principle is created using the ScopedProxyFactoryBean class. Register the original bean definition of the proxy into the container (->ConfigController), and then register the bean definition of the proxy into the container (configController -> ScopedProxyFactoryBean)
The RefreshScope component that handles this bean annotated using @RefreshScope is the RefreshScope component. You can see that the parent class GenericScope implements the BeanDefinitionRegistryPostProcessor and BeanFactoryPostProcessor interfaces. This is the postprocessor of the container. When the container starts, the postProcessBeanDefinitionRegistryPostProcessor interface will be called first, and then the postProcessBeanFactory() method in the BeanFactoryPostProcessor interface will be called.
public class RefreshScope extends GenericScope implements ApplicationContextAware, Ordered{...} public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean{...}
Get all bean definitions contained in the container, and then get the scope through the bean definition decorated by the bean to determine whether it is a scope that RefreshScope can handle. When constructing the RefreshScope instance, the name is set to "refresh" getName().equals(().getBeanDefinition().getScope()). ps: This is a detail. The scope defined by the bean being decorated is "refresh", while the scope of the bean is ""; when processing it later, the decorator bean is first processed, and when processing the original bean, it is judged through this scope. If so, set the class of the bean to LockedScopedProxyFactoryBean and set the current RefreshScope instance object to the constructor parameter. Take a look at the LockedScopedProxyFactoryBean class:
public static class LockedScopedProxyFactoryBean<S extends GenericScope> extends ScopedProxyFactoryBean implements MethodInterceptor{...} public class ScopedProxyFactoryBean extends ProxyConfig implements FactoryBean<Object>, BeanFactoryAware{...}
You can see that the parent class of LockedScopedProxyFactoryBean implements the BeanFactoryAware interface. Therefore, when the bean is instantiated, the setBeanFactory() method will be called. At this time, a proxy object will be created through the proxy factory, that is, the proxy object of the LockedScopedProxyFactoryBean class will be generated. Since the LockedScopedProxyFactoryBean class implements the Advised interface, it can be used as an interceptor for the proxy object, and the interceptor will work when the method of the proxy object is called.
public void setBeanFactory(BeanFactory beanFactory) { ...... ProxyFactory pf = new ProxyFactory(); (this); (); ..... = (()); } public void setBeanFactory(BeanFactory beanFactory) { (beanFactory); Object proxy = getObject(); if (proxy instanceof Advised) { Advised advised = (Advised) proxy; (0, this); } }
Summarize the process of instantiating beans with @RefreshScope annotation:
- The definition of beans using @RefreshScope annotation will be set to
- When creating a bean, it becomes an instance object of LockedScopedProxyFactoryBean
- Since LockedScopedProxyFactoryBean implements the BeanFactoryAware interface, setBeanFactory() will be called
- In the setBeanFactory() method, the proxy object of the LockedScopedProxyFactoryBean object will be created through cglib. The getObject() of the FactoryBean interface will return this proxy object, so the final result is the proxy pair of the LockedScopedProxyFactoryBean object
- Since the proxy object is a subclass of LockedScopedProxyFactoryBean, the Advised interface is implemented, and the LockedScopedProxyFactoryBean object called the setBeanFactory() method can be saved as an interceptor
When the container starts and refreshes, the ContextRefreshedEvent event will be released. The start() in the RefreshScope class uses the @EventListener annotation, which can be used as a listener to handle the ContextRefreshedEvent event. Find the definition of the bean whose scope is "refresh", and then go to the Scope code block for instantiation.
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { ...... if (()) { ...... } else if (()) { ...... } else { String scopeName = (); // Get Scope according to name, there are currently four //refresh -> {RefreshScope@5469} //request -> {RequestScope@7462} //session -> {SessionScope@7464} //application -> {ServletContextScope@7466} final Scope scope = (scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = (beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } } ...... }
After obtaining the RefreshScope object, the get() method will be called for initialization. First create the BeanLifecycleWrapper object, which contains the name of the bean and the factory method (lamdba expression) that creates the bean, and then call the getBean() method in the BeanLifecycleWrapper class to get the instance object of the bean. At this time, call the = (); factory method to create the bean, and then go back to the createBean(beanName, mbd, args); method to complete the creation of the bean. Next, let’s see how the method is called.
- Get the instance object of the proxy original bean
- Create an interceptor chain, find the LockedScopedProxyFactoryBean interceptor that was added before, and call the invoke() method for processing
- Get the proxy object, that is, the proxy object of LockedScopedProxyFactoryBean; get the read lock of the read and write lock, and it cannot be obtained when the current configuration is refreshed.
- Call target methods in reflection (target, args)
Let’s see how the configuration is refreshed into the bean. When the configuration source is changed, the method call chain is as follows:
- NacosContextRefresher#registerNacosListener()Register listener(receiveConfigInfo())
- #publishEvent() Publish RefreshEvent event
- SimpleApplicationEventMulticaster#multicastEvent() method broadcast event
- RefreshEventListener#handle(RefreshEvent event) handles RefreshEvent event
- ContextRefresher#refresh() handles it and does three things
- Refresh the configuration source
- Publish EnvironmentChangeEvent event and regenerate the configuration source bean (@ConfigurationProperties annotation bean)
- Write configuration to the bean annotated by @RefreshScope
Start analyzing how to refresh to the bean marked with @RefreshScope annotation: clear the BeanLifecycleWrapperCache that saves the original proxy bean, acquire the write lock (at this time other threads cannot obtain the read lock), and call the destroy() method of each BeanLifecycleWrapper to destroy it. The cache has been cleared. If you re-acquire the bean, you have to re-create it. At this time, the bean's attributes will be filled, and you can get new attributes from the attribute source.
This is the article about the detailed explanation of the @RefreshScope annotation in Spring. For more information about Spring's @RefreshScope annotation, please search for my previous articles or continue browsing the related articles below. I hope you will support me in the future!