相關背景及資源:html
曹工說Spring Boot源碼系列開講了(1)-- Bean Definition究竟是什麼,附spring思惟導圖分享java
工程結構圖:spring
整體來講,bean definition是什麼,咱們前面幾講,說了個大概了;目前,咱們將聚焦於怎麼獲取bean definition。json
咱們此次作個實驗,就是將bean definition(一共兩個bean,有依賴關係,依賴是手動指定的)定義在json文件內,而後自定義一個applicationcontext,從該文件內讀取bean definiton,最後咱們測試下是否能work。app
注意哈,這裏的依賴,依然和前面講的同樣,都是手動指定依賴,相似@Autowired這種,還會放到後面纔會講,開車也要先學手動檔嘛,是伐?ide
建議你們直接拖源碼下來看:spring-boot
json文件內,要表達bean definition,按照咱們前面說的,基本就包括幾個必要的就好了,好比beanClassName。但我這裏仍是展現一個完整的,但我也是用fastjson
先在以前的工程裏生成了一個json,以後再拷貝到了json文件裏:測試
// 這裏獲取到的bean definition的實際類型是 GenericBeanDefiniton,因此序列化出來的的json,就是一個 // GenericBeanDefiniton集合的json List<BeanDefinition> beanDefinitionList = factory.getBeanDefinitionList() JSON.toJSONString(beanDefinitionList)
json文件內容以下:
[ { "abstract": false, "autowireCandidate": true, "autowireMode": 0, "beanClass": "org.springframework.simple.TestService", "beanClassName": "org.springframework.simple.TestService", "constructorArgumentValues": { "argumentCount": 0, "empty": true, "genericArgumentValues": [], "indexedArgumentValues": {} }, "dependencyCheck": 0, "enforceDestroyMethod": true, "enforceInitMethod": true, "lazyInit": false, "lenientConstructorResolution": true, "methodOverrides": { "empty": true, "overrides": [] }, "nonPublicAccessAllowed": true, "primary": false, "propertyValues": { "converted": false, "empty": true, "propertyValueList": [], "propertyValues": [] }, "prototype": false, "qualifiers": [], "resolvedAutowireMode": 0, "role": 0, "scope": "", "singleton": true, "synthetic": false }, { "abstract": false, "autowireCandidate": true, "autowireMode": 0, "beanClass": "org.springframework.simple.byconstructor.TestControllerByConstructor", "beanClassName": "org.springframework.simple.byconstructor.TestControllerByConstructor", "constructorArgumentValues": { "argumentCount": 2, "empty": false, "genericArgumentValues": [], "indexedArgumentValues": { 0: { "converted": false, "value": { "beanName": "testService", "toParent": false } }, 1: { "converted": false, "value": "wire by constructor" } } }, "dependencyCheck": 0, "enforceDestroyMethod": true, "enforceInitMethod": true, "lazyInit": false, "lenientConstructorResolution": true, "methodOverrides": { "empty": true, "overrides": [] }, "nonPublicAccessAllowed": true, "primary": false, "propertyValues": { "converted": false, "empty": true, "propertyValueList": [], "propertyValues": [] }, "prototype": false, "qualifiers": [], "resolvedAutowireMode": 0, "role": 0, "scope": "", "singleton": true, "synthetic": false } ]
你們可能看得有點懵,其實換成xml,就是相似下面這樣的:
<bean name="testService" class="org.springframework.simple.TestService" /> <bean id="testController" class="org.springframework.simple.TestController"> <constructor-arg ref="testService"/> </bean>
package org.springframework.beans.extend.json.applicationcontext; import org.springframework.beans.BeansException; import org.springframework.beans.extend.json.JsonBeanDefinitionReader; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.ResourceEntityResolver; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractRefreshableConfigApplicationContext; import java.io.IOException; public class ClassPathJsonApplicationContext extends AbstractRefreshableConfigApplicationContext { @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { //其實主要內容和xmlapplicationcontext是同樣的,主要就是下面這行不同,new了一個json reader JsonBeanDefinitionReader beanDefinitionReader = new JsonBeanDefinitionReader(beanFactory); beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); // 這裏經過json bean definiton reader去讀取bean definition loadBeanDefinitions(beanDefinitionReader); } /** *經過json bean definiton reader去讀取bean definition **/ protected void loadBeanDefinitions(JsonBeanDefinitionReader reader) throws BeansException, IOException { // 這裏獲取json文件的path,這個location是在new ClassPathJsonApplicationContext時傳進來的 String[] configResources = getConfigLocations(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } } public ClassPathJsonApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } /** * 這裏如出一轍,不須要任何變化 **/ public ClassPathJsonApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } }
package org.springframework.beans.extend.json; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.core.NamedThreadLocal; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.util.CollectionUtils; import org.springframework.util.StreamUtils; import org.xml.sax.InputSource; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.*; /** * 相似 * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader} * 只是本類是去json文件裏讀取bean definition * */ @Slf4j public class JsonBeanDefinitionReader extends AbstractBeanDefinitionReader { private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<Set<EncodedResource>>("json bean definition resources currently being loaded"); public JsonBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { // 如下照抄xmlbeanDefintionReader開始 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } EncodedResource encodedResource = new EncodedResource(resource); if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } //照抄xmlbeanDefintionReader結束 //這裏的encodedResource.getResource()就是咱們的json文件,這裏經過spring core裏面的一個工具類讀取爲InputStream String json = null; try (InputStream inputStream = encodedResource.getResource().getInputStream()) { json = StreamUtils.copyToString(inputStream, Charset.forName("UTF-8")); } catch (IOException e) { log.error("{}",e); return 0; } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } //熟悉的fastjson,熟悉的味道 List<GenericBeanDefinition> list = JSON.parseArray(json, GenericBeanDefinition.class); if (CollectionUtils.isEmpty(list)) { return 0; } /** * 1:由於GenericBeanDefinition,只有setBeanClassName,因此bean反序列化時,只序列化了這個字 * 段;實際咱們知道,beanClass很重要,因此咱們只能本身處理一下了 * 2:第二個問題,咱們在下面解釋 **/ for (GenericBeanDefinition genericBeanDefinition : list) { /** * 一、處理beanClass */ Class<?> clazz = null; try { clazz = Thread.currentThread().getContextClassLoader().loadClass(genericBeanDefinition.getBeanClassName()); } catch (ClassNotFoundException e) { log.error("bean class cant be load for beandefinition: {}",genericBeanDefinition); throw new RuntimeException(); } genericBeanDefinition.setBeanClass(clazz); /** * 二、處理constructor問題,由於Object value = valueHolder.getValue(); * 是Object類型,但這個實際是一個可變類型,當構造器參數爲String類型時,這個Object就是 * String類型的,當構造器參數類型爲其餘bean的引用時,這個object就是RuntimeBeanReference * 的, * 由於fastjson把個人object轉成jsonobject類型了,因此這裏要手動搞成RuntimeBeanReference */ ConstructorArgumentValues constructorArgumentValues = genericBeanDefinition.getConstructorArgumentValues(); if (constructorArgumentValues.isEmpty()) { continue; } Map<Integer, ConstructorArgumentValues.ValueHolder> map = constructorArgumentValues.getIndexedArgumentValues(); if (CollectionUtils.isEmpty(map)) { continue; } for (ConstructorArgumentValues.ValueHolder valueHolder : map.values()) { Object value = valueHolder.getValue(); if (value instanceof JSONObject) { JSONObject jsonObject = (JSONObject) value; RuntimeBeanReference runtimeBeanReference = jsonObject.toJavaObject(RuntimeBeanReference.class); valueHolder.setValue(runtimeBeanReference); } } } //這裏new一個BeanNameGenerator,這是自帶的 setBeanNameGenerator(new AnnotationBeanNameGenerator()); BeanNameGenerator beanNameGenerator = getBeanNameGenerator(); // 獲取BeanDefinitionRegistry,bean factory默認實現了BeanDefinitionRegistry BeanDefinitionRegistry registry = getRegistry(); //註冊bean definition到BeanDefinitionRegistry裏面去 for (GenericBeanDefinition genericBeanDefinition : list) { String beanName = beanNameGenerator.generateBeanName(genericBeanDefinition, registry); registry.registerBeanDefinition(beanName,genericBeanDefinition); } return list.size(); } }
public class BootStrap { public static void main(String[] args) { // new一個咱們的自定義json上下文 ClassPathJsonApplicationContext context = new ClassPathJsonApplicationContext("beanDefinition.json"); // getBean試一下 TestControllerByConstructor bean = context.getBean(TestControllerByConstructor.class); System.out.println(bean); } }
能夠看到,已經注入進去了。沒有什麼問題。
今天比較晚,寫得也比較急,有問題的話,請你們務必指出,謝謝你們
源碼地址: