Cao worker said Spring Boot source (4) - I is how the custom ApplicationContext, read bean definition from the json file?

EDITORIAL words

Background and resources:

Cao worker said the lecture series Spring Boot source (1) - Bean Definition what in the end is attached spring Mind Map Share

Project code address Mind Mapping address

Engineering Schematic:

General idea

Overall, what bean definition is that we talk about in front of a few, he says the one about; now, we will focus on how to obtain bean definition.

This time we do an experiment, is the bean definition (a total of two bean, there are dependencies, dependence is manually specified) defined in the json file, and then customize a applicationcontext, bean definiton read from the file, and finally we You can work under test.

Note Kazakhstan, rely here, and still speaking in front, are manually specify a dependency, similar @Autowired this, but also put behind will say, it must first learn to drive a manual transmission Well, is cutting?

I suggest that you look at the source code directly drag:

https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-beans-json-extend

Json file definitions

in the json file, to express bean definition, as we said earlier, it includes several basic necessary on the line, such as beanClassName. But I'm still here show a complete, but I'm also used fastjsonto generate a json years before the project, after then copied to the json file:

// 这里获取到的bean definition的实际类型是 GenericBeanDefiniton,所以序列化出来的的json,就是一个
// GenericBeanDefiniton集合的json
List<BeanDefinition> beanDefinitionList = factory.getBeanDefinitionList()
JSON.toJSONString(beanDefinitionList)

json document reads as follows:

[
  {
    "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
  }
]

You may see a little ignorant, in fact, replaced by xml, this is similar to the following:

    <bean name="testService" class="org.springframework.simple.TestService" />

    <bean id="testController" class="org.springframework.simple.TestController">
        <constructor-arg ref="testService"/>
    </bean>

Extended applicationContext

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();
        }
    }
}

Extended jsonBeanDefinitionReader

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) {
            /**
             * 1、处理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);

            /**
             * 2、处理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();
    }
}

Call it a day, to test

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);
    }
}

You can see, it has been injected into it. no problem.

to sum up

今天比较晚,写得也比较急,有问题的话,请大家务必指出,谢谢大家

源码地址:

https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-beans-json-extend

Guess you like

Origin www.cnblogs.com/grey-wolf/p/12078673.html