相關背景及資源:html
曹工說Spring Boot源碼(1)-- Bean Definition究竟是什麼,附spring思惟導圖分享java
曹工說Spring Boot源碼(2)-- Bean Definition究竟是什麼,我們對着接口,逐個方法講解git
曹工說Spring Boot源碼(3)-- 手動註冊Bean Definition不比遊戲好玩嗎,咱們來試一下spring
曹工說Spring Boot源碼(4)-- 我是怎麼自定義ApplicationContext,從json文件讀取bean definition的?json
工程結構圖:ide
bean definition實在太太重要,能夠說是基礎中的基礎,因此咱們花了不少講在這上面,本講的主題,仍是這個。此次,咱們是從properties文件裏讀取bean definition
。函數
可是,上次,從json讀取,咱們本身實現了org.springframework.beans.factory.support.AbstractBeanDefinitionReader
,使用fastjson從json文件內讀取。spring-boot
此次,咱們不須要本身實現,是由於spring-beans包內,竟然自帶了從properties文件讀取bean的實現類。測試
因此,這樣就變得很簡單了,咱們只須要定義一個applicationContext
,讓它使用這個開箱即用的reader便可。
閒言少敘,let's code!
本類的javadoc
如是說:
* Bean definition reader for a simple properties format. * * <p>Provides bean definition registration methods for Map/Properties and * ResourceBundle. Typically applied to a DefaultListableBeanFactory.
這裏說,就是一個從properties格式的文件讀取bean definition
的,那麼,是否是properties
能夠隨便怎麼寫呢?嗯,按理說,是能夠隨便寫,你別管我怎麼寫,形式重要嗎,重要的是,有這個數據。
bean definition的核心數據有哪些?再回憶一下,beanClassName、scope、lazy-init、parent、abstract等。
parent和abstract,是個新概念,前面我也沒有提,你們看下面的例子可能就懂了(來自於PropertiesBeanDefinitionReader
的註釋,我本身梳理了一下)。
這個reader,對properties的格式是有要求的,參考下面這份:
//定義一個抽象bean,名稱爲employee(句號前面爲bean的名稱),表示爲員工,類型爲Employee,兩個屬性:組名:Insurance;useDialUp(我理解爲工位是否配電話):true employee.(class)=org.springframework.simple.Employee employee.(abstract)=true employee.group=Insurance employee.usesDialUp=false //定義一個非抽象bean,parent爲抽象的employee,department屬性爲CEOdepartment,usesDialUp爲true,覆蓋了parent的false ceo.(parent)=employee ceo.department=ceo department ceo.usesDialUp=true //定義另外一個非抽象bean,表示銷售人員,lazy-init,經理字段:引用了另外一個bean,name爲ceo;部門爲Sales salesrep.(parent)=employee salesrep.(lazy-init)=true salesrep.manager(ref)=ceo salesrep.department=Sales //這個相似 techie.(parent)=employee techie.(scope)=prototype techie.department=Engineering techie.usesDialUp=true techie.manager(ref)=ceo
貼心的我給你們花了個圖:
接下來,咱們仍是先看看這個類:
看一個類,其實主要看接口,才能快速瞭解一個類的用途,這裏,它實現了org.springframework.beans.factory.support.BeanDefinitionReader
接口。
這個接口的方法以下:
public interface BeanDefinitionReader { //獲取bean definition 註冊中心,老朋友DefaultListableBeanFactory實現了該接口 BeanDefinitionRegistry getRegistry(); // 獲取資源加載器 ResourceLoader getResourceLoader(); //獲取classloader ClassLoader getBeanClassLoader(); //獲取bean名稱生成器 BeanNameGenerator getBeanNameGenerator(); //從指定資源充,加載bean definition int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; //重載 int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; //重載 int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; //重載 int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException; }
大致,能夠看出來,這個bean definition reader
接口,就是使用指定的classloader,從指定的resource,去加載bean definition。
咱們先看看PropertiesBeanDefinitionReader
怎麼構造:
//調用父類,參數傳入了bean definition 註冊表 public PropertiesBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } //構造默認的資源加載器、environment protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { this.registry = registry; // Determine ResourceLoader to use. if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader) this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } // Inherit Environment if possible if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable)this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); } }
再看主要的loadBeanDefinition
方法,是怎麼實現的:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource), null); }
調用了內部的:
//加載bean definition public int loadBeanDefinitions(EncodedResource encodedResource, String prefix) throws BeanDefinitionStoreException { //讀取properties文件內容到props變量 Properties props = new Properties(); InputStream is = encodedResource.getResource().getInputStream(); if (encodedResource.getEncoding() != null) { InputStreamReader reader = new InputStreamReader(is, encodedResource.getEncoding()); props.load(reader); } else { props.load(is); } //註冊bean definition return registerBeanDefinitions(props, prefix, null); }
繼續深刻上面的倒數第二行的函數:
public int registerBeanDefinitions(Map map, String prefix, String resourceDescription) throws BeansException { if (prefix == null) { prefix = ""; } int beanCount = 0; for (Object key : map.keySet()) { String keyString = (String) key; if (keyString.startsWith(prefix)) { // Key is of form: prefix<name>.property String nameAndProperty = keyString.substring(prefix.length()); // Find dot before property name, ignoring dots in property keys. int sepIdx = nameAndProperty.lastIndexOf(SEPARATOR); if (sepIdx != -1) { String beanName = nameAndProperty.substring(0, sepIdx); if (!getRegistry().containsBeanDefinition(beanName)) { // 若是以前沒註冊這個bean,則註冊之,這裏的prefix:prefix+beanName,其實就是從properties文件中篩選出beanName一致的key-value registerBeanDefinition(beanName, map, prefix + beanName, resourceDescription); ++beanCount; } } } } return beanCount; }
主要就是遍歷map,將property的key用.分割,前面的就是beanName,用beanName做爲前綴,而後調用下一層函數:
public static final String CLASS_KEY = "(class)"; public static final String PARENT_KEY = "(parent)"; public static final String SCOPE_KEY = "(scope)"; public static final String SINGLETON_KEY = "(singleton)"; public static final String ABSTRACT_KEY = "(abstract)"; public static final String LAZY_INIT_KEY = "(lazy-init)"; public static final String REF_SUFFIX = "(ref)"; protected void registerBeanDefinition(String beanName, Map<?, ?> map, String prefix, String resourceDescription) throws BeansException { String className = null; String parent = null; String scope = GenericBeanDefinition.SCOPE_SINGLETON; boolean isAbstract = false; boolean lazyInit = false; ConstructorArgumentValues cas = new ConstructorArgumentValues(); MutablePropertyValues pvs = new MutablePropertyValues(); for (Map.Entry entry : map.entrySet()) { String key = StringUtils.trimWhitespace((String) entry.getKey()); if (key.startsWith(prefix + SEPARATOR)) { String property = key.substring(prefix.length() + SEPARATOR.length()); //核心屬性,bean的ClassName if (CLASS_KEY.equals(property)) { className = StringUtils.trimWhitespace((String) entry.getValue()); }//parent屬性 else if (PARENT_KEY.equals(property)) { parent = StringUtils.trimWhitespace((String) entry.getValue()); }//是否抽象bean definition else if (ABSTRACT_KEY.equals(property)) { String val = StringUtils.trimWhitespace((String) entry.getValue()); isAbstract = TRUE_VALUE.equals(val); }//scope ...此處不重要的屬性代碼進行省略 //經過構造器注入其餘bean,咱們例子裏沒涉及 else if (property.startsWith(CONSTRUCTOR_ARG_PREFIX)) { if (property.endsWith(REF_SUFFIX)) { int index = Integer.parseInt(property.substring(1, property.length() - REF_SUFFIX.length())); cas.addIndexedArgumentValue(index, new RuntimeBeanReference(entry.getValue().toString())); } else { int index = Integer.parseInt(property.substring(1)); cas.addIndexedArgumentValue(index, readValue(entry)); } } // 這裏引用其餘bean,語法是咱們例子用到的,(ref) else if (property.endsWith(REF_SUFFIX)) { property = property.substring(0, property.length() - REF_SUFFIX.length()); String ref = StringUtils.trimWhitespace((String) entry.getValue()); Object val = new RuntimeBeanReference(ref); pvs.add(property, val); } else { // It's a normal bean property. pvs.add(property, readValue(entry)); } } } //構造一個bean definition AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition( parent, className, getBeanClassLoader()); bd.setScope(scope); bd.setAbstract(isAbstract); bd.setLazyInit(lazyInit); //下面這兩行,進行構造器注入和屬性注入 bd.setConstructorArgumentValues(cas); bd.setPropertyValues(pvs); //註冊 getRegistry().registerBeanDefinition(beanName, bd); }
本類的主要代碼就這些,刪減了部分,主要是避免太冗餘,代碼有刪減就會使用...表示。
package org.springframework.beans.extend.properties.applicationcontext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractRefreshableConfigApplicationContext; import java.io.IOException; public class ClassPathPropertyFileApplicationContext extends AbstractRefreshableConfigApplicationContext { /** * Loads the bean definitions via an XmlBeanDefinitionReader. * @see XmlBeanDefinitionReader * @see #loadBeanDefinitions */ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 構造一個propertiesBeanDefinitionReader,就是前面咱們的主角 PropertiesBeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory); beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. loadBeanDefinitions(beanDefinitionReader); } //使用reader,加載bean definition protected void loadBeanDefinitions(PropertiesBeanDefinitionReader reader) throws BeansException, IOException { String[] configResources = getConfigLocations(); if (configResources != null) { //看這,兄弟 reader.loadBeanDefinitions(configResources); } } public ClassPathPropertyFileApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } }
@Slf4j public class BootStrap { public static void main(String[] args) { ClassPathPropertyFileApplicationContext context = new ClassPathPropertyFileApplicationContext("beanDefinition.properties"); Map<String, Employee> beansOfType = context.getBeansOfType(Employee.class); for (Map.Entry<String, Employee> entry : beansOfType.entrySet()) { log.info("bean name:{},bean:{}",entry.getKey(),entry.getValue()); } } }
output:
22:17:26.083 [main] INFO o.s.b.extend.properties.BootStrap - bean name:techie,bean:Employee(group=Insurance, usesDialUp=true, department=Engineering, manager=Employee(group=Insurance, usesDialUp=true, department=ceo department, manager=null)) 22:17:26.083 [main] INFO o.s.b.extend.properties.BootStrap - bean name:salesrep,bean:Employee(group=Insurance, usesDialUp=false, department=Sales, manager=Employee(group=Insurance, usesDialUp=true, department=ceo department, manager=null)) 22:17:26.083 [main] INFO o.s.b.extend.properties.BootStrap - bean name:ceo,bean:Employee(group=Insurance, usesDialUp=true, department=ceo department, manager=null)
這裏能夠看出來,子bean是繼承了父bean的bean definition,並override了父bean中已經存在的屬性。
工程源碼:
這一講,主要是講解了另外一種讀取bean definition
的方式,其實就是告訴咱們要打破思想束縛,bean的來源能夠用不少,不必定只有xml和註解。另外,也是培養咱們的抽象思惟,至少bean definition reader這個接口,給咱們的感受就是如此,我無論你resource
來自哪裏,只要能讀取bean definition
便可,正所謂:英雄不問出處!
咱們做爲技術從業人員也是如此,只要技術夠ok,到哪都能混得走。
下一講,咱們將繼續講解bean definition
,主要是bean definition
的繼承和override
相關內容。
以爲有幫助的話,你們點個贊哈