上海网站建设 永灿,广州互联网广告推广,站酷网素材,游戏app制作spring-framework 版本#xff1a;v5.3.19 前面研究了beanDefinition的注册#xff0c;但也仅仅是注册这一动作。那么在spring容器启动的过程中#xff0c;是何时/如何装配的#xff1f;以及装配的bean是如何注入的#xff1f; #xff08;考虑到xml方式基本不用了以及篇… spring-framework 版本v5.3.19 前面研究了beanDefinition的注册但也仅仅是注册这一动作。那么在spring容器启动的过程中是何时/如何装配的以及装配的bean是如何注入的 考虑到xml方式基本不用了以及篇幅问题所以本篇只看注解方式AnnotationConfigApplicationContext。
文章目录何时装配beanFactoryPostProcessor的执行入口ConfigurationClassPostProcessor的注册如何装配主流程解析配置类加载配置类配置的beanDefinition总结何时装配
beanFactoryPostProcessor的执行入口
实际上Bean、ComponentScan、Import配置的bean装配逻辑是在ConfigurationClassPostProcessor实现的。这个类实现了BeanDefinitionRegistryPostProcessor接口且又因为该接口继承自BeanFactoryPostProcessor所以ConfigurationClassPostProcessor就是一个BeanFactoryPostProcessor。 BeanDefinitionRegistryPostProcessor 提供了一个回调方法 postProcessBeanDefinitionRegistry()可以在这个方法中对 BeanDefinition 进行修改或者添加新的 BeanDefinition。 单从装配上来说把ConfigurationClassPostProcessor认为是BeanDefinitionRegistryPostProcessor会比BeanFactoryPostProcessor更贴切。虽然BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor但是顾名思义BeanDefinitionRegistryPostProcessor在BeanFactoryPostProcessor基础上增加了BeanDefinitionRegistry的功能。 从这一点入手即可知道Bean、ComponentScan、Import配置的bean就是在执行所有的BeanFactoryPostProcessor这一步骤装配的调用流程如下。
在实际执行BeanFactoryPostProcessor时spring容器将具体实现委托给了PostProcessorRegistrationDelegate这个工具类。PostProcessorRegistrationDelegate代码中处理BeanFactoryPostProcessord的代码如下
PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListBeanFactoryPostProcessor beanFactoryPostProcessors) {SetString processedBeans new HashSet();if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry (BeanDefinitionRegistry) beanFactory;ListBeanFactoryPostProcessor regularPostProcessors new ArrayList();ListBeanDefinitionRegistryPostProcessor registryProcessors new ArrayList();//----------遍历spring容器当前已有的beanFactoryPostProcessors---------//for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {BeanDefinitionRegistryPostProcessor registryProcessor (BeanDefinitionRegistryPostProcessor) postProcessor;registryProcessor.postProcessBeanDefinitionRegistry(registry);registryProcessors.add(registryProcessor);}else {regularPostProcessors.add(postProcessor);}}//-----------执行已注册的 BeanDefinitionRegistryPostProcessor bean--------------//ListBeanDefinitionRegistryPostProcessor currentRegistryProcessors new ArrayList();//postProcessBeanDefinitionRegistry()方法//首先先执行实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessorString[] postProcessorNames beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();//然后执行实现了Ordered接口的BeanDefinitionRegistryPostProcessorpostProcessorNames beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName) beanFactory.isTypeMatch(ppName, Ordered.class)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();//最后执行其他未被扫描到的BeanDefinitionRegistryPostProcessor// 因为处理的过程中可能会注册新的BeanDefinitionRegistryPostProcessor// 所以这里用循环确保所有的BeanDefinitionRegistryPostProcessor都会被执行boolean reiterate true;while (reiterate) {reiterate false;postProcessorNames beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);reiterate true;}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();}// postProcessBeanFactory()方法invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);}else {//没有实现BeanDefinitionRegistry接口则直接执行执行BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);}//--------------------------执行已注册的 BeanFactoryPostProcessor bean------------------------------//String[] postProcessorNames beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);// 跟上面一样// 都是先按 实现了PriorityOrdered接口、实现了Ordered接口、其他 的顺序处理 BeanFactoryPostProcessorListBeanFactoryPostProcessor priorityOrderedPostProcessors new ArrayList();ListString orderedPostProcessorNames new ArrayList();ListString nonOrderedPostProcessorNames new ArrayList();for (String ppName : postProcessorNames) {if (processedBeans.contains(ppName)) {// 跳过已经执行的BeanFactoryPostProcessor}else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}sortPostProcessors(priorityOrderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);ListBeanFactoryPostProcessor orderedPostProcessors new ArrayList(orderedPostProcessorNames.size());for (String postProcessorName : orderedPostProcessorNames) {orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}sortPostProcessors(orderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);ListBeanFactoryPostProcessor nonOrderedPostProcessors new ArrayList(nonOrderedPostProcessorNames.size());for (String postProcessorName : nonOrderedPostProcessorNames) {nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);beanFactory.clearMetadataCache();}这个方法首先会判断beanFactory是否来自BeanDefinitionRegistry如果不是则直接执行beanFactory.beanFactoryPostProcessors属性指向的容器本身已有的BeanFactoryPostProcessors如果是则保证先执行所有的BeanDefinitionRegistryPostProcessor包括BeanDefinition中注册的后再执行BeanFactoryPostProcessors包括BeanDefinition中注册的。两者的执行都按 本身已有的-BeanDefinition中PriorityOrdered修饰的-BeanDefinition中Ordered修饰的-BeanDefinition中其他未被执行的顺序执行
ConfigurationClassPostProcessor的注册
上面展示了spring容器在启动的过程中是如何执行BeanFactoryPostProcessors的。那么问题来了ConfigurationClassPostProcessor这个BeanFactoryPostProcessors是spring容器自带的还是BeanDefinition中注册的呢如果是注册的那是何时注册的呢 我们首先排除容器自带的通过debug代码可以发现当执行invokeBeanFactoryPostProcessors方法时spring容器自带的BeanFactoryPostProcessors即传进来的beanFactoryPostProcessors参数为空。 既然不是自带的BeanFactoryPostProcessors那就肯定在前面的某一步中注册了ConfigurationClassPostProcessor这么一个BeanDefinition。 通过debug代码分析可以得知在执行refresh方法前甚至在register方法执行前就已经存在beanName为org.springframework.context.annotation.internalConfigurationAnnotationProcessor的ConfigurationClassPostProcessor bean了。由此可以推断是在AnnotationConfigApplicationContext容器初始化的时候注册的。 查看初始化流程可以发现创建reader时会执行AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry) 在AnnotationConfigUtils.registerAnnotationConfigProcessors中会默认注册一些beanDefinitionConfigurationClassPostProcessor就是在这里注册的 如何装配
主流程
接下来看ConfigurationClassPostProcessor的具体注册beanDefinition的逻辑 ConfigurationClassPostProcessor#registerAnnotationConfigProcessors
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {ListBeanDefinitionHolder configCandidates new ArrayList();String[] candidateNames registry.getBeanDefinitionNames();// 遍历 BeanDefinition 找到所有的 Configuration 配置类for (String beanName : candidateNames) {BeanDefinition beanDef registry.getBeanDefinition(beanName);// 如果CONFIGURATION_CLASS_ATTRIBUTE属性不为空说明已经处理过了打印日志if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) ! null) {if (logger.isDebugEnabled()) {logger.debug(Bean definition has already been processed as a configuration class: beanDef);}}else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// 没有配置类直接返回if (configCandidates.isEmpty()) {return;}// 配置类按order注解排序configCandidates.sort((bd1, bd2) - {int i1 ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});// beanName生成器SingletonBeanRegistry sbr null;if (registry instanceof SingletonBeanRegistry) {sbr (SingletonBeanRegistry) registry;if (!this.localBeanNameGeneratorSet) {BeanNameGenerator generator (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);if (generator ! null) {this.componentScanBeanNameGenerator generator;this.importBeanNameGenerator generator;}}}// 环境变量if (this.environment null) {this.environment new StandardEnvironment();}// 解析 Configuration class 并注册解析到的beanConfigurationClassParser parser new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);SetBeanDefinitionHolder candidates new LinkedHashSet(configCandidates);SetConfigurationClass alreadyParsed new HashSet(configCandidates.size());do {StartupStep processConfig this.applicationStartup.start(spring.context.config-classes.parse);// 解析Configuration配置类parser.parse(candidates);parser.validate();SetConfigurationClass configClasses new LinkedHashSet(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);if (this.reader null) {this.reader new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 注册configurationClass 配置的beanthis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);processConfig.tag(classCount, () - String.valueOf(configClasses.size())).end();// 找出新注册的BeanDefinition再次进入循环candidates.clear();if (registry.getBeanDefinitionCount() candidateNames.length) {String[] newCandidateNames registry.getBeanDefinitionNames();SetString oldCandidateNames new HashSet(Arrays.asList(candidateNames));SetString alreadyParsedClasses new HashSet();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) !alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames newCandidateNames;}}while (!candidates.isEmpty());if (sbr ! null !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());}if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();}}说通俗一点就是 1找到所有配置类 2解析配置类 3加载配置类配置的beanDefinition 4循环23直至没有新的配置类加载
解析配置类
点进去parser.parse(candidates) 最终会来到ConfigurationClassParser#doProcessConfigurationClass
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, PredicateString filter)throws IOException {if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// 解析成员类processMemberClasses(configClass, sourceClass, filter);}// 解析 PropertySource 注解for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info(Ignoring PropertySource annotation on [ sourceClass.getMetadata().getClassName() ]. Reason: Environment must implement ConfigurableEnvironment);}}// 解析 ComponentScan 注解SetAnnotationAttributes componentScans AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {//最终会调用ClassPathBeanDefinitionScanner.doScan()方法实现beanDefinition的注册SetBeanDefinitionHolder scannedBeanDefinitions this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());for (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand null) {bdCand holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// 解析 Import 注解processImports(configClass, sourceClass, getImports(sourceClass), filter, true);// 解析 ImportResource 注解AnnotationAttributes importResource AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource ! null) {String[] resources importResource.getStringArray(locations);Class? extends BeanDefinitionReader readerClass importResource.getClass(reader);for (String resource : resources) {String resolvedResource this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// 解析 Bean 注解与Component不同的是这里不会立即注册到beanDefinitionMap中是在后续的loadBeanDefinition方法才注册SetMethodMetadata beanMethods retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// 递归接口的默认实现processInterfaces(configClass, sourceClass);// 递归父类if (sourceClass.getMetadata().hasSuperClass()) {String superclass sourceClass.getMetadata().getSuperClassName();if (superclass ! null !superclass.startsWith(java) !this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);return sourceClass.getSuperClass();}}return null;}在这一步会解析配置类的 成员类、PropertySource、ComponentScan、Import、ImportResource、Bean 以及递归接口默认实现和递归父类将解析到的结果封装成ConfigurationClass保存到parse.configurationClasses属性然后再调用reader.loadBeanDefinitions(configClasses)方法实现beanDefintion的加载。 需要注意的是ComponentScan会在解析这一步的时先将对应的beanDefinition注册到map中了而Import、Bean注解的beanDefinition是在后续的loadBeanDefinitions才注册到map中的。 加载配置类配置的beanDefinition
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions 可以看到Import和Bean配置的bean都在这一步注册了。 上图提到了两次Import注解但作用是不一样的。第一次如果这个类是被Import进来的则先注册他自身第二次是注册他Import过来类。不过这一步最主要也是最常用的还是注册了Bean注解的bean。 总结
在启动AnnotationConfigApplicationContext容器创建reader时会默认注册一些beanDefinition其中就包括ConfigurationClassPostProcessor。ConfigurationClassPostProcessor又是一个beanFactoryPostProcessor所以后续refresh中invokeBeanFactoryPostProcessors方法执行所有BeanFactoryPostProcessor的方法时就会调用ConfigurationClassPostProcessor的相关逻辑实现Bean、ComponentScan、Import配置的bean的装配。 实现大致如下 1找到所有配置类 2解析配置类ComponentScan在这一步注册 3加载配置类配置的beanDefinitionImportBean在这一步注册 4循环23直至没有新的配置类加载 最后的最后 ComponentScan调用ClassPathBeanDefinitionScanner.doScan() Bean调用BeanDefinitionRegistry.registerBeanDefinition() Import调用ImportBeanDefinitionRegistrar.registerBeanDefinitions() 实现beanDefinition的注册。 文章转载自: http://www.morning.nypsz.cn.gov.cn.nypsz.cn http://www.morning.rycbz.cn.gov.cn.rycbz.cn http://www.morning.dangaw.com.gov.cn.dangaw.com http://www.morning.xlxmy.cn.gov.cn.xlxmy.cn http://www.morning.csnmd.cn.gov.cn.csnmd.cn http://www.morning.yqpzl.cn.gov.cn.yqpzl.cn http://www.morning.zfkxj.cn.gov.cn.zfkxj.cn http://www.morning.rmrcc.cn.gov.cn.rmrcc.cn http://www.morning.jzccn.cn.gov.cn.jzccn.cn http://www.morning.fkgqn.cn.gov.cn.fkgqn.cn http://www.morning.dzyxr.cn.gov.cn.dzyxr.cn http://www.morning.pttrs.cn.gov.cn.pttrs.cn http://www.morning.fsnhz.cn.gov.cn.fsnhz.cn http://www.morning.gwwtm.cn.gov.cn.gwwtm.cn http://www.morning.rfyff.cn.gov.cn.rfyff.cn http://www.morning.sjsks.cn.gov.cn.sjsks.cn http://www.morning.lpyjq.cn.gov.cn.lpyjq.cn http://www.morning.shuanga.com.cn.gov.cn.shuanga.com.cn http://www.morning.qyfqx.cn.gov.cn.qyfqx.cn http://www.morning.zyslyq.cn.gov.cn.zyslyq.cn http://www.morning.xgkxy.cn.gov.cn.xgkxy.cn http://www.morning.kbntl.cn.gov.cn.kbntl.cn http://www.morning.wflsk.cn.gov.cn.wflsk.cn http://www.morning.zcsch.cn.gov.cn.zcsch.cn http://www.morning.nfccq.cn.gov.cn.nfccq.cn http://www.morning.jyznn.cn.gov.cn.jyznn.cn http://www.morning.hctgn.cn.gov.cn.hctgn.cn http://www.morning.jppdk.cn.gov.cn.jppdk.cn http://www.morning.21r000.cn.gov.cn.21r000.cn http://www.morning.rshkh.cn.gov.cn.rshkh.cn http://www.morning.mjyrg.cn.gov.cn.mjyrg.cn http://www.morning.dfojgo.cn.gov.cn.dfojgo.cn http://www.morning.dzgyr.cn.gov.cn.dzgyr.cn http://www.morning.jrtjc.cn.gov.cn.jrtjc.cn http://www.morning.yckrm.cn.gov.cn.yckrm.cn http://www.morning.hpkgm.cn.gov.cn.hpkgm.cn http://www.morning.jbfjp.cn.gov.cn.jbfjp.cn http://www.morning.znkls.cn.gov.cn.znkls.cn http://www.morning.tngdn.cn.gov.cn.tngdn.cn http://www.morning.fqklt.cn.gov.cn.fqklt.cn http://www.morning.gkxyy.cn.gov.cn.gkxyy.cn http://www.morning.ycpnm.cn.gov.cn.ycpnm.cn http://www.morning.flncd.cn.gov.cn.flncd.cn http://www.morning.yswxq.cn.gov.cn.yswxq.cn http://www.morning.jmmzt.cn.gov.cn.jmmzt.cn http://www.morning.mfnjk.cn.gov.cn.mfnjk.cn http://www.morning.cbchz.cn.gov.cn.cbchz.cn http://www.morning.rgtp.cn.gov.cn.rgtp.cn http://www.morning.hmnhp.cn.gov.cn.hmnhp.cn http://www.morning.mtxrq.cn.gov.cn.mtxrq.cn http://www.morning.mznqz.cn.gov.cn.mznqz.cn http://www.morning.mzbyl.cn.gov.cn.mzbyl.cn http://www.morning.nbgfk.cn.gov.cn.nbgfk.cn http://www.morning.lgznf.cn.gov.cn.lgznf.cn http://www.morning.brlcj.cn.gov.cn.brlcj.cn http://www.morning.krklj.cn.gov.cn.krklj.cn http://www.morning.pwrkl.cn.gov.cn.pwrkl.cn http://www.morning.qkcyk.cn.gov.cn.qkcyk.cn http://www.morning.zpyh.cn.gov.cn.zpyh.cn http://www.morning.nrcbx.cn.gov.cn.nrcbx.cn http://www.morning.qrmyd.cn.gov.cn.qrmyd.cn http://www.morning.jlxld.cn.gov.cn.jlxld.cn http://www.morning.qstkk.cn.gov.cn.qstkk.cn http://www.morning.mlfgx.cn.gov.cn.mlfgx.cn http://www.morning.zfcfx.cn.gov.cn.zfcfx.cn http://www.morning.pzss.cn.gov.cn.pzss.cn http://www.morning.qbgff.cn.gov.cn.qbgff.cn http://www.morning.smsjx.cn.gov.cn.smsjx.cn http://www.morning.rqdx.cn.gov.cn.rqdx.cn http://www.morning.mlbdr.cn.gov.cn.mlbdr.cn http://www.morning.rnds.cn.gov.cn.rnds.cn http://www.morning.nmfwm.cn.gov.cn.nmfwm.cn http://www.morning.hilmwmu.cn.gov.cn.hilmwmu.cn http://www.morning.rbhqz.cn.gov.cn.rbhqz.cn http://www.morning.rjjjk.cn.gov.cn.rjjjk.cn http://www.morning.kgrwh.cn.gov.cn.kgrwh.cn http://www.morning.dmcxh.cn.gov.cn.dmcxh.cn http://www.morning.fthqc.cn.gov.cn.fthqc.cn http://www.morning.ghxsn.cn.gov.cn.ghxsn.cn http://www.morning.blfll.cn.gov.cn.blfll.cn