附錄:Spring源碼學習專欄html
在上一章的學習中,咱們對Bean的建立有了一個粗略的瞭解,接着本文挑一個比較重要的知識點Bean的懶加載進行學習前端
懶加載(Lazy-initialized beans):懶加載模式是bean在第一次調用時候被實例,而不是spring容器啓動時候,默認是不開啓的,( A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.),經過改配置lazy-init="true"java
實驗環境:spring
import org.springframework.beans.factory.InitializingBean; /** * <pre> * SpringBean * </pre> * * <pre> * @author mazq * 修改記錄 * 修改後版本: 修改人: 修改日期: 2020/11/05 10:50 修改內容: * </pre> */ public class SpringBean implements InitializingBean { public SpringBean(){ System.out.println("SpringBean構造函數"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("SpringBean afterPropertiesSet"); } }
xml配置方式,在applicationContext.xml加載配置bootstrap
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="springBean" lazy-init="true" class="com.example.bean.SpringBean" ></bean> </beans>
註解方式,使用@Lazy便可後端
import com.example.bean.SpringBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.example.bean.A; import org.springframework.context.annotation.Lazy; /** * <pre> * AppConfiguration * </pre> * * <pre> * @author mazq * 修改記錄 * 修改後版本: 修改人: 修改日期: 2020/11/05 10:26 修改內容: * </pre> */ @Configuration public class AppConfiguration { @Bean @Lazy // 開啓懶加載 // @Lazy(value = false) 默認 public SpringBean springBean() { return new SpringBean(); } }
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // lazy-init開啓的bean,context.getBean調用時候纔會被實例 SpringBean springBean = context.getBean(SpringBean.class); System.out.println(springBean);
爲何設置爲lazy-init以後,Spring IoC容器啓動時候bean不會被實例?能夠基於上一章內容找到答案緩存
{@link org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization}
app
/** * Finish the initialization of this context's bean factory, * initializing all remaining singleton beans. */ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. // 初始化ConversionService,這個bean用於將前端傳過來的參數和後端的 controller 方法上的參數進行綁定 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. // 先初始化LoadTimeWeaverAware 類型的Bean // AspectJ 的內容,IoC的源碼學習,先跳過 String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. // 凍結配置,不讓bean 定義解析、加載、註冊 beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. // 實例全部非懶加載的單例Bean beanFactory.preInstantiateSingletons(); }
找到關鍵代碼beanFactory.preInstantiateSingletons();
ide
@Override public void preInstantiateSingletons() throws BeansException { if (logger.isDebugEnabled()) { logger.debug("Pre-instantiating singletons in " + this); } // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. // 獲取beanName列表,this.beanDefinitionNames 保存了全部的 beanNames List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... // 觸發全部非懶加載的單例bean初始化操做(lazy-init=false) for (String beanName : beanNames) { // 合併rootBean中的配置, <bean id="a" class="a" parent="p" /> RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 非抽象(abstract = false)、非懶加載(lazy-init=false)的單例Bean(scope=singleton) if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 處理FactoryBean,注意對比BeanFactory和FactoryBean if (isFactoryBean(beanName)) { // factoryBean調用在beanName加載前綴符號‘&’ // 爲何要加‘&’,應該是作下標記,不過在bean建立過程要進行轉換,詳情請看下文 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; // FactoryBean是SmartFactoryBean 的基類 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { // 普通的Bean,調這個方法進行實例,往下跟 getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... // SmartInitializingSingleton 的基類在這裏回調 for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
代碼裏有isLazyInit()
的校驗,因此設置lazy-init=true的bean都不會隨着ioc容器啓動而被實例加載函數
綜上所述: