环球今热点:【Spring源码】- 05 扩展点之BeanPostProcessor
类结构
BeanPostProcessor和BeanFactoryPostProcessor是Spring中两个最重要的扩展的。如果说BeanFactoryPostProcessor是面向IoC进行扩展,BeanPostProcessor就是面向Bean进行扩展。
从上面类结构图可以看出,BeanPostProcessor是一个顶层接口,下面有衍生出几个接口,实现对Bean创建、初始化等各个阶段进行更细化的扩展,所以BeanPostProcessor要比BeanFactoryPostProcessor复杂一些,可以实现更多扩展场景。
(资料图片仅供参考)
注册顺序
BeanPostProcessor被注册到IoC中才能起作用,在refresh()方法中registerBeanPostProcessors(beanFactory);这一语句完成BeanPostProcessor的注册工作,注册使用:addBeanPostProcessor(BeanPostProcessor beanPostProcessor)方法完成。
注册BeanPostProcessor也涉及到先后顺序关系,大致逻辑总结如下:
1、获取实现PriorityOrdered接口的BeanPostProcessor,然后通过getBean()方法实例化,排序后注册到容器中;2、获取实现Ordered接口的BeanPostProcessor,然后通过getBean()方法实例化,排序后注册到容器中;3、获取常规没有实现PriorityOrdered和Ordered接口BeanPostProcessor,然后通过getBean()方法实例化,注册到容器中;4、上述步骤中MergedBeanDefinitionPostProcessor类型会单独存储到internalPostProcessors集合中,排序后保证放到末尾5、最后移除ApplicationListenerDetector重新追加到最末尾上面只是BeanPostProcessor注册先后顺序关系,并不会涉及到BeanPostProcessor的执行,由于BeanPostProcessor扩展出几个子类,下面我们来分析下每个子类的执行时机。
BeanPostProcessor
执行时机
接口定义见下:
public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }}之前分析IoC容器启动流程时,介绍过initializeBean()方法完成Bean的init-method初始化工作,BeanPostProcessor就是在init-method执行前后进行扩展。
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //触发BeanPostProcessor#postProcessBeforeInitialization()方法执行 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } //执行init-method方法 invokeInitMethods(beanName, wrappedBean, mbd); if (mbd == null || !mbd.isSynthetic()) { //触发BeanPostProcessor#postProcessAfterInitialization()方法执行 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean;}再来看下这两个方法的调用逻辑:
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result;}如果有postProcessBeforeInitialization()方法返回null,则表示该扩展点提前结束,不再需要继续执行后续BeanPostProcessor的postProcessBeforeInitialization方法。
再来看下postProcessAfterInitialization()方法执行逻辑是一样的:
@Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }使用场景
invokeInitMethods(beanName, wrappedBean, mbd);方法执行Bean的init-method方法进行初始化,进入这个方法可以发现,这里只会执行实现InitializingBean和@Bean(initMethod="xxx")这两种方式设置的init-method方法,我们平时使用很多的@PostConstruct注解方式,其实是通过InitDestroyAnnotationBeanPostProcessor这个扩展类实现:
InitDestroyAnnotationBeanPostProcessor类实现了DestructionAwareBeanPostProcessor和MergedBeanDefinitionPostProcessor这两个接口,间接方式继承BeanPostProcessor。InitDestroyAnnotationBeanPostProcessor就是在postProcessBeforeInitialization()方法中完成了对@PostConstruct注解方法的调用,所以其执行优先级比InitializingBean和@Bean(initMethod="xxx")这两种方式更加靠前。
如果你需要在init-method等Bean的初始化执行前后进行扩展,可以使用此接口实现。比如:判断Bean是否是线程池类,如果是则统一设置管理的线程名前缀:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ThreadPoolTaskExecutor) { ((ThreadPoolTaskExecutor) bean).setThreadNamePrefix("Post-"); } return bean;}还比如ApplicationListenerDetector在postProcessAfterInitialization()方法中实现将ApplicationListener类型的单例Bean注册到事件多播器上,实现对事件的监听:
public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof ApplicationListener) { Boolean flag = this.singletonNames.get(beanName); if (Boolean.TRUE.equals(flag)) { // 如果当前 ApplicationListener bean scope 是 singleton 单例模式,则将它注册到应用的事件多播器上 this.applicationContext.addApplicationListener((ApplicationListener>) bean); } else if (Boolean.FALSE.equals(flag)) { // 如果ApplicationListener bean scope 不是 singleton 单例模式,则尝试输出警告日志,说明情况,并移除 //所以ApplicationListener类型的只能是单例模式才会起作用 this.singletonNames.remove(beanName); } } return bean;}还比如ApplicationContextAwareProcessor这个就是在postProcessBeforeInitialization()方法中实现如ApplicationContextAware、EnvironmentAware等*Aware接口注入功能。实现原理非常简单,就是判断Bean是否实现接口,然后通过setter方式注入即可:
private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }}InstantiationAwareBeanPostProcessor
前面分析BeanPostProcessor接口是在Bean的init-method方法执行前后进行扩展,其子接口InstantiationAwareBeanPostProcessor则可以在Bean的创建前后进行扩展,所以此扩展比BeanPostProcessor扩展更靠前。
接口定义见下:
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { //Bean创建之前回调该方法,beanClass就是将要被创建的Bean对应的Class信息 @Nullable default Object postProcessBeforeInstantiation(Class> beanClass, String beanName) throws BeansException { return null; } //Bean创建之后回调该方法,参数bean就是创建完成的Bean对象 default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { return true; } //postProcessProperties()方法在postProcessAfterInstantiation()方法之后紧挨着执行,其提供PropertyValues类型入参,所以在该方法中可以实现依赖操作 @Nullable default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { return null; } //这个方法标注@Deprecated已经被废弃了,被postProcessProperties()方法取代了 @Deprecated @Nullable default PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { return pvs; }}createBean()方法中Object bean = resolveBeforeInstantiation(beanName, mbdToUse);这条语句中会触发对postProcessBeforeInstantiation()方法的执行。
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { Object bean = resolveBeforeInstantiation(beanName, mbdToUse);//触发对postProcessBeforeInstantiation()方法的执行 if (bean != null) { return bean; } ... Object beanInstance = doCreateBean(beanName, mbdToUse, args);//创建Bean实例(一般真正创建Bean的方法) ...}InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()方法有个重要特性:如果该方法返回非null结果,则表示Bean提前创建完成,同时也会忽略掉后续的依赖注入、init-method初始化等步骤执行,最后只需要执行下BeanPostProcessor#postProcessAfterInitialization这个方法则整个Bean的创建流程就全部完成。
总结:在创建对象之前调用了postProcessBeforeInstantiation方法可以实现给扩展点一次创建代理的机会,如果代理对象返回不为空则不再继续常规方式创建Bean。
我们再来看下InstantiationAwareBeanPostProcessor接口中定义的另两个方法执行时机,Bean创建完成后会执行populateBean()进行依赖注入,它们就是在这个方法中进行触发回调,pupulateBean()方法大致见下:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { //执行InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation方法回调 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } } // 注解注入:后置处理器ibp#postProcessProperties,大名鼎鼎的@Autowired就是在这处理的。 PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { //执行InstantiationAwareBeanPostProcessor#postProcessProperties方法回调 for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { //获取出对象的所有set get方法,现在是有一个 getClass()方法,因为继承了Object, 没什么其他卵用 filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } //postProcessPropertyValues方法已废弃,被postProcessProperties替代 pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } } if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); }}上面代码翻译下大概就是:先执行InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation方法回调,然后再去执行InstantiationAwareBeanPostProcessor#postProcessProperties,最后再去执行applyPropertyValues()完成PropertyValue方式的依赖注入。这里有个大名鼎鼎的@Autowired、@Value方式的依赖注入,就是借助于InstantiationAwareBeanPostProcessor#postProcessProperties()方法实现,这个实现类就是:AutowiredAnnotationBeanPostProcessor,简单看下依赖注入逻辑:
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { /** * 从缓存中找到此类的@Autowired、@Value注解元数据,尝试注入 * InjectionMetadata,持有待注入的元数据信息,执行inject()方法,开始注入属性或方法参数。 */ InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { //为beanName填充上属性bean metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs;}这里有意义的代码就两行:
1、InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);获取Bean中需要依赖注入注入的元素,封装成一个InjectionMetadata对象,该对象有两个重要属性:
targetClass指定目标对象的Class;Collection injectedElements :目标对象中每个需要依赖注入的元素都会封装成一个InjectedElement,然后存储到该集合中。根据@Autowired/@Value注解到字段上还是方法上,InjectedElement又可以分为两类:AutowiredFieldElement和AutowiredMethodElement。2、metadata.inject(bean, beanName, pvs);:这个方法内部就是循环,对每个依赖元素InjectedElement调用inject()方法
if (!elementsToIterate.isEmpty()) { for (InjectedElement element : elementsToIterate) { if (logger.isTraceEnabled()) { logger.trace("Processing injected element of bean "" + beanName + "": " + element); } element.inject(target, beanName, pvs); }}比如我们一般将@Autowired标注到字段上,则这里会触发AutowiredFieldElement#inject()方法执行:
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member;//依赖注入字段 Object value;//存储需要注入的值 if (this.cached) {//如果已被缓存,则直接先从缓存中获取依赖注入值 value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else {//还未被缓存过 //1.DependencyDescriptor:用于对该依赖注入描述信息 DependencyDescriptor desc = new DependencyDescriptor(field, this.required); desc.setContainingClass(bean.getClass()); Set autowiredBeanNames = new LinkedHashSet<>(1); Assert.state(beanFactory != null, "No BeanFactory available"); TypeConverter typeConverter = beanFactory.getTypeConverter(); try { /* 2.查找依赖注入的值 比如: @Autowired private TestService03 testService03; 这个value就是从IoC容器中查找到的TestService03对象 还比如:@Value("${spring.name}"),这个value就是从Spring上下文环境变量中解析出的spring.name变量值 */ value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } //3.下面synchronized块主要实现缓存功能,已被解析过的包装成ShortcutDependencyDescriptor类型,上面resolvedCachedArgument对这种类型会特殊处理 synchronized (this) { if (!this.cached) { if (value != null || this.required) { this.cachedFieldValue = desc; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this.cachedFieldValue = new ShortcutDependencyDescriptor( desc, autowiredBeanName, field.getType()); } } } else { this.cachedFieldValue = null; } this.cached = true; } } } //4.查找到的依赖值不为null,则使用反射方式注入,因为是通过反射方式,所以@Autowired、@Value是不需要setter/getter方法也可以注入 if (value != null) { //通过反射方式将查找到的需要依赖注入的值设置到对象实例中 ReflectionUtils.makeAccessible(field); field.set(bean, value); }} SmartInstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor还有个子接口:SmartInstantiationAwareBeanPostProcessor,其定义如下:
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor { //推断类型 @Nullable default Class> predictBeanType(Class> beanClass, String beanName) throws BeansException { return null; } //根据一定规则推断出Bean中优选的构造方法 @Nullable default Constructor>[] determineCandidateConstructors(Class> beanClass, String beanName) throws BeansException { return null; } default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean; }}SmartInstantiationAwareBeanPostProcessor接口有三个方法,在实例创建前智能判断实例类型、智能判断构造函数、提起获取暴露Bean引用,该接口主要是spring框架内部使用,开发时很少去扩展该接口。
这里主要注意第三个方法:getEarlyBeanReference(),这个扩展方法主要与Spring中的循环依赖有关系。前面分析IoC容器启动时分析过:为了解决Spring中的循环依赖问题,在doCreateBean()方法内部,会将刚创建还未来得及进行依赖注入和初始化的半成品Bean提前暴露出去,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));,注意这里不是直接将Bean暴露出去,而是通过() -> getEarlyBeanReference(beanName, mbd, bean)这句将Bean包装成ObjectFactory类型再暴露出去。
这里的一个核心就是:为什么不直接暴露Bean,而是将Bean包装成ObjectFactory再去暴露?将Bean包装成ObjectFactory再去暴露,调用getObject()方法时会触发SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference方法回调。
分析到这里,还不够完善,因为你可能会问:那这个方法回调又可以给我们解决什么问题呢?
可以利用Spring AOP原理来回答这个问题,Spring AOP主要基于AnnotationAwareAspectJAutoProxyCreator这个类实现,这个类实现了BeanPostProcessor接口,在postProcessAfterInitialization()方法中对创建完成的Bean采用动态代理方式将增强逻辑织入进去。
如果存在这样情况:A依赖B,B同时依赖A,这就是所说的Spring循环依赖,但是如果我们对A采用了AOP增强,这个过程会是怎样情况呢?
init-method初始化方法后,postProcessAfterInitialization()执行时会给A通过动态代理方式织入增强逻辑;这时,步骤3中给B注入的是A的原生对象,但是步骤6会给A创建一个代理对象,但是B中这时还是原生对象没法改变,这就会导致有的依赖注入的是原生对象,有的依赖注入的是代理对象,会出现错乱问题。如何解决呢?这个就是SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference这个扩展点作用。
A对象提前暴露时,利用ObjectFactory包装了一层,B对象在进行依赖注入时获取到对象A时,不是直接返回A,而是触发getEarlyBeanReference()方法执行,AnnotationAwareAspectJAutoProxyCreator类在getEarlyBeanReference()方法中实现判断A需要做动态代理,则对A进行动态代理后返回,这时B中依赖注入的就不是原生对象。
总结:SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference()方法是在循环依赖场景下,对提前暴露的Bean可以通过该扩展点进行处理。只有因为存在循环依赖,才会导致需要需要获取那些提前暴露的Bean时才会触发该扩展点,所以,理解这个扩展点关键在于你对Spring循环依赖的理解。
DestructionAwareBeanPostProcessor
DestructionAwareBeanPostProcessor是BeanPostProcessor子接口,其定义如下:
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor { //Bean销毁前回调方法 void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException; //可以根据Bean进行过滤,哪些Bean需要用到当前这个回调 default boolean requiresDestruction(Object bean) { return true; }}从名称就可以看出,该扩展主要用于Bean销毁之前,回调时机在:DisposableBeanAdapter#destroy()
public void destroy() { //调用DestructionAwareBeanPostProcessor#postProcessBeforeDestruction,Bean销毁之前回调接口 if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { processor.postProcessBeforeDestruction(this.bean, this.beanName); } } ... ((DisposableBean) this.bean).destroy();//调用DisposableBean.destroy() ... }DestructionAwareBeanPostProcessor接口有个实现类InitDestroyAnnotationBeanPostProcessor,实现对@PreDestroy注解支持。该扩展接口本身是比较简单的,后续分析Bean生命周期destroy流程整体梳理。
MergedBeanDefinitionPostProcessor
MergedBeanDefinitionPostProcessor
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor { void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName); //Spring5.1新增接口,实现BeanDefinition重置通知,一般该方法实现重置前清理metadata等元数据缓存 default void resetBeanDefinition(String beanName) { }}我们主要看下postProcessMergedBeanDefinition()方法调用时机:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { //1.创建对象 //2.执行MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition回调方法 synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } } //3.提前暴露Bean //4.populateBean(beanName, mbd, instanceWrapper); //5.exposedObject = initializeBean(beanName, exposedObject, mbd);}MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition发生在Bean刚创建完成,Bean还未提前暴露之前。MergedBeanDefinitionPostProcessor在Spring中有很多的应用,比如:AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、InitDestroyAnnotationBeanPostProcessor等。这个扩展的一般套路是和其它扩展点一起使用,其起到一个帮手角色,postProcessMergedBeanDefinition将需要处理的注解信息解析成元数据信息缓存起来,其它扩展点就可以从缓存中获取需要处理的注解信息进行处理。有关这扩展点更多的情况会在后续案例分析中再详细分析。
总结
BeanFactoryPostProcessor和BeanPostProcessor是Spring提供的两个最核心、最基础的扩展方式:一个面向IoC进行扩展,另一个面向Bean的创建流程进行各种扩展。BeanPostProcessor及其子类实现了对Bean创建过程中的各种扩展:Bean创建前后、Bean初始化前后、获取提前暴露对象前等等这些。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 02:06:20
环球今热点:【Spring源码】- 05 扩展点之BeanPostProcessor
BeanPostProcessor和BeanFactoryPostProcessor是Spring中两个最重要的扩展的。如果说BeanFactoryPostPr
-
2023-03-28 23:12:23
【天天新视野】Keep 再更新香港 IPO 招股书:2022 年营收 22 亿元,调整后净亏损 6.7 亿元
收入方面,由2020年的11 1亿元增长至2021年的16 2亿元,同比增46 3%,并进一步增长至2022年的22 1亿元,同比增36 6%
-
2023-03-28 21:45:10
世界播报:我和我的村丨认了一村的亲戚
讲述人:宁夏彭阳县委宣传部派驻彭阳县新集乡白林村第一书记杨卫民三月,白林的风已不再凛冽,走在柏油路上暖风拂面,想着过段
-
2023-03-28 19:57:25
世界观焦点:四十余载转型升级 浙江安吉椅业组团“征战”广州家博会
28日,浙江省湖州市安吉县140余家椅业企业组团来到中国(广州)国际家具博览会(以下简称“广州家博会”)参展,参展总面积超1 6万平方米。广州家
-
2023-03-28 19:22:44
头条:广西打造全国知名“果盘子”工作推进会暨果业高质量发展研修班在桂林举办
3月25-26日,广西打造全国知名“果盘子”工作推进会暨果业高质量发展研修班在桂林市隆重召开。自治区农业农村厅党组书记、厅长黄智宇做工作...
-
2023-03-28 18:35:44
全球观热点:迪士尼启动7000人大裁员:包括主题公园和ESPN 本周启动首轮
中国网是国务院新闻办公室领导,中国外文出版发行事业局管理的国家重点新闻网站。本网通过10个语种11个文版,24小时对外发布信息,是中国进行
-
2023-03-28 17:29:49
环球信息:奎克利40+9,尼克斯137-115力克火箭
北京时间3月28日,NBA常规赛继续进行,尼克斯主场对阵火箭。这场比赛火箭上半场打得还是可以的。但来到下半之后,火箭完全成为漏洞。再加上末
-
2023-03-28 16:33:29
天天精选!“三轮车宰客”引发游客吐槽,山东一市发布重要通知
李蕊3月27日,济南市文化和旅游局网站发布趵突泉、大明湖周边三轮车宰客问题的《重要通知》,要求全市广大旅行社、导游人员不得从事欺诈宰客等
-
2023-03-28 15:33:15
全球快报:网信办:组织水军的公司、MCN机构一经发现将列入黑名单
央视网消息:国新办3月28日就2023年“清朗”系列专项行动有关情况举行发布会,国家互联网信息办公室网络综合治理局局长张拥军在会上介绍,...
-
2023-03-28 14:06:10
今日热门!南宁一栋“待拆危房”下全是商铺,不少人进出?街道办回应
南宁一栋“待拆危房”下全是商铺,不少人进出?街道办回应
-
2023-03-28 12:26:35
环球看点!大基金新动作 光刻胶龙头中招 重要股东+北上资金联手卖出这些股
【大基金新动作光刻胶龙头中招重要股东+北上资金联手卖出这些股】除大基金外,3月以来,多家上市公司重要股东发布拟减持公告。数据宝统计,3月
-
2023-03-28 10:59:35
环球快看点丨QuestMobile报告:2022中国互联网广告总量突破6600亿元
QuestMobile报告:2022中国互联网广告总量突破6600亿元
-
2023-03-28 10:20:53
世界新消息丨苹果收购 WaveOne 初创公司:致力于 AI 驱动的视频压缩
苹果收购WaveOne初创公司:致力于AI驱动的视频压缩
-
2023-03-28 09:14:47
【全球报资讯】詹皇回归影响队友效率?名宿:里夫斯八村塁首当其冲
詹皇回归影响队友效率?名宿:里夫斯八村塁首当其冲,勒布朗詹姆斯,湖人,公牛,八村,nba
-
2023-03-28 07:58:19
焦点简讯:今天最新消息 新方法实现跨物种微生物群落可控组装
◎丁宁宁本报记者刘传书自然界微生物群落发挥着重要作用。土壤中的微生物菌群在碳循环中发挥重要作用;肠道微生物菌群在代谢
-
2023-03-28 04:53:53
当前报道:麒麟985和麒麟980区别_麒麟985和麒麟990区别
1、区别:cpu搭配不同。2、麒麟985采用7nm工艺制造,集成八个CPU核心,搭载一颗2 58GHzA76大核、三颗2
-
2023-03-28 00:57:21
每日速读!萨提亚_关于萨提亚的简介
1、萨提亚是最早提出在人际关系及治疗关系中,“人人平等,人皆有价值”的想法的人,她所建立的一套心理治疗方法,最大特点
-
2023-03-27 22:04:45
当前聚焦:什么地方开口说话要付钱打一数字_什么地方开口说话要付钱
1、什么地方开口说话要付钱?电话亭。2、话吧。3、律师事务所都是开口收费的!。本文分享完毕,希望对大家有所帮助。
-
2023-03-27 20:02:17
当前快报:【亲测有效】PyCharm激活码(2023PyCharm最新激活码)
更新日期:2023 03 17①如有失效在此免费获取:点此直接复制下面最新的激活码https: docs qq com doc DVXN0Y3ZkaU9ERGx5②公众号每天都会更新
-
2023-03-27 18:48:21
天天实时:人体摄影技术讲座_五_人体摄影技术
1、1 风景2、通常有两种类型的人体摄影:内部或外部。3、内景拍摄:好处是没有外界干扰,可以专心和幻影交流,尝试不同的姿
-
2023-03-27 17:29:29
当前观察:环球消息!“吃喝板块”回暖,白酒股一马当先!机构:社零超预期,持续看好消费复苏
21日早盘,“吃喝板块”回暖,白酒股涨幅居前,五粮液涨超2%,贵州茅台涨超1%,老白干酒大涨5%。食品ETF(515710)小幅高开后持续走高,一
-
2023-03-27 16:17:05
全球今头条!博鳌前瞻:海南积极拓展自贸港“国际朋友圈”
中新社海口3月27日电博鳌亚洲论坛2023年年会28日将在海南博鳌拉开序幕。着力谋划实施“全球自由贸易港伙伴计划”的海南,将在论坛年会上举...
-
2023-03-27 15:19:51
当前热门:樊文祯被查!
樊文祯被查!监督眼 2023-03-2714:04:20 来源:中国房地产网扫描二维码分享
-
2023-03-27 13:45:25
世界热议:羽绒服如何去除鸭绒味?
可以试试看以下方法去除鸭绒味:1、用白醋将羽绒服浸泡一会儿即可,白醋不仅可以杀菌消毒。2、其中的醋酸能中和偏碱性的洗涤剂产品,能消除色和
-
2023-03-27 12:05:31
世界最新:033期小诸葛大乐透预测奖号:后区两码参考
开奖回顾:第2023032期奖号:0506182830+0207,各位号码遗漏值分别为:011093+1815。前区开出3个热码:050630,开出2个温码:1
-
2023-03-27 11:09:32
全球最新:ecg正常数值是多少_ecg正常值是多少
1、心电图就是心电图,指的是心脏。2、在每个心动周期中,起搏点、心房和心室相继兴奋,并伴有生物电的变化。3、通过ECG扫
-
2023-03-27 09:49:59
热资讯!中邮证券给予桃李面包买入评级 2022年需求盈利承压 2023年预计逐季复苏
中邮证券03月27日发布研报称,给予桃李面包(603866 SH,最新价:15 25元)买入评级。评级理由主要包括:1)Q4疫情影响下需求承压营收下滑,盈利
-
2023-03-27 08:45:35
焦点!浮法玻璃持续去库&涨价,推荐玻璃底部机会
【建材】浮法玻璃持续去库&涨价,推荐玻璃底部机会——行业周报-202303261、浮法玻璃持续去库&涨价,推荐玻璃底
-
2023-03-27 06:16:24
全球快播:Java中restTemplate携带Header请求
restTemplate发送POST请求时可以通过如下方法获取ResponseEntity:
-
2023-03-27 01:13:22
天天微资讯!与埃德加·艾伦·坡的联系(第1话)
美国作家埃德加·艾伦·坡(EdgarAllanPoe),被称为侦探小说鼻祖、科幻小说先驱和恐怖小说大师,主张“为艺术而艺术”及“情节服务于效果”...