2019獨角獸企業重金招聘Python工程師標準>>>
Spring提供了豐富的標簽和注解來進行bean的定義,除此之外框架來提供了擴展機制讓使用可以通過properties來定義bean,與強大的標簽式和注解式的bean定義相比,properties提供的規則要簡單許多。
key部分用.分隔即通過A.B來進行相關的屬性定義,其中A表示bean名稱,B通過不同的值還表達不同的含義:
?
- (class),bean的類型
- (parent),bean的父bean
- name,bean的name屬性,name是一個普通屬性
- childBean(ref),bean的childBean屬性,childBean是一個引用屬性
- (singleton),是否單例
- (lazy-init),是否懶加載
- $0,第一個構造子參數
- (scope),作用域
- (abstract),是否是抽象bean
?
看一個例子:
bean.properties文件
?
[java]?view plain?copy
- #bean1??
- propBean.(class)?=?spring.beans.properties.PropBean??
- propBean.(parent)?=?commonBean??
- propBean.name?=?name1??
- propBean.childBean(ref)?=?childBean??
- propBean.(singleton)?=?true??
- propBean.(lazy-init)?=?true??
- ??
- #bean2??
- childBean.(class)?=?spring.beans.properties.ChildBean??
- childBean.$0?=?cid1??
- chlldBean.(scope)?=?singleton??
- ??
- #abstract?bean??
- commonBean.(class)?=?spring.beans.properties.CommonBean??
- commonBean.id?=?1??
- commonBean.(abstract)?=?true??
上面的properties文件定義了三個bean:
?
?
- commonBean,類型是spring.beans.properties.CommonBean,注入值1到id屬性,這是一個抽象bean
- childBean,類型spring.beans.properties.ChildBean,構造器注入cid1,作用域是singleton
- propBean,類型是spring.beans.properties.PropBean,父bean是commonBean,注入一個普通屬性name,和引用屬性childBean,引用的bean是childBean,bean是單例并且懶加載。
?
bean定義文件寫好之后,通過PropertiesBeanDefinitionReader來加載解析bean定義,這個解析器的原理很簡單,在此不做詳細分析,下面是實例代碼。
?
[java]?view plain?copy
- public?void?test()?{??
- ????GenericApplicationContext?ctx?=?new?GenericApplicationContext();??
- ????Resource?res?=?new?ClassPathResource(??
- ????????????"spring/beans/properties/bean.properties");??
- ????PropertiesBeanDefinitionReader?propReader?=?new?PropertiesBeanDefinitionReader(??
- ????????????ctx);??
- ????propReader.loadBeanDefinitions(res);??
- ????PropBean?propBean?=?(PropBean)?ctx.getBean("propBean");??
- ????assertNotNull(propBean);??
- ????assertNotNull(propBean.getId());??
- ??
- ????assertNotNull(propBean.getChildBean());??
- ????assertNotNull(propBean.getChildBean().getCid());??
- }??
也可以完全不用單獨定義個properties文件,只需要把相關的key-value放到一個Map中,再通過PropertiesBeanDefinitionReader加載這個map中的key-value。
?
通過這種方式,使用者可以根據需要自定義些bean的定義規則,比如可以把bean定義放在數據庫中,把數據庫中的信息讀取出來拼接成滿足properties規則的bean定義,在Spring中就定義了一個org.springframework.jdbc.core.support.JdbcBeanDefinitionReader來完成這種需求,看下這個類的代碼。
?
[java]?view plain?copy
- public?class?JdbcBeanDefinitionReader?{??
- ??
- ????private?final?PropertiesBeanDefinitionReader?propReader;??
- ??
- ????private?JdbcTemplate?jdbcTemplate;??
- ??
- ??
- ????/**?
- ?????*?Create?a?new?JdbcBeanDefinitionReader?for?the?given?bean?factory,?
- ?????*?using?a?default?PropertiesBeanDefinitionReader?underneath.?
- ?????*?<p>DataSource?or?JdbcTemplate?still?need?to?be?set.?
- ?????*?@see?#setDataSource?
- ?????*?@see?#setJdbcTemplate?
- ?????*/??
- ????public?JdbcBeanDefinitionReader(BeanDefinitionRegistry?beanFactory)?{??
- ????????this.propReader?=?new?PropertiesBeanDefinitionReader(beanFactory);??
- ????}??
- ??
- ????/**?
- ?????*?Create?a?new?JdbcBeanDefinitionReader?that?delegates?to?the?
- ?????*?given?PropertiesBeanDefinitionReader?underneath.?
- ?????*?<p>DataSource?or?JdbcTemplate?still?need?to?be?set.?
- ?????*?@see?#setDataSource?
- ?????*?@see?#setJdbcTemplate?
- ?????*/??
- ????public?JdbcBeanDefinitionReader(PropertiesBeanDefinitionReader?beanDefinitionReader)?{??
- ????????Assert.notNull(beanDefinitionReader,?"Bean?definition?reader?must?not?be?null");??
- ????????this.propReader?=?beanDefinitionReader;??
- ????}??
- ??
- ??
- ????/**?
- ?????*?Set?the?DataSource?to?use?to?obtain?database?connections.?
- ?????*?Will?implicitly?create?a?new?JdbcTemplate?with?the?given?DataSource.?
- ?????*/??
- ????public?void?setDataSource(DataSource?dataSource)?{??
- ????????this.jdbcTemplate?=?new?JdbcTemplate(dataSource);??
- ????}??
- ??
- ????/**?
- ?????*?Set?the?JdbcTemplate?to?be?used?by?this?bean?factory.?
- ?????*?Contains?settings?for?DataSource,?SQLExceptionTranslator,?NativeJdbcExtractor,?etc.?
- ?????*/??
- ????public?void?setJdbcTemplate(JdbcTemplate?jdbcTemplate)?{??
- ????????Assert.notNull(jdbcTemplate,?"JdbcTemplate?must?not?be?null");??
- ????????this.jdbcTemplate?=?jdbcTemplate;??
- ????}??
- ??
- ??
- ????/**?
- ?????*?Load?bean?definitions?from?the?database?via?the?given?SQL?string.?
- ?????*?@param?sql?SQL?query?to?use?for?loading?bean?definitions.?
- ?????*?The?first?three?columns?must?be?bean?name,?property?name?and?value.?
- ?????*?Any?join?and?any?other?columns?are?permitted:?e.g.?
- ?????*?{@code?SELECT?BEAN_NAME,?PROPERTY,?VALUE?FROM?CONFIG?WHERE?CONFIG.APP_ID?=?1}?
- ?????*?It's?also?possible?to?perform?a?join.?Column?names?are?not?significant?--?
- ?????*?only?the?ordering?of?these?first?three?columns.?
- ?????*/??
- ????public?void?loadBeanDefinitions(String?sql)?{??
- ????????Assert.notNull(this.jdbcTemplate,?"Not?fully?configured?-?specify?DataSource?or?JdbcTemplate");??
- ????????final?Properties?props?=?new?Properties();??
- ????????this.jdbcTemplate.query(sql,?new?RowCallbackHandler()?{??
- ????????????public?void?processRow(ResultSet?rs)?throws?SQLException?{??
- ????????????????String?beanName?=?rs.getString(1);??
- ????????????????String?property?=?rs.getString(2);??
- ????????????????String?value?=?rs.getString(3);??
- ????????????????//?Make?a?properties?entry?by?combining?bean?name?and?property.??
- ????????????????props.setProperty(beanName?+?"."?+?property,?value);??
- ????????????}??
- ????????});??
- ????????this.propReader.registerBeanDefinitions(props);??
- ????}??
- ??
- }??
此外,通過理解PropertiesBeanDefinitionReader的實現方式,發現也可以通過擴展BeanDefinitionReader來擴展bean定義,我們可以通過繼承AbstractBeanDefinitionReader來完成這種擴展。