只有四个!只有四个!只有四个!

是的,Spring Bean的生命周期只有这四个阶段。

要彻底搞清楚Spring的生命周期,首先要把这四个阶段牢牢记住。实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。

  1. 实例化->Instantiation
  2. 属性赋值->Populate
  3. 初始化->Initialization
  4. 销毁->Destruction

实例化 -> 属性赋值 -> 初始化 -> 销毁

第一大类:影响多个Bean的接口

实现了这些接口的Bean会切入到多个Bean的生命周期中。正因为如此,这些接口的功能非常强大,Spring内部扩展也经常使用这些接口,例如自动注入以及AOP的实现都和他们有关。

  • BeanPostProcessor
  • InstantiationAwareBeanPostProcessor

这两兄弟可能是Spring扩展中最重要的两个接口!InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。如图:

其中 InstantiationAwareBeanPostProcessor继承自BeanPostProcessor 是spring非常重要的拓展接口

1、postProcessBeforeInstantiation调用时机为bean实例化(Instantiation)之前 如果返回了bean实例, 则会替代原来正常通过target bean生成的bean的流程. 典型的例如aop返回proxy对象. 此时bean的执行流程将会缩短, 只会执行
BeanPostProcessor#postProcessAfterInitialization接口完成初始化。
2、postProcessAfterInstantiation调用时机为bean实例化(Instantiation)之后和任何初始化(Initialization)之前。
3、postProcessProperties调用时机为postProcessAfterInstantiation执行之后并返回true, 返回的PropertyValues将作用于给定bean属性赋值. spring 5.1之后出现以替换@Deprecated标注的postProcessPropertyValues
4、postProcessPropertyValues已经被标注@Deprecated,后续将会被postProcessProperties取代。

进入执行流程

步骤1 :InstantiationAwareBeanPostProcessor的触发入口从AbstractAutowireCapableBeanFactory#createBean开始。

/*** Central method of this class: creates a bean instance,* populates the bean instance, applies post-processors, etc.** @see #doCreateBean*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// 省略......try {/*** 注释1. InstantiationAwareBeanPostProcessor#postProcessorsBeforeInstantiation触发入口*/Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}} catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}// 省略......try {/*** 注释2. postProcessAfterInstantiation、postProcessProperties 触发入口* 主要逻辑都在doCreateBean()方法中,* 方法中包含了实例化、属性赋值、初始化过程。逻辑很清晰* 这三个方法与三个生命周期阶段一一对应,非常重要*/Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;} catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {// A previously detected exception with proper bean creation context already,// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.throw ex;} catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}
}

步骤2:注释1中,跟进AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation, 分析postProcessorsBeforeInstantiation执行时机 :

/** 注释1 代码进入后执行InstantiationAwareBeanPostProcessor#ostProcessBeforeInstantiation方法*/
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// Make sure bean class is actually resolved at this point.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {/*** 注释3:回调beanPostProcessorsBeforeInstantiation实例化,如果返回bean非null则直接执行* 不为空null就直接返回了而不执行doCreateBean()方法了,而该方法是创建Bean对象的方法* beanPostProcessorsAfterInitialization进行实例初始化*/bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {/** 注释4 */bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}mbd.beforeInstantiationResolved = (bean != null);}return bean;
}/**注释3 代码跟进*/
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {/*** 注释5:只要其中一个postProcessBeforeInstantiation返回实例bean即结束回调,* 这个bean将会直接返回给bean容器管理*/InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);if (result != null) {return result;}}}return null;
}/** 注释4 代码跟进*/
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {/**注释6 */Object current = processor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;
}

可以看到,postProcessBeforeInstantiation在doCreateBean之前调用,也就是在bean实例化之前调用的,英文源码注释解释道该方法的返回值会替换原本的Bean作为代理,这也是Aop等功能实现的关键点。

代码说明:
1. 注释5中,如果postProcessBeforeInstantiation方法返回了Object是null;那么就直接返回,调用doCreateBean方法();
2. 注释5中,如果postProcessBeforeInstantiation返回不为null;说明修改了bean对象;然后这个时候就立马执行postProcessAfterInitialization方法(注意这个是初始化之后的方法,也就是通过这个方法实例化了之后,直接执行初始化之后的方法;中间的实例化之后 和 初始化之前都不执行);
3. 注释6中,在调用postProcessAfterInitialization方法时候如果返回null;那么就直接返回,调用doCreateBean方法();(初始化之后的方法返回了null,那就需要调用doCreateBean生成对象了)
4. 在调用postProcessAfterInitialization时返回不为null;那这个bean就直接返回给ioc容器了初始化之后的操作是这里面最后一个方法了;

步骤2:跟进AbstractAutowireCapableBeanFactory#doCreateBean, 分析postProcessAfterInstantiation、postProcessProperties执行时机 :

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {/**  注释7 实例化阶段! */instanceWrapper = createBeanInstance(beanName, mbd, args);}// 省略......// Initialize the bean instance.Object exposedObject = bean;try {/** 注释8 依据bean definition 完成bean属性赋值 */populateBean(beanName, mbd, instanceWrapper);/** 注释9 执行bean初始化 */exposedObject = initializeBean(beanName, exposedObject, mbd);} catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;} else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}// 省略......return exposedObject;
}

这三个方法与三个生命周期阶段一一对应,非常重要

  1. createBeanInstance() -> 实例化(注释7)
  2. populateBean() -> 属性赋值(注释8)
  3. initializeBean() -> 初始化(注释9)

注释8中,继续跟进AbstractAutowireCapableBeanFactory#populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.boolean continueWithPropertyPopulation = true;// InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()/*** 注释10:满足两个要求:* 1、BeanDefinition为应用程序bean,而非基础框架bean信息。* 2、注册过InstantiationAwareBeanPostProcessor类型接口,上文有提到这个标志位。* 3、注册了多个接口时,只要其中一个postProcessAfterInstantiation返回false,即停止后续执行。*/ if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {continueWithPropertyPopulation = false;break;}}}}// 忽略后续的属性赋值操作代码
}

可以看到该方法在属性赋值方法内,但是在真正执行赋值操作之前。其返回值为boolean,返回false时可以阻断属性赋值阶段(continueWithPropertyPopulation = false;
关于BeanPostProcessor执行阶段的源码穿插在下文Aware接口的调用时机分析中,因为部分Aware功能的就是通过他实现的!只需要先记住BeanPostProcessor在初始化前后调用就可以了。

第二大类:只调用一次的接口

这一大类接口的特点是功能丰富,常用于用户自定义扩展。
第二大类中又可以分为两类:

  1. Aware类型的接口
  2. 生命周期接口

Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!
Aware接口众多,这里同样通过分类的方式帮助大家记忆。
Aware接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是Aware接口的执行顺序,能够见名知意的接口不再解释。

Aware Group1

  1. BeanNameAware
  2. BeanClassLoaderAware
  3. BeanFactoryAware

Aware Group2

  1. EnvironmentAware
  2. EmbeddedValueResolverAware (这个知道的人可能不多,实现该接口能够获取Spring EL解析器,用户的自定义注解需要支持spel表达式的时候可以使用,非常方便。)
  3. ApplicationContextAware(ResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAware) 这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的ApplicationContext对象,因为ApplicationContext是一个复合接口,如下:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

这里涉及到另一道面试题,ApplicationContext和BeanFactory的区别,可以从ApplicationContext继承的这几个接口入手,除去BeanFactory相关的两个接口就是ApplicationContext独有的功能,这里不详细说明。
Aware调用时机源码分析

详情如下,忽略了部分无关代码。代码位置就是我们上文提到的initializeBean方法详情,这也说明了Aware都是在初始化阶段之前调用的!

// 注释9 代码进入 调用初始化阶段
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {// 注释11 这里调用的是Group1中的三个Bean开头的AwareinvokeAwareMethods(beanName, bean);Object wrappedBean = bean;/** * 这里调用的是Group2中的几个Aware,* 而实质上这里就是前面所说的BeanPostProcessor的调用点!* 也就是说与Group1中的Aware不同,这里是通过BeanPostProcessor(ApplicationContextAwareProcessor)实现的。*//** 注释12 */wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);/** 注释13 下文即将介绍的InitializingBean调用点 */invokeInitMethods(beanName, wrappedBean, mbd);/** 注释14 BeanPostProcessor的另一个调用点*/wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);return wrappedBean;
}/**注释11 代码进入 */
private void invokeAwareMethods(final String beanName, final Object bean) {if (bean instanceof Aware) {if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}if (bean instanceof BeanClassLoaderAware) {((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());}if (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);}}
}

注释11 代码进入后,可以看到并不是所有的Aware接口都使用同样的方式调用。Bean××Aware都是在代码中直接调用的。

而ApplicationContext相关的Aware都是通过applyBeanPostProcessorsBeforeInitialization来调用BeanPostProcessor#postProcessBeforeInitialization()实现的。感兴趣的可以自己看一下ApplicationContextAwareProcessor这个类的源码,就是判断当前创建的Bean是否实现了相关的Aware方法,如果实现了会调用回调方法将资源传递给Bean。
至于Spring为什么这么实现,应该没什么特殊的考量。也许和Spring的版本升级有关。基于对修改关闭,对扩展开放的原则,Spring对一些新的Aware采用了扩展的方式添加。
BeanPostProcessor的调用时机也能在这里体现,包围住invokeInitMethods方法,也就说明了在初始化阶段的前后执行。
关于Aware接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。每组中各个Aware方法的调用顺序其实没有必要记,有需要的时候点进源码一看便知。简单的两个生命周期接口
至于剩下的两个生命周期接口就很简单了,实例化和属性赋值都是Spring帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。

  1. InitializingBean 对应生命周期的初始化阶段,在上面源码的invokeInitMethods(beanName, wrappedBean, mbd);方法中调用。
    有一点需要注意,因为Aware方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用Aware接口获取的资源,这也是我们自定义扩展Spring的常用方式。
    除了实现InitializingBean接口之外还能通过注解或者xml配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。
  2. DisposableBean 类似于InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了DisposableBean接口的Bean然后调用其destroy()方法 。感兴趣的可以自行跟一下源码。

扩展阅读: BeanPostProcessor 注册时机与执行顺序
注册时机
我们知道BeanPostProcessor也会注册为Bean,那么Spring是如何保证BeanPostProcessor在我们的业务Bean之前初始化完成呢?
请看我们熟悉的refresh()方法的源码,省略部分无关代码:

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.// 所有BeanPostProcesser初始化的调用点registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.// 所有单例非懒加载Bean的调用点finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}}

可以看出,Spring是先执行registerBeanPostProcessors()进行BeanPostProcessors的注册,然后再执行finishBeanFactoryInitialization初始化我们的单例非懒加载的Bean。
执行顺序

BeanPostProcessor有很多个,而且每个BeanPostProcessor都影响多个Bean,其执行顺序至关重要,必须能够控制其执行顺序才行。关于执行顺序这里需要引入两个排序相关的接口:PriorityOrdered、Ordered

  • PriorityOrdered是一等公民,首先被执行,PriorityOrdered公民之间通过接口返回值排序
  • Ordered是二等公民,然后执行,Ordered公民之间通过接口返回值排序
  • 都没有实现是三等公民,最后执行

在以下源码中,可以很清晰的看到Spring注册各种类型BeanPostProcessor的逻辑,根据实现不同排序接口进行分组。优先级高的先加入,优先级低的后加入。

// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 首先,加入实现了PriorityOrdered接口的BeanPostProcessors,顺便根据PriorityOrdered排了序String[] 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);currentRegistryProcessors.clear();// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 然后,加入实现了Ordered接口的BeanPostProcessors,顺便根据Ordered排了序postProcessorNames = 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);currentRegistryProcessors.clear();// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
// 最后加入其他常规的BeanPostProcessorsboolean 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);currentRegistryProcessors.clear();}// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 首先,加入实现了PriorityOrdered接口的BeanPostProcessors,顺便根据PriorityOrdered排了序String[] 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);currentRegistryProcessors.clear();// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 然后,加入实现了Ordered接口的BeanPostProcessors,顺便根据Ordered排了序postProcessorNames = 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);currentRegistryProcessors.clear();// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
// 最后加入其他常规的BeanPostProcessorsboolean 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);currentRegistryProcessors.clear();}

根据排序接口返回值排序,默认升序排序,返回值越低优先级越高。

/*** Useful constant for the highest precedence value.* @see java.lang.Integer#MIN_VALUE*/int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;/*** Useful constant for the lowest precedence value.* @see java.lang.Integer#MAX_VALUE*/int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

PriorityOrdered、Ordered接口作为Spring整个框架通用的排序接口,在Spring中应用广泛,也是非常重要的接口。

总结

Spring Bean的生命周期分为四个阶段多个扩展点。扩展点又可以分为影响多个Bean影响单个Bean。整理如下:
四个阶段

  • 实例化 Instantiation
  • 属性赋值 Populate
  • 初始化 Initialization
  • 销毁 Destruction

多个扩展点

  • 影响多个Bean

    • BeanPostProcessor
    • InstantiationAwareBeanPostProcessor
  • 影响单个Bean

    • Aware

      • Aware Group1

        • BeanNameAware
        • BeanClassLoaderAware
        • BeanFactoryAware
      • Aware Group2
        • EnvironmentAware
        • EmbeddedValueResolverAware
        • ApplicationContextAware(ResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAware)
    • 生命周期

      • InitializingBean
      • DisposableBean

至此,Spring Bean的生命周期介绍完毕,由于作者水平有限难免有疏漏,欢迎留言纠错。

nfvo通过调用哪个接口实现对vnf生命周期的管理_Spring-Bean生命周期相关推荐

  1. 在VS2015中用C++创建DLL并用C#调用且同时实现对DLL的调试

    from:http://m.blog.csdn.net/article/details?id=51075023 在VS2015中先创建C#项目,然后再创建要编写的动态库DLL项目,这样做的好处是整个解 ...

  2. @Autowire注入了bean(A)在当前类,但其他类需要调用这个A,报null异常,使用get获取bean为解决方案

    @Autowire注入了bean(A)在当前类,但其他类需要调用这个A,报null异常,使用get获取bean为解决方案 问题背景 解决方案 心得 Lyric: 远远抛开一切 问题背景 在做项目时,我 ...

  3. QTP提供的编程接口实现对QTP操作

    我们可以通过调用 Automation Object Model 里的对象接口 ,来实现对QTP 的操作,如打开 /运行脚本等动作 Automation object model 的对象结构图如下 ( ...

  4. uni-app调用Native.jsAPI实现对Android原生日历的增删查操作

    文章目录 前言 一.引入插件(uni-app插件市场) 二.使用步骤 1.手机系统日历操作需要添加权限才能操作 2.在对应的页面中进行使用 总结 前言 最近的uni-app项目中有一个直播日历的功能, ...

  5. vue函数如何调用其他函数?_从源码中学Vue(一)生命周期中的钩子函数的那点事儿...

    欢迎来到我的<从源码中学Vue>专题系列文章,更多精彩内容持续更新中,欢迎关注 :) Vue作为当下前端最流行的框架之一,在国内占绝对的优势.所以接下来我们一起来学习它吧! 我不会像其它人 ...

  6. 170630、springboot编程之普通类中调用spring管理的bean对象

    我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器进行管理,但是在实际当中,我们往往会碰到在一个普通的Java类中,想直接使用 ...

  7. SpringBoot 项目中在Controller 直接调用Mapper,提示:Field mapper in ‘*Controller ‘ required a bean of type ‘*Map

    今天使用IDEA 搭建SpringBoot 项目,我心血来潮想省略Service 层的编写就直接在Controller 层调用Mapper 层接口,提示如下错误信息: **************** ...

  8. springCloud Feign调用报错 Parameter 6 of constructor in xxxxxxxx required a bean of type ‘xxxxxx‘ that

    Description: Parameter 6 of constructor in xxxxxxx required a bean of type 'xxxxxxx' that could not ...

  9. 基于python的类的调用(快递查询,天气查询,学员管理,旅游查询,电影查询,景点查询,手机销售系统,内涵段子,手机号id查询))

    一.汇总模型 #模块<==>python文件 #引入express中的KD类 from express import KD from weather import WT from stud ...

最新文章

  1. JAVA C++ 左花括号{该另起一行写还是写在行尾的思考
  2. Java深入了解String对象
  3. Spring框架在属性注入时@Autowired和@Resource的区别
  4. SQLServer获取最后插入生成的ID 不同方法
  5. 【NLP】Google BERT详解
  6. 8位16进制频率计设计实验--VHDL
  7. arm linux 时间获取,菜鸟自学ARM:linux下编程获取系统时间和设置时间
  8. RAN adjusts schedule for 2nd wave of 5G specifications
  9. 全球首个零损耗容器等多款新品重磅发布,华为云用全栈创新普惠千行百业
  10. 2019版PHP自动发卡平台源码
  11. 华为S5700交换机端口聚合
  12. 机械电子工程和计算机联系应用,论机械电子技术与计算机的融合发展
  13. Day6——yaml简介
  14. (公式)用欧拉公式推导三角函数恒等式
  15. Windows远程桌面无法最大化问题
  16. 阿姆斯特朗数python
  17. 斐波那契的N种实现方式
  18. VS Code 自定义语法高亮 —— 入门
  19. sprin boot计算公式
  20. Android Surface system analyze

热门文章

  1. Qt元对象QMetaObject的indexOfSlot等函数获取类方法注意问题
  2. java 优化 寄存器_JVM性能优化系列-(6) 晚期编译优化
  3. 浅析Hibernate映射(一)——基本映射
  4. iOS7 企业应用无法安装应用程序 因为证书无效的解决方案
  5. java 兼容excel_Java解析Excel工具类(兼容xls和xlsx)
  6. mysql change命令_Mysql 操作基本命令大全
  7. 计算结构体、数组、指针的sizeof
  8. redis3.0.7_sds.c_sdsnewlen()
  9. 傅里叶变换库FFTW的安装配置(VS2010)
  10. 建立SQL Server警告和给操作员发送email通知