世界速看:【Spring源码】- 05 扩展点之BeanPostProcessor

2023-03-28 12:09:22 来源: 腾讯云

类结构

BeanPostProcessorBeanFactoryPostProcessorSpring中两个最重要的扩展的。如果说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()方法完成Beaninit-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,则表示该扩展点提前结束,不再需要继续执行后续BeanPostProcessorpostProcessBeforeInitialization方法。

再来看下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);方法执行Beaninit-method方法进行初始化,进入这个方法可以发现,这里只会执行实现InitializingBean@Bean(initMethod="xxx")这两种方式设置的init-method方法,我们平时使用很多的@PostConstruct注解方式,其实是通过InitDestroyAnnotationBeanPostProcessor这个扩展类实现:

InitDestroyAnnotationBeanPostProcessor类实现了DestructionAwareBeanPostProcessorMergedBeanDefinitionPostProcessor这两个接口,间接方式继承BeanPostProcessorInitDestroyAnnotationBeanPostProcessor就是在postProcessBeforeInitialization()方法中完成了对@PostConstruct注解方法的调用,所以其执行优先级比InitializingBean@Bean(initMethod="xxx")这两种方式更加靠前。

如果你需要在init-methodBean的初始化执行前后进行扩展,可以使用此接口实现。比如:判断Bean是否是线程池类,如果是则统一设置管理的线程名前缀:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof ThreadPoolTaskExecutor) {  ((ThreadPoolTaskExecutor) bean).setThreadNamePrefix("Post-"); } return bean;}

还比如ApplicationListenerDetectorpostProcessAfterInitialization()方法中实现将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()方法中实现如ApplicationContextAwareEnvironmentAware*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指定目标对象的ClassCollection injectedElements:目标对象中每个需要依赖注入的元素都会封装成一个InjectedElement,然后存储到该集合中。根据@Autowired/@Value注解到字段上还是方法上,InjectedElement又可以分为两类:AutowiredFieldElementAutowiredMethodElement

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依赖BB同时依赖A,这就是所说的Spring循环依赖,但是如果我们对A采用了AOP增强,这个过程会是怎样情况呢?

A对象创建完成后,由于可能会存在循环依赖问题,所以Spring会提前将A暴露出去;然后对A进行依赖注入,发现A依赖B,然后A就卡主了,通过getBean(B)获取B实例时,这时就会进入B对象创建流程;同样B对象创建完成后并将B对象提前暴露出去,然后开始执行B对象的依赖注入,通过getBean(A)方式获取,因为A已经提前暴露出去了,所以获取A是没问题的;然后B顺利执行完依赖注入、init-method初始化工作,则B整个创建流程全部完成;这时A中通过getBean(B)方法就可以返回B对象,然后依赖注入到A中,然后执行init-method初始化;由于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

DestructionAwareBeanPostProcessorBeanPostProcessor子接口,其定义如下:

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还未提前暴露之前。MergedBeanDefinitionPostProcessorSpring中有很多的应用,比如:AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorInitDestroyAnnotationBeanPostProcessor等。这个扩展的一般套路是和其它扩展点一起使用,其起到一个帮手角色,postProcessMergedBeanDefinition将需要处理的注解信息解析成元数据信息缓存起来,其它扩展点就可以从缓存中获取需要处理的注解信息进行处理。有关这扩展点更多的情况会在后续案例分析中再详细分析。

总结

BeanFactoryPostProcessorBeanPostProcessorSpring提供的两个最核心、最基础的扩展方式:一个面向IoC进行扩展,另一个面向Bean的创建流程进行各种扩展。BeanPostProcessor及其子类实现了对Bean创建过程中的各种扩展:Bean创建前后、Bean初始化前后、获取提前暴露对象前等等这些。Spring中大量注解简化了我们使用框架的复杂性,而这些注解很大一部分就是基于这些扩展进行处理,学习这些扩展点可以帮助我们更好的熟悉Spring的运行机理,同时可以在开发中帮助我们灵活的实现各种功能扩展。

标签:

上一篇 :

下一篇 :

世界速看:【Spring源码】- 05 扩展点之BeanPostProcessor

BeanPostProcessor和BeanFactoryPostProcessor是Spring中两个最重要的扩展的。如果说BeanFactoryPostPr

03-28 12:09:22

欠钱怎么构成诈骗案

欠钱构成诈骗案一般是看行为人在主观上是否存在以非法占有的目的虚构事实、隐瞒真相骗取数额较大的公私...

03-28 11:20:15

【天天新要闻】杭州市佛教协会副会长、灵顺寺住持印旭法师圆寂,享年50岁

澎湃新闻记者从印旭法师治丧委员会方面获悉,浙江省佛教协会理事、杭州市佛教协会副会长、杭州北高峰灵...

03-28 10:02:29

世界观热点:羊肚菌撑开“致富伞”

华声在线3月27日讯(通讯员葛倩全媒体记者彭雅惠奉永成)春分过后,宁远县九嶷山瑶族乡西湾村羊肚菌种植基...

03-28 09:12:47

全球快播:浙商中拓03月27日获深股通增持28.94万股

03月27日,浙商中拓获深股通增持28 94万股,最新持股量为80 89万股,占公司A股总股本的0 12%。

03-28 07:41:23

【速看料】无人机板块3月27日跌0.66%,思特威领跌,主力资金净流出9.04亿元

3月27日无人机板块较上一交易日下跌0 66%,思特威领跌。当日上证指数报收于3251 4,下跌0 44%。深证...

03-28 04:43:09

“新运河”通航倒计时,杭州离“超级枢纽”更近了_百事通

“新运河”通航倒计时,杭州离“超级枢纽”更近了

03-28 00:15:49

两部门:继续实施物流企业大宗商品仓储设施用地税收优惠政策 环球微头条

中新经纬3月27日电财政部网站消息,近日,财政部、国家税务总局联合发布《关于继续实施物流企业大宗商品...

03-27 21:43:07

当前聚焦:便民春风优服务 出口退税暖人心

中国青年网齐齐哈尔3月27日电(记者张心觉)为进一步推进“便民办税春风行动”,不断提升纳税人缴费人满...

03-27 20:06:09

大象脚印图片简笔画(大象脚印)

1、大象的脚印就像一个个木桩砸出的圆坑。2、它们主要外部特征为柔韧而肌肉发达的长鼻和扇大的耳朵,具...

03-27 18:42:16

大宗交易:祥源文旅成交760万元,折价3.06%(03-27) 报道

2023年3月27日,祥源文旅发生1笔大宗交易,总成交100万股,成交金额760万元,成交价7 60元,折价3 06%。

03-27 17:05:24

传吉利旗下远程汽车赴美IPO前将再融资 环球通讯

传吉利旗下远程汽车赴美IPO前将再融资

03-27 16:08:50

金煌作品 时光未来,现代主义的极致演绎_环球微速讯

清新自然的城市精神现代主义的极致演绎01客厅Livingroom这是一套300平米的现代风格别墅装修案例,整体空...

03-27 15:05:48

wPS如何打斜箭头 环球快讯

1、打开WPS软件,新建空白文档,找到插入,单击一下,再找到“形状”,单击一下。2、单击“形状”后,就...

03-27 13:16:08

河狸家

1、河狸家创立于2014年。2、为用户提供美甲、美睫、美容、理疗、健身、美体、美发、化妆造型等“泛美业...

03-27 12:03:14

天天快看:金雷股份3月27日盘中涨幅达5%

以下是金雷股份在北京时间3月27日10:54分盘口异动快照:3月27日,金雷股份盘中涨幅达5%,截至10点54分,...

03-27 10:54:31

兴业银行南宁分行普惠金融出实招 线上“兴优贷”助企纾困 焦点要闻

“兴业银行推出的线上‘兴优贷’产品真好,给我名下企业放款1000万元,及时补充企业现金流。感谢兴业银...

03-27 09:41:14

现代汽车计划年内在美成立空中出行子公司

据财联社报道,现代汽车集团最早将于上半年在美国华盛顿成立专门负责城市空中出行(UAM)项目的子公司。...

03-27 08:17:29

今日量比是什么意思换手率多少才合适_量比是什么意思_当前关注

1、(1)量比是衡量相对成交量的指标。2、它是指股市开市后平均每分钟的成交量与过去5个交易日平均每分钟...

03-27 05:52:38

贵州省黔东南苗族侗族自治州黄平县2023-03-26 21:13发布雷电黄色预警 当前关注

一、贵州省黔东南苗族侗族自治州黄平县天气预报1、黄平县气象台2023年3月26日21时13分发布雷电黄色预警...

03-27 01:01:25

气温“狂飙”!洛阳最高气温将升至25℃

这个双休日,您和家人出门赏花了吗?好消息是,阳光将持续在线,一路向暖,洛阳下周最高气温将达到25℃...

03-26 21:30:29

秦巴盆地美汉中|天天观天下

【大美中国】每个人心中都有属于自己的桃花源。北依秦岭,南屏巴山,三千里汉江源头的汉中,奇山秀水与...

03-26 19:05:49

从百草园到三味书屋好句批注_从百草园到三味书屋好句 今日快讯

1、1 不必说碧绿的菜畦,光滑的石井栏,高大的皂荚树,紫红的桑葚;也不必说鸣蝉在树叶里长吟,肥胖的...

03-26 17:12:12

耗子尾汁是什么梗_网络语耗子尾汁是什么意思_新资讯

欢迎观看本篇文章,小勉来为大家解答以上问题。耗子尾汁是什么梗,网络语耗子尾汁是什么意思很多人还不...

03-26 14:59:16

如何注销QQ秀

大家好,下面为大家介绍如何注销QQ秀功能。通过检索工具搜索注销QQ秀,选择官方网页点击进入。在官方页...

03-26 13:25:25

整改“一键打新”!多家券商已开始行动!

早在约一年前,当时新股频频破发,券商中国微信公众号刊发了多篇报道,建议对“一键打新”以及新股中签...

03-26 12:05:48

美国密西西比州受龙卷风影响地区进入紧急状态 全球速读

据美国有线电视新闻网当地时间3月25日报道,美国密西西比州州长泰特·里夫斯宣布该州所有受龙卷风影响的...

03-26 10:03:15

怂怂txt下载 世界最资讯

1、链接:提取码:afhz小说名称:怂怂[快穿]作者:扶苏与柳叶类型:穿越连载状态:已完结字数:769962字简介:

03-26 07:10:25

乌克兰与中国时差几个小时 乌克兰和中国时差多少个小时 全球快看

导语:大家都知道,有些国家与国家之间是有时差的,你在这个国家的时候是深夜,去到另个国家就是中午了...

03-26 06:12:20

今日无言以对歌词王以太(无言以对 歌词)

无言以对歌词王以太,无言以对歌词很多人还不知道,现在让我们一起来看看吧!1、窗外天亮了房里还是有点...

03-26 02:01:48

门头沟这处棚改定向安置房即将精装修!另一地块完工待交竣!

门头沟这处棚改定向安置房即将精装修!另一地块完工待交竣!,棚改,精装修,门头沟,住宅用地,定向安置房

03-25 22:02:59

宝藏Lumin潮玩由你,打造年轻人时尚出街风向标 快资讯

近日,长安Lumin发起“宝藏Lumin潮玩由你”潮改用户作品征集活动,面向全网征集Lumin个性潮改设计方案、...

03-25 19:15:57

广西黑五类食品有限公司

1、广西黑五类食品有限公司于1995年04月03日在容县工商行政管理局登记成立。2、法定代表人韦清文,公司...

03-25 16:45:24

【天天时快讯】北京市崇文区中学排名

1、2、北京市广渠门中学3、北京市龙潭中学4、北京市第十一中学分校(原永定门中学)5、北京市第五十中学...

03-25 16:50:47

年轻人的地摊故事:有人还清200万负债,有人陷于精神内耗 环球通讯

【民生调查局】编者按:这里是民生调查局,见人所未见,调查民生之变。关注你想关注的、你没关注的,调...

03-25 14:14:47

【全球时快讯】2023女生最容易考上的军校有哪些 女生军校推荐

2023年高考女生容易考上的军校有国防科技大学、解放军战略支援部队信息工程大学、中国人民武警警官学院...

03-25 12:09:25

获赔370万,宁德时代赢得与中创新航不正当竞争案

界面新闻记者|庄键宁德时代(300750 SZ)代诉中创新航(03931 HK)不正当竞争案件已于近日判决,中创新航...

03-25 10:20:31

tplink怎么设置IP

TPLINK路由器设置IP地址的方法:1、在浏览器中输入路由器的管理地址,当弹出路由器的管理界面时,输入帐...

03-25 09:20:05

每日短讯:芝加哥农产品期价24日上涨

新华社芝加哥3月24日电(记者徐静)芝加哥期货交易所玉米、小麦和大豆期价24日全线上涨。  当天,芝加...

03-25 08:01:20

魔兽世界私服网站新开网_魔兽世界私服网

1、额。2、熊熊或者统一还可以吧。本文就为大家分享到这里,希望小伙伴们会喜欢。

03-25 04:54:56

粳米和大米的区别_粳怎么读-世界今头条

1、【汉字】:粳【读音】:jīng【部首】:米【笔画】:13【释义】:】稻的一种,米粒宽而厚,近圆形,...

03-25 00:17:45

京东运动户外超级品类日26日开启 聚焦露营、骑行、垂钓、徒步四大场景 全场满299减80-世界今热点

3月26日京东运动户外超级品类日即将全面开启活动将聚焦最受消费者喜爱的露营骑行徒步垂钓等四大户外运动...

03-24 21:48:26

菲律宾香蕉和国产香蕉区别_世界聚看点

产地区别、价格区别、口感区别。产地:菲律宾香蕉主要来自菲律宾,进口香蕉可以在大型商场和超市购买,...

03-24 20:11:23

天天观天下!美疾控中心:美国耳念珠菌感染病例数持续上升

美国疾病控制和预防中心日前发布公报说,美国耳念珠菌感染病例数每年持续上升,其中2020至2021年在医疗保...

03-24 19:56:22

天天动态:上海市金山区预制菜产业发展协会成立“湾区食谷”品牌

近年来,预制菜成为发展迅速、潜力巨大的产业。近日,在市场监管部门的指导下,上海市金山区预制菜产业...

03-24 18:28:31

【全球新视野】2023年中国化肥行业市场全景调研

化学肥料,简称化肥。用化学和(或)物理方法制成的含有一种或几种农作物生长需要的营养元素的肥料。也称...

03-24 17:22:09

德外大街西社区

1、北京市西城区德胜街道德外大街西社区。本文到此分享完毕,希望对大家有所帮助。

03-24 15:50:12

燃竞巅峰,筑梦领绣|积极拥抱后疫情时代新变化,迸发强劲活力

律回春晖渐,万象始更新。随着春暖花开,我国经济发展迎来全面复苏,家居软装行业也是火力全开,各大企...

03-24 14:38:08

世界快资讯:解锁四川安岳的春天打开方式,逛街吃美食看电影,慢生活很惬意

春天是一个适合旅行的季节,背上行囊,带上相机,走进四川安岳,开启一场休闲惬意的慢旅行。安岳是一个...

03-24 12:57:00

天天资讯:良辰美景奈何天的意思

1、良辰美景奈何天的意思是:这样美丽的景色但是我无心欣赏,表达的意境是无奈的情感。出自《牡丹亭》中...

03-24 12:41:53

欠钱怎么构成诈骗案
【天天新要闻】杭州市佛教协会副会长、灵顺寺住持印旭法师圆寂,享年50岁
世界观热点:羊肚菌撑开“致富伞”
全球快播:浙商中拓03月27日获深股通增持28.94万股
【速看料】无人机板块3月27日跌0.66%,思特威领跌,主力资金净流出9.04亿元
“新运河”通航倒计时,杭州离“超级枢纽”更近了_百事通
两部门:继续实施物流企业大宗商品仓储设施用地税收优惠政策 环球微头条
当前聚焦:便民春风优服务 出口退税暖人心
大象脚印图片简笔画(大象脚印)
大宗交易:祥源文旅成交760万元,折价3.06%(03-27) 报道
传吉利旗下远程汽车赴美IPO前将再融资 环球通讯
金煌作品 时光未来,现代主义的极致演绎_环球微速讯
wPS如何打斜箭头 环球快讯
河狸家
天天快看:金雷股份3月27日盘中涨幅达5%
兴业银行南宁分行普惠金融出实招 线上“兴优贷”助企纾困 焦点要闻
现代汽车计划年内在美成立空中出行子公司
今日量比是什么意思换手率多少才合适_量比是什么意思_当前关注
贵州省黔东南苗族侗族自治州黄平县2023-03-26 21:13发布雷电黄色预警 当前关注
气温“狂飙”!洛阳最高气温将升至25℃
秦巴盆地美汉中|天天观天下
从百草园到三味书屋好句批注_从百草园到三味书屋好句 今日快讯
耗子尾汁是什么梗_网络语耗子尾汁是什么意思_新资讯
如何注销QQ秀
整改“一键打新”!多家券商已开始行动!
美国密西西比州受龙卷风影响地区进入紧急状态 全球速读
怂怂txt下载 世界最资讯
乌克兰与中国时差几个小时 乌克兰和中国时差多少个小时 全球快看
今日无言以对歌词王以太(无言以对 歌词)
门头沟这处棚改定向安置房即将精装修!另一地块完工待交竣!
宝藏Lumin潮玩由你,打造年轻人时尚出街风向标 快资讯
广西黑五类食品有限公司
【天天时快讯】北京市崇文区中学排名
年轻人的地摊故事:有人还清200万负债,有人陷于精神内耗 环球通讯
【全球时快讯】2023女生最容易考上的军校有哪些 女生军校推荐
获赔370万,宁德时代赢得与中创新航不正当竞争案
tplink怎么设置IP
每日短讯:芝加哥农产品期价24日上涨
魔兽世界私服网站新开网_魔兽世界私服网
粳米和大米的区别_粳怎么读-世界今头条
京东运动户外超级品类日26日开启 聚焦露营、骑行、垂钓、徒步四大场景 全场满299减80-世界今热点
菲律宾香蕉和国产香蕉区别_世界聚看点
天天观天下!美疾控中心:美国耳念珠菌感染病例数持续上升
天天动态:上海市金山区预制菜产业发展协会成立“湾区食谷”品牌
【全球新视野】2023年中国化肥行业市场全景调研
德外大街西社区
燃竞巅峰,筑梦领绣|积极拥抱后疫情时代新变化,迸发强劲活力
世界快资讯:解锁四川安岳的春天打开方式,逛街吃美食看电影,慢生活很惬意
天天资讯:良辰美景奈何天的意思
山东新发现富铁矿1400万吨
X 广告
资讯
X 广告

Copyright ©  2015-2022 北方畜牧网版权所有  备案号:京ICP备2021034106号-50   联系邮箱: 55 16 53 8@qq.com