天天快报!【Spring源码】- 08 扩展点之mybatis集成
概述
mybatis
将与spring
集成的代码拆分到了mybatis-spring
模块,避免mybatis
与spring
之间的耦合,如果你只需要纯粹的使用mybatis api
,就避免了必须将spring
依赖也耦合进来的问题。mybatis
使用中一般是将Sql
语句写在xml
文件中,为方便操作,我们会创建一个Mapper
接口文件进行映射,mybatis
提供了采用动态代理方式对Mapper
接口类进行包装,这样我们就可以像使用普通对象一样执行各种方法调用。
mybatis
和spring
集成的一个核心任务就是将这些动态代理包装的Mapper
对象注入到IoC
容器中,这样其它Bean
就可以方便的使用如@Autowired
等方式进行依赖注入。
(资料图)
MapperScannerConfigurer
需要将mybatis
生成的动态代理对象注入到IoC
容器中,自然我们想到之前的BeanFactoryPostProcessor
的子类BeanDefinitionRegistryPostProcessor
这个扩展类。MapperScannerConfigurer
就是实现了BeanDefinitionRegistryPostProcessor
接口,然后在该接口中通过类扫描器scanner
进行扫描注册。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { processPropertyPlaceHolders(); } ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);//指定引用的SqlSessionFactory scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.registerFilters(); //basePackage指定扫描Mapper接口包路径 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}
ClassPathMapperScanner
这个就是继承之前介绍过的Spring
中ClassPathBeanDefinitionScanner
类扫描器进行了扩展,它可以实现将包路径下至少含有一个方法的接口类注册到IoC
中。
这里有个问题:注册进入的BeanDefinition
中beanClass
指向的都是接口,到后续创建对象时会存在问题,接口是没法创建实例的。所以,ClassPathMapperScanner
扫描器在注册完成后,又会对BeanDefinition
进行处理。处理逻辑位于ClassPathMapperScanner#processBeanDefinitions()
方法中,其核心逻辑见下:
private void processBeanDefinitions(Set beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); String beanClassName = definition.getBeanClassName(); definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59 definition.setBeanClass(this.mapperFactoryBeanClass); ... }}
其中最重要的一条语句:definition.setBeanClass(this.mapperFactoryBeanClass)
,偷偷的将BeanDefinition
的beanClass
替换成了MapperFactoryBean
,而不再指向Mapper
接口类。同时将Mapper
接口类作为参数传入到了MapperFactoryBean
中,即调用下面构造方法:
public MapperFactoryBean(Class mapperInterface) { this.mapperInterface = mapperInterface;}
MapperFactoryBean
实现了FactoryBean
接口,这样实际上它是通过getObject()
方法获取到对象然后注入到IoC
容器中。而在getObject()方法中,我们就可以使用mybatis api
获取到Mapper
接口类的动态代理对象:SqlSession#getMapper()
public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface);}
上面我们分析了如何将Mapper
接口类注入到IoC
容器中的实现思路,现在总结下主要有:
BeanDefinitionRegistryPostProcessor
接口实现扩展,然后动态向IoC
容器中注入Bean
;在注入时,会使用到ClassPathMapperScanner
类扫描器将所有的Mapper
接口类解析成BeanDefinition
集合注入;为了解决接口不能创建对象问题,再注入后又将BeanDefinition
的beanClass
替换成FactoryBean
的实现类:MapperFactoryBean
,在该实现类中通过mybatis api
:SqlSession#getMapper()
获取到Mapper
接口的动态代理类扩展点引入
通过MapperScannerConfigurer
,解决了如何将Mapper
接口类注入到IoC
容器的问题,现在还有另外一个问题,这个扩展点只有注册到Spring
中才会起作用,那又如何将其注册到Spring
中呢?
方式一:最直接方式就是直接创建MapperScannerConfigurer
类型的Bean
实例,比如:
这种方式是最简单直接的,但是使用角度来说不方便,所以,mybatis-spring-1.2
新增了两种方式:
标签方式和@MapperScan
注解方式。
首先来看下
标签方式,添加mybatis
的schema
,然后就可以使用
:
后台处理类NamespaceHandler
给
标签注册解析器MapperScannerBeanDefinitionParser
:
public class NamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser()); }}
再看下MapperScannerBeanDefinitionParser
解析器:
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); builder.addPropertyValue("processPropertyPlaceHolders", true); try { String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION); if (StringUtils.hasText(annotationClassName)) { @SuppressWarnings("unchecked") Class extends Annotation> annotationClass = (Class extends Annotation>) classLoader .loadClass(annotationClassName); builder.addPropertyValue("annotationClass", annotationClass); } String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE); if (StringUtils.hasText(markerInterfaceClassName)) { Class> markerInterface = classLoader.loadClass(markerInterfaceClassName); builder.addPropertyValue("markerInterface", markerInterface); } String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR); if (StringUtils.hasText(nameGeneratorClassName)) { Class> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName); BeanNameGenerator nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class); builder.addPropertyValue("nameGenerator", nameGenerator); } String mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS); if (StringUtils.hasText(mapperFactoryBeanClassName)) { @SuppressWarnings("unchecked") Class extends MapperFactoryBean> mapperFactoryBeanClass = (Class extends MapperFactoryBean>) classLoader .loadClass(mapperFactoryBeanClassName); builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass); } } catch (Exception ex) { XmlReaderContext readerContext = parserContext.getReaderContext(); readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause()); } builder.addPropertyValue("sqlSessionTemplateBeanName", element.getAttribute(ATTRIBUTE_TEMPLATE_REF)); builder.addPropertyValue("sqlSessionFactoryBeanName", element.getAttribute(ATTRIBUTE_FACTORY_REF)); builder.addPropertyValue("lazyInitialization", element.getAttribute(ATTRIBUTE_LAZY_INITIALIZATION)); builder.addPropertyValue("basePackage", element.getAttribute(ATTRIBUTE_BASE_PACKAGE)); return builder.getBeanDefinition(); }
最关键的就是第一句BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
,又是将MapperScannerConfigurer
动态注入到Spring
中,下面一堆都是解析标签属性进行依赖注入。
再来看下@MapperScan
注解方式,如:@MapperScan(basePackages = "org.simon.demo01.mapper")
:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(MapperScannerRegistrar.class)public @interface MapperScan {}
@MapperScan
注解上面使用了使用了一种非常常见的扩展方式:@Import
扩展。通过@Import
注解,引入了MapperScannerRegistrar
,它是ImportBeanDefinitionRegistrar
类型,通常和@Import
注解组合使用,实现动态注入功能:
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //获取注解上属性 AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0)); } } void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) { //创建一个MapperScannerConfigurer的BeanDefinition BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); /** * 下面就是解析注解属性值,通过PropertyValue方式进行依赖注入到Bean中 */ builder.addPropertyValue("processPropertyPlaceHolders", true); Class extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { builder.addPropertyValue("annotationClass", annotationClass); } Class> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { builder.addPropertyValue("markerInterface", markerInterface); } ...//各种依赖注入 builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages)); //将生成的BeanDefinition注册到IoC中 registry.registerBeanDefinition(beanName, builder.getBeanDefinition());}
方法中同样有BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class)
这句,动态的将MapperScannerConfigurer
注入到Spring
中,然后是一堆的解析注解属性进行依赖注入,这样通过@Import
+ImportBeanDefinitionRegistrar
动态注入,就实现了将MapperScannerConfigurer
扩展点注册到Spring
中。
SpringBoot自动装配
不论是通过
标签方式,还是@MapperScan
注解方式,这些是常规的第三方模块与Spring
进行集成方式。这种集成方式比较繁琐的是:你不光要通过
或@MapperScan
注解将第三方集成进来,你还需要初始化一些依赖对象,比如这里的DataSource
、SqlSessionFactory
等。当一个项目集成了很多第三方模块时,每个模块都这样搞一下,配置的工作量就大了,比如最常使用的ssm
集成配,传统Spring
集成要搞一大堆配置。
所以,SpringBoot
提出了一个比较优秀的思想:自动装配。需要什么模块直接把依赖添加进来,自动完成装配,对于个性化可以在属性文件中进行配置,从使用角度来说,即插即用,不需要有太多的编码。第三方程序和spring
就像完全融入一体一样,简化项目构建时集成成本,也降低项目配置的复杂性,所以SpringBoot
会被越来越多的项目所采用,进而也推动微服务的兴起。
在SpringBoot
中使用mybatis
,直接依赖mybatis-spring-boot-starter
,它会把mybatis
、mybatis-spring
和mybatis-spring-boot-autoconfigure
三个依赖包都添加进来。前面两个依赖包好理解,这里关键是第三个依赖包,就是通过它实现了mybatis
自动装配功能。下面我们来看下SpringBoot
是如何实现mybatis
的主动装配。
1、首先,定义一个mybatis
主动装配配置类,如下:
@org.springframework.context.annotation.Configuration@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })@ConditionalOnSingleCandidate(DataSource.class)@EnableConfigurationProperties(MybatisProperties.class)@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })public class MybatisAutoConfiguration implements InitializingBean { public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider, ObjectProvider typeHandlersProvider, ObjectProvider languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider databaseIdProvider, ObjectProvider> configurationCustomizersProvider) { ... } @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } applyConfiguration(factory); ...//省略一堆配置 if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) { factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver); } return factory.getObject(); } @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); if (executorType != null) { return new SqlSessionTemplate(sqlSessionFactory, executorType); } else { return new SqlSessionTemplate(sqlSessionFactory); } } public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!AutoConfigurationPackages.has(this.beanFactory)) { logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); return; } logger.debug("Searching for mappers annotated with @Mapper"); List packages = AutoConfigurationPackages.get(this.beanFactory); if (logger.isDebugEnabled()) { packages.forEach(pkg -> logger.debug("Using auto-configuration base package "{}"", pkg)); } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", true); builder.addPropertyValue("annotationClass", Mapper.class); builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); Stream.of(beanWrapper.getPropertyDescriptors()) .filter(x -> x.getName().equals("lazyInitialization")).findAny() .ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}")); registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); } @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } } @org.springframework.context.annotation.Configuration @Import(AutoConfiguredMapperScannerRegistrar.class) @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class }) public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { }}
这里主要利用MapperScannerRegistrarNotFoundConfiguration
类上的@Import(AutoConfiguredMapperScannerRegistrar.class)
引入,然后在AutoConfiguredMapperScannerRegistrar
中BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class)
这句又是动态注入MapperScannerConfigurer
。不过,主动装配配置类中,还会把相关的依赖也一起创建、初始化,比如:SqlSessionFactory
、SqlSessionTemplate
。
@EnableConfigurationProperties(MybatisProperties.class)
把mybatis
相关配置引入进来,这样在创建、初始化过程中的定制需求就可以通过配置修改。
2、有了这个主动装配配置类还不行,下一步就是看如何让主动装配配置类生效。SpringBoot提供了SpringFactoriesLoader
工厂加载机制,类似于JDK
中的SPI
机制,实现将模块META-INF/spring.factories
文件中配置注入到Spring
容器中。mybatis-spring-boot-autoconfigure
模块下META-INF/spring.factories
文件中就有MybatisAutoConfiguration
:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
3、使用时依赖添加进来,配置下属性,就可以直接使用,基本不再需要编码:
mybatis.mapper-locations: classpath:mapper/*Mapper.xmlmybatis.type-aliases-package: com.example.demo.entity
总结
从上面来看,mybatis
和spring
集成的关键的是将mybatis-spring
模块下MapperScannerConfigurer
集成进来,因为,它是一个BeanDefinitionRegistryPostProcessor
类型的扩展,内部通过自定义scanner
扫描Mapper
接口自动注册到IoC
容器中,这一点在各种集成方式中是统一一样的。不同点在于:MapperScannerConfigurer
扩展类是如何被引入的。传统的Spring
方式通过@Mapper
注解或
自定义标签实现,但是对于一些依赖对象还是需要手工创建,比较繁琐;而SpringBoot
利用自动装配,让第三方模块集成变成了一个插件,即插即用,无需太多编码。
分析了mybatis
集成方式,从中也学习了如何利用Spring
的各种扩展点进行定制,更重要的是也为我们开发自己模块和Spring
集成提供了思路。
标签:
-
2022-02-07 14:57:45
奇迹!绝杀!女足亚洲杯逆转夺冠!<
刚刚,中国女足上演逆转绝杀奇迹!她们在亚洲杯决赛中3:2力克韩国队,时隔16年再夺亚洲杯冠军!
-
2022-02-07 14:57:45
中国政府与阿根廷共和国政府签署共建“一带一路”谅解备忘录<
新华社北京2月6日电(记者安蓓)国家发展改革委6日称,国家发展改革委主任何立峰与阿根廷外交、国际贸易和宗教事
-
2022-02-07 14:57:43
中华人民共和国和阿根廷共和国关于深化中阿全面战略伙伴关系的联合声明(全文)<
新华社北京2月6日电中华人民共和国和阿根廷共和国关于深化中阿全面战略伙伴关系的联合声明一、应中方邀请,阿根廷
-
2022-02-07 14:57:40
春节假期国内旅游出游2.51亿人次<
春节遇冬奥,旅游年味浓。根据文化和旅游部数据中心测算,2022年春节假期7天,全国国内旅游出游2 51亿人次,同比
-
2022-02-07 14:57:40
中吉签署关于经典著作互译出版的备忘录 开启两国人文交流互鉴新阶段<
新华社北京2月6日电(记者史竞男)国家主席习近平6日会见来华出席北京2022年冬奥会开幕式的吉尔吉斯斯坦总统扎帕
-
2023-03-29 12:27:12
天天快报!【Spring源码】- 08 扩展点之mybatis集成
mybatis将与spring集成的代码拆分到了mybatis-spring模块,避免mybatis与spring之间的耦合,如果你只需要纯粹的使用mybati
-
2023-03-29 11:51:54
全球信息:大脚聊天频道屏蔽广告_大脚聊天频道屏蔽广告
1、这个屏蔽不了的,如果你是大脚频道管理员的话可以把他T出大脚世界频道,如果不是的话只有一个一个右键自己屏蔽,或者
-
2023-03-29 11:39:10
天天精选!GDC官方回应会场恶劣行为:鼓励受害者向警方报案
此前我们报道了,有开发者表示2023年游戏开发者大会GDC会场发声了许多恶劣行为,包括酗酒、针对女性的性骚扰行为,甚至是在他人的饮品中“加料
-
2023-03-29 10:19:38
当前快讯:无锡出台全省首个专项政策 激发养老服务消费活力
60周岁以上的老人可享8折优惠、市属国有景区门票免费、发放养老消费优惠券……3月28日,我市下发了《关于促进养老服务消费的若干政策措施》...
-
2023-03-29 09:28:17
全球微资讯!广州地铁有裸女?AI脱衣造谣,粉丝提醒美女网红报警维权
一名身材高挑的美女,竟然在大庭广众之下脱下衣服,一丝不挂地裸露着站在地铁车厢里?这样的行为你相信吗?以往网上有句潮语“有图有真相...
-
2023-03-29 08:26:40
【天天时快讯】一位 AI 画家的成长历程丨TECH TUESDAY
都是AI文生图,为何差别这么大。文丨贺乾明编辑丨龚方毅在电脑上输入几个词,等着在屏幕上看到它们被转化成图片,比如山川湖海、鸟兽鱼虫、楼
-
2023-03-29 06:43:03
世界热议:千眼菩提子_关于千眼菩提子的介绍
1、千眼菩提,酒椰果实的硬化胚乳,因表面有很多天然斑点,仿佛有众多的眼睛而得名。2、千眼菩提坚硬无比,为实心状,密度硬度
-
2023-03-29 03:10:58
速读:失业金如何领取北京_失业金如何领取
1、我先说个前提哈,首先是你现在与单位解除或终止合同了,并且不是你主动要求离职的,其次单位给你交养老保险满一年了。2、办理方法:第一步
-
2023-03-28 23:05:45
即时看!《铃芽之旅》似乎是这个新时代的千与千寻
今日刚刚看完。仅代表个人说下观点。我看完的第一感觉是这部作品堪称“新时代的千与千寻”。哇,刚查了下《千与千寻》是2001年的,那真的在...
-
2023-03-28 21:54:52
每日速看!纯电续航500km,体验女神专属座驾——欧拉芭蕾猫
如今国内女性消费者的购买能力不容小觑,在汽车市场同样如此。不少车企为了博得女性消费者的关注,还推出了不少契合女性朋友审美的专属座驾。
-
2023-03-28 20:06:34
【独家焦点】新华社在洪都拉斯设立分社,为全球第182个驻外分社
经洪都拉斯政府批准,新华通讯社特古西加尔巴分社27日成立。26日,中国与洪都拉斯政府签署建交公报,两国建立外交关系。洪都拉斯战略计划部部
-
2023-03-28 19:08:43
热头条丨ego是什么牌子(ego是什么牌子鞋)
ego是什么牌子?ego是什么牌子?Ego澳洲家庭保养第一品牌QV澳洲EGO大药厂出品,该药厂成立于1953年。意高的信念建立在职业道德上,专注于皮肤
-
2023-03-28 18:21:50
焦点快播:GBA集团(00261)发盈警 预期年度股东应占亏损同比扩大约两倍
GBA集团(00261)发布公告,截至2022年12月31日止年度,集团预期将取得
-
2023-03-28 17:31:08
世界微速讯:沈阳“雅馨苑北”地块重磅亮相,首府新区这块地到底“值钱吗”?
沈阳“雅馨苑北”地块重磅亮相,首府新区这块地到底“值钱吗”?
-
2023-03-28 16:35:25
天天微资讯!湖北省2023年普通高校招生专项计划报考资格申报时间提醒
湖北省2023年普通高校招生专项计划报考资格继续实行网上申报,考生个人申报时间为3月29日至4月10日,逾期不再受理。 请符合条件的考生在规
-
2023-03-28 15:54:22
世界新资讯:obs怎么推流直播_obs推流是什么意思
1、开始推流是给直播用的。2、直播平台开启直播之后开始推流就将主播的画面推送到直播网站,此时观众就可以看你的直播了开始录
-
2023-03-28 14:14:05
快播:乐蔓驱虫药_乐蔓
1、白熊啤酒酒精度数为7%,白熊啤酒是以麦芽、啤酒花、香料、香橙皮为原料制作而成的比利时小麦啤酒,在整个比利时啤酒的大类
-
2023-03-28 12:48:32
天天百事通!闽西职业技术学院学报_闽西职业技术学院
1、同学你好,没有任何一所院校在所有招生计划录取之前是会公布分数线的,因为如果一旦先划线。2、那么可能我只招2000名同
-
2023-03-28 11:25:18
天天热点!刚刚发布,2023年青岛市中小学招生政策来了!
28日上午,市政府新闻办举行发布会,发布了2023年全市中小学及幼儿园招生政策。发布会有哪些重点内容,观海君带你来看↓↓↓幼儿园招生2023年
-
2023-03-28 10:32:16
视点!小钱期市:黄金原油3月28日操作建议
【现货黄金3月28日操作建议】技术面黄金昨日延续回调并大幅下探日线大阴报收并下破10日线MACD0轴上方金叉红柱收缩整体呈继续回调态势60分钟图
-
2023-03-28 09:17:48
全球热议:下月起,开发商一旦存在这5种错误行为,就等着“吃牢饭”吧!
近年来,关于开发商的负面新闻可是越来越多了,就连碧桂园、万科等知名房企也频频曝出丑闻。然而,不管开发商怎么可恶,购房者在维权时总是困
-
2023-03-28 08:10:58
热推荐:北京今天晴转多云最高温21℃ 花粉浓度极高需注意防护
预计明后天,北京阳光将持续“在岗”,气温一路“狂飙”,后天最高温可达25℃,需及时增减衣物,谨防感冒。
-
2023-03-28 05:54:24
【环球速看料】维维股份疯狂跨界后遗症净利降57% 两主业营收下降“双百千亿”目标遥不可及
“维维豆奶,欢乐开怀”,这句经典广告语曾让维维股份红极一时,但维维股份(600300 SH)的日子过得远未“欢乐开怀”。 3月24日晚间,维...
-
2023-03-28 01:13:48
天天通讯!为什么朋友圈有的视频打不开怎么回事
原因:1 网速过慢导致,暂时不要打开微信朋友圈,等网速正常时再打开。2 微信信息等内容太多,可以清除下无
-
2023-03-27 22:05:52
【天天时快讯】恢复现场祭扫、取消预约入园……多地清明祭扫不对疫情防控做要求
清明临近,多地已发布清明祭扫通知。人民日报健康客户端不完全统计,多地的清明祭扫通知中已不对疫情防控做出要求。2022年清
-
2023-03-27 20:17:48
焦点日报:当虹科技: 杭州当虹科技股份有限公司2023年第一次临时股东大会决议公告
当虹科技:杭州当虹科技股份有限公司2023年第一次临时股东大会决议公告
-
2023-03-27 18:57:02
当前热讯:网传海锅股份参与了海油观澜号的改造?公司回应
27日,有传闻称,海锅股份参与了海油观澜号的改造,对此,海锅股份证券部工作人员回应:“没有听说。公司是生产锻件的。把锻件提供给下游客...
-
2023-03-27 17:50:48
每日讯息!钢结构防火涂料厚度与耐火极限是多少-钢结构防火涂料厚度与耐火极限
1、按照GB9978建筑升温曲线的标准的话有如下关系1 5h2 0h2 5h3 0h11mm15mm19mm23mm按照BSEN1363-2:199
-
2023-03-27 16:27:04
天天观天下!原奶收购价走低 酸奶零售价缘何坚挺
原奶(即生鲜乳)收购价格持续走低。农业农村部近日发布的2023年3月第3周价格数据显示,内蒙古、河北等10个主产省份生鲜乳平均价格3 99元 公斤
-
2023-03-27 15:16:53
观速讯丨汉阴公安平梁派出所:多举措优化营商环境,全力护航企业发展
汉阴公安平梁派出所:多举措优化营商环境,全力护航企业发展