曹操の労働者は語った春のブートソース(4) - 私はApplicationContextのカスタムは、JSONファイルからBean定義を読んでどのようにでしょうか?

EDITORIAL言葉

背景とリソース:

曹操の労働者は、講義シリーズ春ブートソース(1)と述べた - 最後に春マインドマップを共有添付されているものBean定義

プロジェクトコードアドレス マインドマッピングアドレス

エンジニアリング回路図:

概念

全体的に、どのような豆の定義は、我々はいくつかの前にについて話すということです、彼は約言う;今、私たちは、Bean定義を取得する方法に焦点を当てます。

我々は実験を行う。この時間は、Bean定義である(2つの豆の合計、依存関係があり、依存性が手動で指定された)JSONファイルで定義され、その後のApplicationContextをカスタマイズし、豆definitonがファイルから読み込まれ、最終的に我々あなたは、テストの下で働くことができます。

注カザフスタンは、言うだろう、ここで依存している、とまだ前で言えば、手動で、依存関係を指定する同様の@Autowiredこれをするだけでなく、背後に置かれ、それが最初のマニュアルトランスミッションまあを駆動するために学ばなければならない、カッティングのですか?

私は、あなたがソースコードを直接ドラッグを見てみることをお奨め:

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

JSONファイルの定義

JSONファイルには、我々が先に言ったように、そのようなbeanClassNameとしてライン、上のいくつかの基本的な必要含み、Bean定義を表現します。しかし、私は完全に示し、ここではまだだけど、私も使用してい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>

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

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

テストに、一日それを呼びます

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

あなたが見ることができる、それはそれに注入されています。問題ありません。

概要

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

源码地址:

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

おすすめ

転載: www.cnblogs.com/grey-wolf/p/12078673.html