zoukankan      html  css  js  c++  java
  • Disconf源码分析之启动过程分析下(2)

    接上文,下面是第二次扫描的XML配置。

    <bean id="disconfMgrBean2" class="com.baidu.disconf.client.DisconfMgrBeanSecond"
          init-method="init" destroy-method="destroy">
    </bean>
    

    查看init()方法,会调用DisconfMgr的secondScan()方法。

    protected synchronized void secondScan() {
    	// 上面是顺序的校验
        try {
            // 扫描回调函数
            if (scanMgr != null) {
                scanMgr.secondScan();
            }
    
            // 注入数据至配置实体中
            // 获取数据/注入/Watch
            if (disconfCoreMgr != null) {
                disconfCoreMgr.inject2DisconfInstance();
            }
    
        } catch (Exception e) {
            LOGGER.error(e.toString(), e);
        }
    
        isSecondInit = true;
    }
    

    scanMgr是扫描处理器,调用第二次扫描的secondScan()方法。主要处理如下

    // 将回调函数实例化并写入仓库
    ScanDynamicStoreAdapter.scanUpdateCallbacks(scanModel, registry);
    

    ScanDynamicStoreAdapter是动态扫描与Store模块的转换器。下面主要对回调函数的处理。

    // ScanStaticModel是第一次扫描结束得到的静态配置存储的对象
    public static void scanUpdateCallbacks(ScanStaticModel scanModel, Registry registry) {
        // 扫描出来
        ScanDynamicModel scanDynamicModel = analysis4DisconfUpdate(scanModel, registry);
    
        // 写到仓库中
        transformUpdateService(scanDynamicModel.getDisconfUpdateServiceInverseIndexMap());
        transformPipelineService(scanDynamicModel.getDisconfUpdatePipeline());
    }
    

    analysis4DisconfUpdate()会将配置中和回调相关的配置扫描处理。

    private static ScanDynamicModel analysis4DisconfUpdate(ScanStaticModel scanModel, Registry registry) {
    
        // 配置项或文件,DisconfKey通过配置类型和名称来标记一个配置key
        Map<DisconfKey, List<IDisconfUpdate>> inverseMap = new HashMap<DisconfKey, List<IDisconfUpdate>>();
    
    	// disconfUpdateService是第一次扫描和回调相关的配置,@DisconfUpdateService注解
        Set<Class<?>> disconfUpdateServiceSet = scanModel.getDisconfUpdateService();
        for (Class<?> disconfUpdateServiceClass : disconfUpdateServiceSet) {
    
            // 回调对应的参数
            DisconfUpdateService disconfUpdateService =
                    disconfUpdateServiceClass.getAnnotation(DisconfUpdateService.class);
    
            // 校验是否有继承正确,是否继承IDisconfUpdate
            if (!ScanVerify.hasIDisconfUpdate(disconfUpdateServiceClass)) {
                continue;
            }
    
            // 获取回调接口实例
            IDisconfUpdate iDisconfUpdate = getIDisconfUpdateInstance(disconfUpdateServiceClass, registry);
            if (iDisconfUpdate == null) {
                continue;
            }
    
            // 主要逻辑,将DisconfKey作为key、回调接口作为list vlaue,存入到inverseMap中
            // 配置项
            processItems(inverseMap, disconfUpdateService, iDisconfUpdate);
    
            //
            // 配置文件
            processFiles(inverseMap, disconfUpdateService, iDisconfUpdate);
    
        }
    
        // set data,存储所有和回调相关配置结果集合
        ScanDynamicModel scanDynamicModel = new ScanDynamicModel();
        scanDynamicModel.setDisconfUpdateServiceInverseIndexMap(inverseMap);
    
        //
        // set update pipeline,实现iDisconfUpdatePipeline接口的处理
        //
        if (scanModel.getiDisconfUpdatePipeline() != null) {
            IDisconfUpdatePipeline iDisconfUpdatePipeline = getIDisconfUpdatePipelineInstance(scanModel
                    .getiDisconfUpdatePipeline(), registry);
            if (iDisconfUpdatePipeline != null) {
                // 存储到scanDynamicModel中
                scanDynamicModel.setDisconfUpdatePipeline(iDisconfUpdatePipeline);
            }
        }
    
        return scanDynamicModel;
    }
    

    返回以后,会将结果存储到Store仓库中。两种回调方式分别处理。

    transformUpdateService(scanDynamicModel.getDisconfUpdateServiceInverseIndexMap());
    transformPipelineService(scanDynamicModel.getDisconfUpdatePipeline());
    
     private static void transformUpdateService(Map<DisconfKey,
                List<IDisconfUpdate>> disconfUpdateServiceInverseIndexMap) {
    	// 分别取出配置文件仓库和配置项仓库处理器
        DisconfStoreProcessor disconfStoreProcessorFile = DisconfStoreProcessorFactory.getDisconfStoreFileProcessor();
        DisconfStoreProcessor disconfStoreProcessorItem = DisconfStoreProcessorFactory.getDisconfStoreItemProcessor();
        for (DisconfKey disconfKey : disconfUpdateServiceInverseIndexMap.keySet()) {
            try {
                if (disconfKey.getDisConfigTypeEnum().equals(DisConfigTypeEnum.FILE)) {
    				// 如果是文件,第一次静态扫描结束后,肯定会有对应的配置值
                    if (!disconfStoreProcessorFile.hasThisConf(disconfKey.getKey())) {
                        throw new Exception();
                    }
                    // 存储到仓库的回调函数属性中
                    disconfStoreProcessorFile.addUpdateCallbackList(disconfKey.getKey(),
                            disconfUpdateServiceInverseIndexMap
                                    .get(disconfKey));
    
                } else if (disconfKey.getDisConfigTypeEnum().equals(DisConfigTypeEnum.ITEM)) {
    				// 配置项
                    if (!disconfStoreProcessorItem.hasThisConf(disconfKey.getKey())) {
                        throw new Exception();
                    }
    				// 存储到仓库的回调函数属性中
                    disconfStoreProcessorItem.addUpdateCallbackList(disconfKey.getKey(),
                            disconfUpdateServiceInverseIndexMap
                                    .get(disconfKey));
                }
    
            } catch (Exception e) {
                // 找不到回调对应的配置,这是用户配置 错误了
                StringBuffer sb = new StringBuffer();
                sb.append("cannot find " + disconfKey + "for: ");
                for (IDisconfUpdate serClass : disconfUpdateServiceInverseIndexMap.get(disconfKey)) {
                    sb.append(serClass.toString() + "	");
                }
                LOGGER.error(sb.toString());
            }
        }
    }
    

    对于pipeline回调函数类似的处理。

    继续第二次扫描。

    // 注入数据至配置实体中
    // 获取数据/注入/Watch
    if (disconfCoreMgr != null) {
        disconfCoreMgr.inject2DisconfInstance();
    }
    

    该方法的处理,会分别处理File和item两项,分别调用DisconfCoreProcessor实现类(和第一次扫描处理类似)

    for (DisconfCoreProcessor disconfCoreProcessor : disconfCoreProcessorList) {
        disconfCoreProcessor.inject2Conf();
    }
    

    下面已File处理为例分析:

    inject2Conf()的处理逻辑。

    Object object;
    try {
        object = disconfCenterFile.getObject();
        if (object == null) {
      		// 从上下文获取实例
            object = registry.getFirstByType(disconfCenterFile.getCls(), false, true);
        }
    } catch (Exception e) {
        LOGGER.error(e.toString());
        object = null;
    }
    // 注入实体中
    disconfStoreProcessor.inject2Instance(object, fileName);
    

    继续向下看。

    @Override
    public void inject2Instance(Object object, String fileName) {
    	// 先取出配置存储对象
        DisconfCenterFile disconfCenterFile = getInstance().getConfFileMap().get(fileName);
    
        // 校验是否存在
        if (disconfCenterFile == null) {
            LOGGER.error("cannot find " + fileName + " in store....");
            return;
        }
        //
        // 非静态类
        //
        if (object != null) {
            // 设置object
            disconfCenterFile.setObject(object);
        }
    
        // 根据类型设置值
        //
        // 注入实体
        //
        Map<String, FileItemValue> keMap = disconfCenterFile.getKeyMaps();
        for (String fileItem : keMap.keySet()) {
    
            // 根据类型设置值
            try {
    
                //
                // 静态类
                //
                if (object == null) {
    
                    if (keMap.get(fileItem).isStatic()) {
                        LOGGER.debug(fileItem + " is a static field. ");
                        keMap.get(fileItem).setValue4StaticFileItem(keMap.get(fileItem).getValue());
                    }
    
                    //
                    // 非静态类
                    //
                } else {
    
                    LOGGER.debug(fileItem + " is a non-static field. ");
    
                    if (keMap.get(fileItem).getValue() == null) {
    
                        // 如果仓库值为空,则实例 直接使用默认值
                        Object defaultValue = keMap.get(fileItem).getFieldDefaultValue(object);
                        keMap.get(fileItem).setValue(defaultValue);
    
                    } else {
    
                        // 如果仓库里的值为非空,则实例使用仓库里的值
                        keMap.get(fileItem).setValue4FileItem(object, keMap.get(fileItem).getValue());
                    }
                }
    
            } catch (Exception e) {
                LOGGER.error("inject2Instance fileName " + fileName + " " + e.toString(), e);
            }
        }
    }
    

    分别对静态和非静态对象属性赋值。

    到这里位置第二次扫描结束了。

    通过两次扫描加载的数据,都是通过注解式的分布式配置方式,Disconf同时支持XML非注解式配置方式,在上篇介绍的时候,我们留下了关于XML载入的配置处理的分析,下面分析下XML非注解式配置的源码。

    对于非注解式配置,Disconf主要区分为properties文件和非properties文件(properties文件才支持自动reload)、是否自动载入reload到bean对象中(通过XML配置决定)。

    我们先分析支持自动reload。

    <!-- 使用托管方式的disconf配置(无代码侵入, 配置更改会自动reload)-->
    <bean id="configproperties_disconf"
          class="com.baidu.disconf.client.addons.properties.ReloadablePropertiesFactoryBean">
        <property name="locations">
            <list>
                <value>classpath:/autoconfig.properties</value>
                <value>classpath:/autoconfig2.properties</value>
            </list>
        </property>
    </bean>
    
    <bean id="propertyConfigurer"
          class="com.baidu.disconf.client.addons.properties.ReloadingPropertyPlaceholderConfigurer">
        <property name="ignoreResourceNotFound" value="true" />
        <property name="ignoreUnresolvablePlaceholders" value="true" />
        <property name="propertiesArray">
            <list>
                <ref bean="configproperties_disconf"/>
            </list>
        </property>
    </bean>
    

    解读上面的配置前,先了解下Spring提供的PropertyPlaceholderConfigurer类 ,它支持将properties文件中的配置项读取并在XML中通过#{}的方式读取,他的触发是因为实现了BeanFactoryPostProcessor接口,扩展了postProcessBeanFactory方法。

    而Disconf就是在此基础上继续扩展,ReloadingPropertyPlaceholderConfigurer继承了PropertyPlaceholderConfigurer类。

    首先看下ReloadablePropertiesFactoryBean类,它继承了PropertiesLoaderSupport类,入口是setLocations()方法。

    public void setLocations(List<String> fileNames) {
        List<Resource> resources = new ArrayList<Resource>();
        for (String filename : fileNames) {
            // trim
            filename = filename.trim();
            String realFileName = getFileName(filename);
            //
            // register to disconf
            // 开始扫描,可以参考上文文件和配置项的扫描
            //
            DisconfMgr.getInstance().reloadableScan(realFileName);
    
            //
            // only properties will reload
            //
            String ext = FilenameUtils.getExtension(filename);
            if (ext.equals("properties")) {
    
                PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver =
                        new PathMatchingResourcePatternResolver();
                try {
                    Resource[] resourceList = pathMatchingResourcePatternResolver.getResources(filename);
                    for (Resource resource : resourceList) {
                        resources.add(resource);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        this.locations = resources.toArray(new Resource[resources.size()]);
        lastModified = new long[locations.length];
        super.setLocations(locations);
    }
    

    reloadableScan()方法如下:

    public synchronized void reloadableScan(String fileName) {
        if (!isFirstInit) {
            return;
        }
        if (DisClientConfig.getInstance().ENABLE_DISCONF) {
            try {
            	// 判断是不是忽略同步的文件
                if (!DisClientConfig.getInstance().getIgnoreDisconfKeySet().contains(fileName)) {
                    if (scanMgr != null) {
                    	// 扫描配置
                        scanMgr.reloadableScan(fileName);
                    }
                    if (disconfCoreMgr != null) {
                        // 核心处理器处理
                        disconfCoreMgr.processFile(fileName);
                    }
                    LOGGER.debug("disconf reloadable file: {}", fileName);
                }
            } catch (Exception e) {
                LOGGER.error(e.toString(), e);
            }
        }
    }
    

    scanMgr.reloadableScan()会执行StaticScannerNonAnnotationFileMgrImpl.scanData2Store();,注意StaticScannerNonAnnotationFileMgrImpl,在上文中,我们介绍了,扫描工具主要包括三种:文件、配置项、非注解配置。在第一次扫描的时候,非注解配置因为list为空(详细看上文),所以没有执行后面的逻辑。

    继续看非注解扫描工具处理。

    public static void scanData2Store(String fileName) {
    	// 组装仓库对象,和注解文件的不同在于,文件注解本身就是一个对象,非注解配置是一个配置文件,需要转换为对象。
    	// 组装的过程,存在disconfCenterFile.setIsTaggedWithNonAnnotationFile(true);设置。
    	// 在第一次扫描的时候,有提到,如果是非注解的,该属性会覆盖之前注解的仓库对象
        DisconfCenterBaseModel disconfCenterBaseModel =
                    StaticScannerNonAnnotationFileMgrImpl.getDisconfCenterFile(fileName);
        // 因为非注解配置肯定文件,所以调用文件仓库处理器,后面的逻辑参考上文    DisconfStoreProcessorFactory.getDisconfStoreFileProcessor().transformScanData(disconfCenterBaseModel);
        }
    

    扫描完成以后,开始核心处理器处理。

    /**
     * 只处理某一个
     */
    @Override
    public void processFile(String fileName) {
    	// 获取配置文件核心处理器,原理和上文一样
        DisconfCoreProcessor disconfCoreProcessorFile =
                DisconfCoreProcessorFactory.getDisconfCoreProcessorFile(watchMgr, fetcherMgr, registry);
        // 在第一次扫描的时候会调用processAllItems()处理,但是xml配置的扫描肯定是单个的,所以直接调用单个处理
        disconfCoreProcessorFile.processOneItem(fileName);
    }
    

    再后面的处理和第一次扫描的处理是同一个方法,一個配置文件, 下载、注入到仓库、Watch 三步骤。

    继续XML配置解析,对于properties类型的文件,Spring的PropertyPlaceholderConfigurer类支持处理,所以最后将properties类型的文件设置到父类的locations属性中。setLocations()结束。

    因为ReloadablePropertiesFactoryBean继承自PropertiesFactoryBean,PropertiesFactoryBean实现了InitializingBean接口,所以在初始化的时候,会调用afterPropertiesSet()方法。

    public final void afterPropertiesSet() throws IOException {
        if(this.singleton) {
            this.singletonInstance = this.createProperties();
        }
    }
    protected Properties createProperties() throws IOException {
        return this.mergeProperties();
    }
    

    而ReloadablePropertiesFactoryBean重载了createProperties()方法。

    @Override
    protected Properties createProperties() throws IOException {
        return (Properties) createMyInstance();
    }
    
    /**
     * @throws IOException
     */
    protected Object createMyInstance() throws IOException {
        // would like to uninherit from AbstractFactoryBean (but it's final!)
        if (!isSingleton()) {
            throw new RuntimeException("ReloadablePropertiesFactoryBean only works as singleton");
        }
    
        // set listener
        reloadableProperties = new ReloadablePropertiesImpl();
        if (preListeners != null) {
            reloadableProperties.setListeners(preListeners);
        }
    
        // reload
        reload(true);
    
        // add for monitor
        ReloadConfigurationMonitor.addReconfigurableBean((ReconfigurableBean) reloadableProperties);
    
        return reloadableProperties;
    }
    

    首先看ReloadablePropertiesImpl的实现,他继承自ReloadablePropertiesBase,包含了List<IReloadablePropertiesListener> listeners监听列表。开始preListeners默认为null,直接执行reload(true),默认情况下reload方法通过判断配置文件的修改时间来确认是否重新加载,这里因为传参为true,所以强制reload()。调用ReloadablePropertiesBase的setProperties()。

    /**
     * 通过listener去通知 reload
     *
     * @param oldProperties
     */
    protected void notifyPropertiesChanged(Properties oldProperties) {
        PropertiesReloadedEvent event = new PropertiesReloadedEvent(this, oldProperties);
        for (IReloadablePropertiesListener listener : listeners) {
            listener.propertiesReloaded(event);
        }
    }
    
    /**
     * set value 触发
     *
     * @param properties
     */
    protected void setProperties(Properties properties) {
        Properties oldProperties = internalProperties;
        synchronized(this) {
            internalProperties = properties;
        }
        notifyPropertiesChanged(oldProperties);
    }
    

    可以看到最后会遍历前面所说的listeners列表,如果有值的情况会调用listener的propertiesReloaded()方法去reload。IReloadablePropertiesListener接口的实现类是ReloadingPropertyPlaceholderConfigurer

    有了ReloadablePropertiesFactoryBean以后,Disconf支持两种非注解式处理,分别的Spring自带的PropertyPlaceholderConfigurer和ReloadingPropertyPlaceholderConfigurer。两者的区别是会不会自动reload。结合上面所说,如果想要自动reload,就是通过listeners列表实现。如果使用Spring自带的PropertyPlaceholderConfigurer,那么自然就不会有listener。

    当我们使用ReloadingPropertyPlaceholderConfigurer的作为XML配置时,因为实现了InitializingBean接口,所以会执行afterPropertiesSet()。

    /**
     * afterPropertiesSet
     * 将自己 添加 property listener
     */
    public void afterPropertiesSet() {
        for (Properties properties : propertiesArray) {
            if (properties instanceof ReloadableProperties) {
                logger.debug("add property listener: " + properties.toString());
                // addReloadablePropertiesListener执行了listeners.add()。
                ((ReloadableProperties) properties).addReloadablePropertiesListener(this);
            }
        }
    }
    

    在加载ReloadablePropertiesFactoryBean的时候,我们已经把所有的properties格式的文件放入到propertiesArray,所以都会加入到listener中,最后会调用propertiesReloaded()进行处理。

    至此,Disconf的启动过程分析结束。

    转载请注明出处。
    作者:wuxiwei
    出处:https://www.cnblogs.com/wxw16/p/10741202.html

  • 相关阅读:
    硬盘坏道及后续的拯救工作
    Extension GL_VERSION_1_2 could not be loaded.
    js显示当前的年月日时分秒
    如何删除桌面上的回收站?
    Fedora 15 U盘 安装心得
    庄子·内篇·逍遥游
    Additional Oracle Performance Extensions
    Features Specific to JDBC OCI Driver
    OracleDatabase 配置
    vim字符编码设置
  • 原文地址:https://www.cnblogs.com/wxw16/p/10741202.html
Copyright © 2011-2022 走看看