Spring5.0源码深度解析之SpringBean的生命周期

Stella981
• 阅读 312

Spring5.0源码深度解析之SpringBean的生命周期

SpringBean的生命周期

一:单例与多例对象是如何初始化

单例默认情况下是在容器被加载的时候就会初始化

多例是在每次获取Bean对象的时候初始化

代码验证:

@Component public class UserEntity { public UserEntity() { System.out.println(">>>>UserEntity无参数构造函数执行..."); }

@Configuration @ComponentScan("com.mayikt.entity") public class MyConfig { }

>>>>UserEntity无参数构造函数执行...

当加上@Scope("prototype"),没有输出结果

@Component @Scope("prototype") public class UserEntity { public UserEntity() { System.out.println(">>>>UserEntity无参数构造函数执行..."); }

说明单例默认是在容器被加载的时候初始化,多例是在每次获取Bean对象的时候初始化。

二:Bean对象的初始化与销毁过程01

Bean初始化:指的就是对象已经创建,里面的所有set方法都已经执行完毕了。

举个例子:

@Configuration @ComponentScan("com.mayikt.entity") public class MyConfig {

/\*\*
 \* initMethod:指定初始化方法执行
 \* destroyMethod:指定销毁方法
 \* @return
 \*/
@Bean(initMethod = "**initMethod**",destroyMethod = "**destroyMethod**")
public UserEntity userEntity(){
    return new UserEntity();
}

}

@Component public class UserEntity { public UserEntity() { System.out.println(">>>>UserEntity无参数构造函数执行..."); } /** * 思考:initMethod是在无参构造函数之前执行还是后执行.. */ private void initMethod() { System.out.println(">>>>UserEntity initMethod 执行..."); } private void destroyMethod() { System.out.println(">>>>UserEntity destroyMethod 执行..."); } }

public static void main(String[] args) { /** * IOC容器初始化单例对象都是循环遍历调用getBean方法 */ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class); applicationContext.close();

返回结果

>>>>UserEntity无参数构造函数执行...
>>>>UserEntity initMethod 执行...
>>>>UserEntity destroyMethod 执行...

构造函数:Bean的创建,Map集合存储对象

initMethod:表示对象已经创建成功之后执行

destroyMethod:表示对象被销毁之后执行,clean

destroyMethod方法执行时,相当于调用了close方法去销毁Bean

applicationContext.close();

public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } }

protected void doClose() { if (this.active.get() && this.closed.compareAndSet(false, true)) { .... // Destroy all cached singletons in the context's BeanFactory. destroyBeans();

  // Close the state of this context itself.
  closeBeanFactory();

  // Let subclasses do some final clean-up if they wish...
  onClose();

  this.active.set(false);

} }

protected void destroyBeans() { getBeanFactory().destroySingletons(); }

Spring5.0源码深度解析之SpringBean的生命周期

public void destroySingletons() { super.destroySingletons(); this.manualSingletonNames.clear(); this.clearByTypeCache(); }

private void clearByTypeCache() { this.allBeanNamesByType.clear(); this.singletonBeanNamesByType.clear(); }

上面执行了clear操作,再回到前面

这里给用户自定义关闭操作:模板方法设计模式

protected void onClose() { // For subclasses: do nothing by default. }

再把活跃状态设置为false。

三:Bean对象的初始化与销毁过程02

1.实现InitializingBean,DisposableBean两个接口

@Component public class MemberEntity implements InitializingBean,DisposableBean{ // implements InitializingBean, DisposableBean public MemberEntity() { System.out.println("无参构造函数执行.."); } // afterPropertiesSet initMet hod // 1.对象创建 对象属性赋值 set方法全部走完 @Override public void afterPropertiesSet() throws Exception { System.out.println("MemberEntity >>>afterPropertiesSet"); } @Override public void destroy() throws Exception { System.out.println("MemberEntity >>> destroy"); } }

输出结果:

无参构造函数执行..
MemberEntity >>>afterPropertiesSet
MemberEntity >>> destroy

2.使用Java封装的注解方式@PostConstruct, @PreDestroy

@Component public class MemberEntity{ // implements InitializingBean, DisposableBean public MemberEntity() { System.out.println("无参构造函数执行.."); } // afterPropertiesSet initMet hod // 1.对象创建 对象属性赋值 set方法全部走完 @PostConstruct public void afterPropertiesSet() throws Exception { System.out.println("MemberEntity >>>afterPropertiesSet"); } @PreDestroy public void destroy() throws Exception { System.out.println("MemberEntity >>> destroy"); } }

输出结果

无参构造函数执行..
MemberEntity >>>afterPropertiesSet
MemberEntity >>> destroy

四:现在我们开始分析SpringBean的生命周期

SpringBean生命周期有个很好的理念就是后置处理器BeanPostProcessor

后置处理器:BeanPostProcessor

Spring5.0源码深度解析之SpringBean的生命周期

BeanPostProcessor引入:

@Component public class MyApplicationContext implements ApplicationContextAware { private ApplicationContext applicationContext; /** * spring底层中为什么能够实现ApplicationContextAware接口 就能够拿到ApplicationContext * @param applicationContext * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { MemberEntity memberEntity = applicationContext.getBean("memberEntity", MemberEntity.class); System.out.println("memberEntity:" + memberEntity); } }

@Configuration @ComponentScan("com.mayikt.entity") @Import(MyApplicationContext.class)//这里注入到spring容器中 public class MyConfig {}

输出结果:对象初始化,赋值完毕,就可以通过setApplicationContext拿到bean对象

memberEntity:com.mayikt.entity.MemberEntity@11e21d0e

思考问题:spring底层中为什么能够实现ApplicationContextAware接口 就能够拿到ApplicationContext

关键就是在于BeanPostProcessor

下面我们开始分析:BeanPostProcessor后置处理器(非常重要!!!)

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); refresh(); }

public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { .... try { .... // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); } .... }

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { .... // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }

@Override public void preInstantiateSingletons() throws BeansException {     .... List beanNames = new ArrayList<>(this.beanDefinitionNames);

for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction) () -> ((SmartFactoryBean) factory).isEagerInit(), getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } else { getBean(beanName); } } } .... }

Spring5.0源码深度解析之SpringBean的生命周期

@Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }

protected T doGetBean(final String name, @Nullable final Class requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { .... Object sharedInstance = getSingleton(beanName);//检测缓存中或者实例工厂中是否有对应的实例 if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);//返回对应的实例,有时候存在诸如BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回的实例 }

else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) {//只有在单例情况下才会去尝试解决循环依赖 throw new BeanCurrentlyInCreationException(beanName); }

  // Check if bean definition exists in this factory.
  BeanFactory parentBeanFactory = **getParentBeanFactory**();
  if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {//如果beaanDefinitionMap中也就是在所有已经加载的类中不包括beanName则尝试从parentBeanFactory中检测
     // Not found -> check parent.
     String nameToLookup = originalBeanName(name);
     if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
              nameToLookup, requiredType, args, typeCheckOnly);
     }
     else if (args != null) {//递归到BeanFactory中寻找
        // Delegation to parent with explicit args.
        return (T) parentBeanFactory.getBean(nameToLookup, args);
     }
     else {
        // No args -> delegate to standard getBean method.
        return parentBeanFactory.getBean(nameToLookup, requiredType);
     }
  }

  if (!typeCheckOnly) {//如果不是仅仅做类型检查,则是创建Bean,这里进行记录
     markBeanAsCreated(beanName);
  }

  try {
     final RootBeanDefinition mbd = **getMergedLocalBeanDefinition**(beanName);//转换为RootBeanDefinition
     checkMergedBeanDefinition(mbd, beanName, args);

     // Guarantee initialization of beans that the current bean depends on.
     String\[\] dependsOn = mbd.getDependsOn();
     if (dependsOn != null) {//若存在依赖则需要递归实例化依赖的Bean
        for (String dep : dependsOn) {
           if (isDependent(beanName, dep)) {
              throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
           }
           **registerDependentBean**(dep, beanName);//缓存依赖调用
           getBean(dep);
        }
     }

     // Create bean instance.
     if (mbd.isSingleton()) {//实例化依赖的bean后,便可实例化mbd本身了,单例模式的创建
        sharedInstance = **getSingleton**(beanName, () -> {
           try {
              return **createBean**(beanName, mbd, args);
           }
           catch (BeansException ex) {
              destroySingleton(beanName);
              throw ex;
           }
        });
        bean = **getObjectForBeanInstance**(sharedInstance, name, beanName, mbd);
     }

     else if (mbd.isPrototype()) {//prototype模式的创建
        // It's a prototype -> create a new instance.
        Object prototypeInstance = null;
        try {
           beforePrototypeCreation(beanName);
           prototypeInstance = **createBean**(beanName, mbd, args);
        }
        finally {
           afterPrototypeCreation(beanName);
        }
        bean = **getObjectForBeanInstance**(prototypeInstance, name, beanName, mbd);
     }

     else {
        String scopeName = mbd.getScope();
        final Scope scope = this.scopes.get(scopeName);
        if (scope == null) {
           throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
        }
        try {
           Object scopedInstance = scope.get(beanName, () -> {
              beforePrototypeCreation(beanName);
              try {
                 return **createBean**(beanName, mbd, args);
              }
              finally {
                 afterPrototypeCreation(beanName);
              }
           });
           bean = **getObjectForBeanInstance**(scopedInstance, name, beanName, mbd);
        }
        catch (IllegalStateException ex) {
           throw new BeanCreationException(beanName,
                 "Scope '" + scopeName + "' is not active for the current thread; consider " +
                 "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                 ex);
        }
     }
  }
  catch (BeansException ex) {
     cleanupAfterBeanCreationFailure(beanName);
     throw ex;
  }

}

// Check if required type matches the type of the actual bean instance. if (requiredType != null && !requiredType.isInstance(bean)) {//检测需要的类型是否符合实际bean的类型 try { T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return convertedBean; } catch (TypeMismatchException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }

步骤大致如下:

1.转换BeanName

2.尝试从缓存中加载单例bean

3.Bean的实例化

4.原型模式的依赖检查

5.检查parentBeanFactory

6.将存储XML配置文件的GernericBeanDefinition转换为RootBeanDefinition

7.寻找依赖

8.针对不同的scope进行bean的创建

9.类型转换

判断是单例:

if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }

@Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { .... Object beanInstance = doCreateBean(beanName, mbdToUse, args); .... }

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

final Object bean = instanceWrapper.getWrappedInstance(); .... Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper);//循环给属性赋值 exposedObject = initializeBean(beanName, exposedObject, mbd); } .... }

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(() -> { this.invokeAwareMethods(beanName, bean); return null; }, this.getAccessControlContext()); } else { this.invokeAwareMethods(beanName, bean); }

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
    wrappedBean = this.**applyBeanPostProcessorsBeforeInitialization**(bean, beanName);
}

try {
    this.**invokeInitMethods**(beanName, wrappedBean, mbd);**//执行自定义的init方法**
} catch (Throwable var6) {
    throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}

if (mbd == null || !mbd.isSynthetic()) {
    wrappedBean = this.**applyBeanPostProcessorsAfterInitialization**(wrappedBean, beanName);
}

return wrappedBean;

}

private void invokeAwareMethods(String beanName, Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) {    //判断类型并设置beanName ((BeanNameAware)bean).setBeanName(beanName); }

    if (bean instanceof BeanClassLoaderAware) {
        ClassLoader bcl = this.getBeanClassLoader();
        if (bcl != null) {
            ((BeanClassLoaderAware)bean).setBeanClassLoader(bcl);
        }
    }
    if (bean instanceof BeanFactoryAware) {
        ((BeanFactoryAware)bean).setBeanFactory(this);
    }
}

}

我们就明白了:IOC容器初始化单例对象都是循环遍历调用getBean方法。

下面我们手写看下springBean的生命周期

@Component public class PayEntity implements BeanNameAware, BeanFactoryAware, InitializingBean, ApplicationContextAware { public PayEntity() { System.out.println("1.对象的实例化完成.."); }

@Override
public void setBeanName(String name) {
    System.out.println("2.对象beanName:" + name);
}
@Override
public void **setBeanFactory**(BeanFactory beanFactory) throws BeansException {
    System.out.println("3.beanFactory:" + beanFactory);
}
@Override
public void **setApplicationContext**(ApplicationContext applicationContext) throws BeansException {
    System.out.println("4.获取到applicationContext对象");
}
@Override
public void **afterPropertiesSet**() throws Exception {
    System.out.println("5.bean init方法执行..");
}

输出结果

1.对象的实例化完成..
2.对象beanName:payEntity
3.beanFactory:org.springframework.beans.factory.support.DefaultListableBeanFactory@55f3ddb1: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,myConfig,payEntity]; root of factory hierarchy
4.获取到applicationContext对象
5.bean init方法执行..

BeanPostProcessor的作用

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { .... if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);//init之前处理操作 } .... try { invokeInitMethods(beanName, wrappedBean, mbd);//init执行逻辑 } .... if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);//init之后处理操作 }

return wrappedBean; }

BeanPostProcessor后置处理器的前置处理逻辑和后置处理逻辑:

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { Object current = beanProcessor.postProcessBeforeInitialization(result, beanName);    ... }

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { Object current = beanProcessor.postProcessAfterInitialization(result, beanName); ... }

我们自定义类实现BeanPostProcessor

@Component public class MyBeanPostProcessor implements BeanPostProcessor { //BeanPostProcessor 后置处理器 对我们bean的对象实现增强 @Override // 执行自定义init方法之前处理 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("执行init方法之前处理 : " + beanName); return bean; }

@Override
// 执行自定义init方法之后处理
public Object **postProcessAfterInitialization**(Object bean, String beanName) throws BeansException {
    System.out.println("执行init方法之后处理 :" + beanName);
    return bean;
}
//BeanPostProcessor  后置处理
// Aware    实现

}

输出结果:

执行init方法之前处理 : org.springframework.context.event.internalEventListenerProcessor
执行init方法之后处理 :org.springframework.context.event.internalEventListenerProcessor
执行init方法之前处理 : org.springframework.context.event.internalEventListenerFactory
执行init方法之后处理 :org.springframework.context.event.internalEventListenerFactory
执行init方法之前处理 : myConfig
执行init方法之后处理 :myConfig
1.对象的实例化完成..
2.对象beanName:payEntity
3.beanFactory:org.springframework.beans.factory.support.DefaultListableBeanFactory@55f3ddb1: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,myConfig,payEntity,com.mayikt.processor.MyBeanPostProcessor]; root of factory hierarchy
4.获取到applicationContext对象
执行init方法之前处理 : payEntity
5.bean init方法执行..
执行init方法之后处理 :payEntity

ApplicationAware接口原理

实现ApplicationAware接口怎么就可以setApplicationContext呢?

@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("4.获取到applicationContext对象"); }

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {

Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { Object current = beanProcessor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }

@Override public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; }

发现BeanPostProcessor后置处理器的postProcessAfterInitialization后置处理什么也没做:

再去前置找:

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {

Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { Object current = beanProcessor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }

Spring5.0源码深度解析之SpringBean的生命周期

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException { AccessControlContext acc = null;

if (System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); }

if (acc != null) { AccessController.doPrivileged((PrivilegedAction) () -> { invokeAwareInterfaces(bean); return null; }, acc); } else { invokeAwareInterfaces(bean); }

return bean; }

private void invokeAwareInterfaces(Object bean) { if (bean instanceof Aware) { 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类型,赋值 ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } }

看到没有,最后判断:bean instanceof ApplicationContextAware类型,再赋值

我们就可以知道上面提出的这个问题:实现ApplicationAware接口怎么就可以setApplicationContext?

所以我们知道ApplicationContextAware是通过BeanPostProcessor后置处理器的postProcessBeforeInitialization****前置处理实现的。

SpringBean的生命周期总结

源码分析流程:

1.进入到刷新refresh()方法

2.finishBeanFactoryInitialization()初始化所有单例对象

3.preInstantiateSingletons()初始化所有的单例对象:注意是非懒加载

4.getBean()-》doGetBean()先查询该对象是否有初始化过,没有的化就创建注册到IOC容器中

5.createBean()判断对象如果是单例的情况下,就调用该方法去创建对象

6.doCreateBean()创建IOC对象

7.createBeanInstance()使用Java的反射机制实例化我们的对象

8.populateBean()给对象的set属性赋值

9.initializeBean()执行初始化方法(也可以自己定义初始化的方法)

10.invokeAwareMethods()判断bean的类型是否是Aware相关依赖,如果存在的情况回调方法

11.applyBeanPostProcessorsBeforeInitialization()在初始化方法之前执行处理(增强)

12.invokeInitMethods()调用自定义的init方法,Java反射技术

13.applyBeanPostProcessorsAfterInitialization()在初始化方法之后执行处理(增强)

14.正常是用我们初始化好的这个Bean对象

15.销毁bean

本文参考

参考书籍:Spring源码深度解析

蚂蚁课堂:http://www.mayikt.com/

点赞
收藏
评论区
推荐文章
秃头王路飞 秃头王路飞
5个月前
webpack5手撸vue2脚手架
webpack5手撸vue相信工作个12年的小伙伴们在面试的时候多多少少怕被问到关于webpack方面的知识,本菜鸟最近闲来无事,就尝试了手撸了下vue2的脚手架,第一次发帖实在是没有经验,望海涵。languageJavaScript"name":"vuecliversion2","version":"1.0.0","desc
浅梦一笑 浅梦一笑
5个月前
初学 Python 需要安装哪些软件?超级实用,小白必看!
编程这个东西是真的奇妙。对于懂得的人来说,会觉得这个工具是多么的好用、有趣,而对于小白来说,就如同大山一样。其实这个都可以理解,大家都是这样过来的。那么接下来就说一下python相关的东西吧,并说一下我对编程的理解。本人也是小白一名,如有不对的地方,还请各位大神指出01名词解释:如果在编程方面接触的比较少,那么对于软件这一块,有几个名词一定要了解,比如开发环
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
技术小男生 技术小男生
5个月前
linux环境jdk环境变量配置
1:编辑系统配置文件vi/etc/profile2:按字母键i进入编辑模式,在最底部添加内容:JAVAHOME/opt/jdk1.8.0152CLASSPATH.:$JAVAHOME/lib/dt.jar:$JAVAHOME/lib/tools.jarPATH$JAVAHOME/bin:$PATH3:生效配置
光头强的博客 光头强的博客
5个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
5个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
小森森 小森森
5个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
晴空闲云 晴空闲云
5个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
5个月前
快速入门|使用MemFire Cloud构建React Native应用程序
MemFireCloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
NVIDIA安培架构下MIG技术分析
关键词:NVIDIA、MIG、安培一什么是MIG2020年5月,NVIDIA发布了最新的GPU架构:安培,以及基于安培架构的最新的GPU:A100。安培提供了许多新的特性,MIG是其中一项非常重要的新特性。MIG的全名是MultiInstanceGPU。NVIDIA安培架构中的MIG模式可以在A100GPU上并行运行七个作业。多实
helloworld_28799839 helloworld_28799839
5个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue