Spring IOC初始化深度解析
1.前言
本文是基于JAVA配置方式对Spring IOC进行分析,掌握Spring IOC初始化流程对于我们更好的使用Spring、学习Spring还是很有帮助的,本文所使用的Spring版本为5.2.2,下面进入分析
2.初始化流程概要图
PS:图比较大,可以选择新标签页打开
3.初始化流程详解
3.1核心对象说明
beanDefinitionMap:缓存所有bean的bean定义(BeanDefinition),key为beanName,value为bean定义;
singletonObjects:单例bean缓存池(对于使用FactoryBean创建出来的bean,并未保存在此map中!FactoryBean的情况,这里面存的是beanName->factoryBeanObject);
singletonFactories:缓存单例工厂,beanName -> objectFactory,用来解决循环依赖问题;
earlySingletonObjects:缓存提前暴露出来的单例bean,已完成实例化,但未完成初始化,用来解决循环依赖问题;
singletonsCurrentlyInCreation:正在创建的bean name的集合,在开始创建bean时add;
factoryBeanObjectCache:缓存FactoryBean创建出来的单例bean,典型的例子就是mybatis中的mapper对象(MapperProxy),就保存在此map中。
3.2核心流程分析
在上图中,将Spring IOC初始化分为15个大步骤,每个大步骤里面有多个小步骤,本文将会对比较重要的步骤进行分析,通过这些步骤的解析,将解决如下几个问题:
1.Spring何时,如何解析注解信息,配置类如何生效?
2.@Configuration注解的真正含义?
3.@Import注解何时生效?
4.getBean(beanName)怎样完成bean的获取?
5.createBean()怎样完成bean的创建?
6.Spring如何区分普通bean与FactoryBean?
7.Spring如何完成依赖注入?
8.Spring如何解决循环依赖?
9.Spring何时为bean实例创建代理对象?
下面针对上面的8个问题,对应上图,一一分析:
<1>.Spring何时,如何解析注解信息,配置类如何生效?
在图中的9.1中:PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors方法调用:registryProcessor.postProcessBeanDefinitionRegistry(registry)方法,因为在2.1步骤中已经将Spring内部类ConfigurationClassPostProcessor注册到容器,所以这里会调用到ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法,内部调用processConfigBeanDefinitions(registry)方法,从容器中拿到所有的BeanDefinitionNames,通过metadata判断是否有@Configuration,@Component,@ComponentScan,@Import,@ImportResource,@Bean中的任意一个,如果bean加了前面的任何一个注解,则认定bean是候选的配置类(ConfigurationClassCandidate),同时,如果bean加有@Configuration,且其proxyBeanMethods属性为true,则设置bean的configurationClass属性为full,否则,如果加有@Configuration,@Component,@ComponentScan,@Import,@ImportResource,@Bean注解中的任意一个,则设置bean的configurationClass属性为lite;
------>调用ConfigurationClassParser类的parse(candidates)方法解析上一步得到的配置类
------>在ConfigurationClassParser的parse方法中遍历传进来的candidates,调用processConfigurationClass(configClass)处理配置类(将配置类传入,返回值为配置类的父类,当配置类的父类不为Object时,循环调用其父类),真正的处理逻辑在doProcessConfigurationClass(configClass, sourceClass)方法中,在该方法中,先处理@PropertySource、@PropertySources注解,再处理@ComponentScans,@ComponentScan注解,接着处理@Import注解,然后处理@ImportResource注解,再处理@Bean注解,然后处理加在接口默认方法上的@Bean注解,最后判断配置类是否有父类,如果有,则返回父类。下面来分析上面每个注解的处理过程:
处理@PropertySource、@PropertySources注解:解析属性文件,将属性添加到Spring内部的Environment中
处理@ComponentScans,@ComponentScan注解:调用ComponentScanAnnotationParser类的parse方法进行组件扫描,在方法内部最后会将扫描到的bean注册到beanDefinitionMap中,循环包扫描得到的BeanDefinitions,循环判断扫描到的bean是否是一个配置类,如果是,则递归调用parse()方法进行解析;
处理@Import注解:在processImports方法内部判断被导入的类的类型:
如果是ImportSelector.class类型,则初始化candidateClass,并调用invokeAwareMethods方法,调用candidateBean的selectImports方法,获得需要导入的类全名字符串数组,接着将数组转换为SourceClass对象(对加了注解的source classes以一致的方式处理进行的简单包装,不管他们是怎样被加载的),递归调用processImports方法,直到导入的类不是ImportSelector.class类型为止;
如果是ImportBeanDefinitionRegistrar.class类型,则将初始化candidateClass,并调用invokeAwareMethods方法,将registrar添加到configClass的importBeanDefinitionRegistrars,后续再处理;
如果不是上面2种类型,则说明导入的是一个普通类,则将其作为配置类处理,调用processConfigurationClass(configClass)方法进行解析。
处理@ImportResource注解:将@ImportResource注解属性添加到configClass的importedResources属性中,后续处理;
处理@Bean注解:获取所有加了@Bean注解的元数据,将元数据封装为BeanMethod对象保存到configClass的beanMethods属性中;
处理加在接口默认方法上的@Bean注解:如果配置类实现了接口的默认方法,则会获取接口中@Bean方法的默认实现元数据,将元数据封装为BeanMethod对象保存到configClass的beanMethods属性中,如果配置类没有重写接口的默认方法,则这一步与上一步处理@Bean注解拿到的元数据是相同的;
在图中的9.1.2中,对9.1.1得到的configurationClasses使用ConfigurationClassBeanDefinitionReader进行加载,调用loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator)方法,内部调用registerBeanDefinitionForImportedConfigurationClass(configClass)方法注册被导入的BeanDefinition到beanDefinitionMap,调用loadBeanDefinitionsForBeanMethod(beanMethod)方法注册@Bean方法BeanDefinition,调用loadBeanDefinitionsFromImportedResources(configClass.getImportedResources())处理通过@ImportResources导入的xml配置,调用loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars())方法进而调用ImportBeanDefinitionRegistrar的registerBeanDefinitions方法注册BeanDefinition。
至此,配置类解析、注册完毕。
<2>@Configuration注解的真正含义?
在图中的9.2中,调用beanFactoryPostProcessor.postProcessBeanFactory(beanFactory),由于ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,而这个接口继承了BeanFactoryPostProcessor接口,所以会调用到ConfigurationClassPostProcessor的postProcessBeanFactory方法,在方法中通过在9.1.1中设置到BeanDefinition中的configurationClass属性判断是否需要增强@Configuration类,如果配置类的configurationClass属性为lite则返回;如果为full,则为其创建enhancedClass,并通过beanDef.setBeanClass(enhancedClass)设置到BeanDefinition中。为什么要为@Configuration类创建代理对象呢?因为存在这种情况:在@Configuration类中有2个方法,@Bean ClassA classA()与@Bean ClassB classB(),classB()中方法内部调用了classA()方法,这种情况当然是常见的,比如SqlSessionFactory中需要获得DataSource;如果不使用cglib为@Configuration类创建代理修改对@Bean方法的调用,那么在调用classA()和classB()方法时都会调用到new Class(),也就是说会创建2个ClassA对象,也就违反了单例的原则,而且,这2个对象一个存在于容器中,另一个存在于classB对象中,除了classB对象,再无其他引用指向它,所以,@Configuration的类默认会被Spring使用cglib增强,用来解决创建单例对象遇到的问题, 这才是@Configuration类与其他配置类(@Component、@ComponentScan、@Import、@ImportResource、@Bean)的不同之处!!!
既然创建了代理对象,又是如何解决单例问题的呢?EnhancedClass中设置了方法过滤,Spring在实例化@Configuration类时为其创建代理对象,当调用到@Bean方法时,会进入到MethodInterceptor的intercept方法中,在方法中判断currentlyInvoked与intercept方法的第二个参数beanMethod对比2个Method是否是同一个,如果是同一个,则使用cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs)调用父类方法,创建对象;如果不是同一个,说明这个classA()方法是classB()方法调用的,也就需要通过beanFactory从容器中拿到classA实例;currentlyInvoked是啥?为啥可以这样对比呢?因为Spring通过工厂方法方式实例化对象的时候会调用到SimpleInstantiationStrategy(简单的初始化策略)中的instantiate方法,在方法内部会将现在调用的实例化方法设置到currentlyInvokedFactoryMethod(一个ThreadLocal类型变量)中,这样在实例化classB时,也就是调用classB()方法时,可以通过currentlyInvokedFactoryMethod.get()拿到现在正在初始化的方法,在classB()方法内部调用classA()方法时,classB对象还正在创建,所以在调用classA()时,程序运行到切面逻辑,在拦截器中发现classA()和通过currentlyInvokedFactoryMethod.get()拿出来的方法不是同一个,则需要通过beanFactory.getBean(beanName)获取bean,如何保证创建单例bean的问题貌似解决了,不过,还有一个重要的问题,那就是@Configuration类的代理类中并没有工厂对象,没关系,我们可以通过enhancer.setInterfaces(new Class<?>[] EnhancedConfiguration.class})为代理增强类增加对BeanFactoryAware接口的实现,这样在Spring调用到BeanFactoryAware的setBeanFactory方法时,我们在@Configuration类代理对象中就可以通过beanFactory.getBean(beanName)从容器中获取bean了,不过,到这里,还存在一个问题,只有setBeanFactory方法还不够,我们还需要在setBeanFactory方法内部将Spring传过来的beanFactory保存起来,没问题,那就再增加一个对BeanFactoryAware的setBeanFactory方法调用的拦截器,在Spring调用setBeanFactory方法时,将beanFactory保存起来,说到保存,那我们的代理类中就需要有一个BeanFactory类型的变量,可以通过enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));为增强类设置创建策略,在transform方法中为Enhance添加beanFactory字段;
到这里,单例对象的创建问题完美解决,主要还是代理思想的运用。再来总结一下:为@Configuration类创建增强类EnhancedConfiguration,添加对BeanFactoryAware接口的实现,同时加入beanFactory字段,添加对setBeanFactory方法和对@Bean方法调用的拦截器,当调用setBeanFactory方法时,将beanFactory保存到代理对象中,在调用@Bean方法时通过对比代理方法与实例化调用的是不是同一个方法判断是调用父类的方法真正创建还是通过beanFactory.getBean(beanName)获取bean。
<3>@Import注解何时生效?
这里要分3种情况分析
1.如果通过@Import导入的是普通类,则在9.1.1.3步骤中当做是配置类调用processConfigurationClass(configClass)进行处理;
2.如果是@Import导入的是ImportSelector类型的类,则在9.1.1.3步骤中直接调用其selectImports方法获取需要导入全类名数组,获取Class并注册到beanDefinitionMap;
3.如果@Import导入的是ImportBeanDefinitionRegistrar类型的类,则在9.1.1.3步骤中将实例化ImportBeanDefinitionRegistrar类,然后调用configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata())将其添加到importBeanDefinitionRegistrars中,在9.1.2中调用 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars())遍历之前得到的importBeanDefinitionRegistrars,调用其registerBeanDefinitions方法完成注册。
<4>getBean(beanName)怎样完成bean的获取?
在15.1中,getBean方法是空壳方法,继续调用doGetBean方法,在doGetBean方法中,先将传过来的name转化为beanName,为什么要转换?这里有2个原因:原因1,name可能是别名,singletonObjects只维护了beanName ->bean instance的对应关系,没有维护别名与bean instance 的对应关系,所以这里需要先将别名转化为beanName;原因2:name可能是以&开头,表名现在需要获取的是FactoryBean本身,而普通的bean instance,而单例的FactoryBean也保存在singletonObjects中,但是在保存到singletonObjects时将&移除了,所以要获取FactoryBean本身,需要将name中的&移除。然后调用getSingleton(beanName)方法从容器中获取bean,如果从singletonObjects获取bean为null,则说明bean尚未实例化或已经实例化但因为循环依赖问题还未添加到singletonObjects中,如果bean正在创建则从earlySingletonObjects中获取提前暴露出来的bean,如果为null,且允许循环引用,则从中singletonFactories获取singletonFactory,如果singletonFactory不为nul,则通过singletonFactory.getObject()获取提前暴露出来的bean,然后将bean添加到earlySingletonObjects中;注意:earlySingletonObjects中的bean是完成实例化,但未完成初始化的bean!!!获取到bean之后,调用getObjectForBeanInstance(sharedInstance, name, beanName, null),判断name是否以&开头,表明用户需要获取FactoryBean本身,如果是以&开头,且sharedInstance为FactoryBean类型,则直接返回,如果sharedInstance不是FactoryBean类型,则也直接返回sharedInstance(普通bean);否则,说明现在是要获取FactoryBean创建出来的bean,先判断factoryBeanObjectCache中获取已经被FactoryBean创建出来的bean,如果获取为null,则调用getObjectFromFactoryBean方法从FactoryBean中获取bean,FactoryBean是单例并且已经注册到singletonObjects,则调用factory.getObject()获取bean,接着调用beforeSingletonCreation(beanName)将beanName添加到singletonsCurrentlyInCreation,调用postProcessObjectFromFactoryBean(object, beanName)应用bean后置处理器,调用afterSingletonCreation(beanName)将beanName从singletonsCurrentlyInCreation中移除,最后将bean保存到factoryBeanObjectCache并返回bean。至此,getBean(name)流程结束。
<5>createBean()怎样完成bean的创建?
在上面的第<4>步中,如果调用getSingleton(beanName)返回null,则说明bean既不存在singletonObjects中,也为提前暴露,则此时需要真正创建bean;如果bean是单例,则调用getSingleton(beanName, singletonFactory)方法,在这个方法中主要完成4件事:1.调用beforeSingletonCreation(beanName)将beanName添加到singletonsCurrentlyInCreation;2.调用singletonFactory.getObject()创建bean;3.调用afterSingletonCreation(beanName)将beanName从singletonsCurrentlyInCreation中移除;4.如果创建bean成功,则调用addSingleton(beanName, singletonObject)将bean添加到singletonObjects中;最重要的当然是singletonFactory.getObject()创建bean的过程,当调用singletonFactory.getObject()方法时,会调用到createBean(beanName, mbd, args)方法(图中的15.2),在方法调用resolveBeforeInstantiation(beanName, mbdToUse)方法在bean实例化之前应用bean后置处理器,如果postProcessBeforeInstantiation方法返回的bean不为空(这里默认返回null),则直接调用bean初始化后置处理器(不会执行doGetBean()),并返回bean;回到createBean方法,继续调用doCreateBean(beanName, mbdToUse, args)真正开始创建bean,先从factoryBeanInstanceCache获取bean,如果未获取到,则调用createBeanInstance(beanName, mbd, args)方法创建bean实例,在createBeanInstance方法中,如果BeanDefinition中的工厂方法不为空,则调用instantiateUsingFactoryMethod(beanName, mbd, args)使用工厂方法实例化bean(在@Configuration配置类中通过@Bean配置的bean在此初始化),否则,继续调用determineConstructorsFromBeanPostProcessors确定bean的构造器,如果有设置构造器参数自动注入,则调用autowireConstructor(beanName, mbd, ctors, null)使用自动注入的构造方法创建bean实例,否则,使用默认构造方法创建bean实例并返回。回到doGetBean方法,通过earlySingletonExposure判断是否允许提前暴露单例bean,如果为true,则调用addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))将singletonFactory添加到singletonFactories中,用来解决循环依赖问题(详细的后面再说),然后调用populateBean方法完成属性填充,调用initializeBean方法完成bean的初始化,最后调用registerDisposableBeanIfNecessary方法注册类型为DisposableBean类型、AutoCloseable类型或BeanDefinition中有设置destroyMethodName方法的bean到disposableBeans中。populateBean方法下面再说,先来说一下initializeBean方法:先调用invokeAwareMethods(beanName, bean)方法完成对实现了Aware接口实现类的set方法的调用,再调用processor.postProcessBeforeInitialization(result, beanName),InitDestroyAnnotationBeanPostProcessor实现了该方法,在postProcessBeforeInitialization方法中完成对加了@PostConstruct注解的方法的调用;然后调用invokeInitMethods方法,方法中先调用((InitializingBean) bean).afterPropertiesSet()完成对实现了InitializingBean接口的实现类的afterPropertiesSet方法调用,再调用自定义的初始化方法(init-method):invokeCustomInitMethod(beanName, bean, mbd);
到这里bean就初始化完成了。再来梳理一下bean初始化的过程,先调用Aware接口的set方法,再调用加了@PostConstruct注解的方法,然后调用实现了InitializingBean接口实现类的afterPropertiesSet方法,最后调用自定义的ini-method。
<6>Spring如何区分普通bean与FactoryBean?
在Spring中,其实有3种bean,普通bean、FactoryBean类型的bean和调用FactoryBean.getObject()获取到的bean;在singletonObjects中保存了前两种bean实例,对于FactoryBean类型的bean,在保存到singletonObjects中时只会将FactoryBean的beanName保存,而不会加上&前缀;而通过调用FactoryBean.getObject()获取到的bean保存在FactoryBeanRegistrySupport类的factoryBeanObjectCache中,在getBean的时候会先获取到FactoryBean,再通过factoryBeanObjectCache获取FactoryBean创建出来的bean;这样,如果想获取到FactoryBean本身,则调用getBean(&beanName),其他情况,调用getBean(beanName)即可。
<7>Spring如何完成依赖注入?
来分析populateBean方法(图中的15.2.2):先调用ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName),在postProcessAfterInstantiation方法中可以进行自定义的属性注入,如果返回true,则Spring的依赖注入还会应用,如果返回false,则Spring的依赖注入将不会应用(被跳过),Spring中所有实现了InstantiationAwareBeanPostProcessor接口的实现类都默认返回了true;如果bean的自动注入模型为AUTOWIRE_BY_NAME或AUTOWIRE_BY_TYPE,则会调用autowireByName(beanName, mbd, bw, newPvs)或autowireByType(beanName, mbd, bw, newPvs),在autowireByName方法中会获取到bean的所有非简单数据类型的setXxx(BeanA bfjdsl)方法,然后getBean(xxx)获取依赖属性,设置到MutablePropertyValues中;在autowireByType方法中会调用bean的所有setXxx(BeanA beanA)方法,获取到所有的set方法参数类型,按照类型从容器中获取依赖bean属性,然后设置到MutablePropertyValues中,完成按照名字自动注入和按照类型自动注入后(只是将依赖的属性设置到MutablePropertyValues,尚未应用到bean),调用ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName)方法,这里会调用到AutowiredAnnotationBeanPostProcessor的postProcessProperties方法完成基于@Autowired和@Value注解的依赖注入;最后调用applyPropertyValues(beanName, mbd, bw, pvs)方法将通过bean依赖的属性应用到bean中。
<8>Spring如何解决循环依赖?
循环依赖可以分为3种情况,Spring可以解决前2种情况的循环依赖:假如有beanA与beanB,他们相互依赖对方,
第一种:beanA与beanB都是单例对象,是通过set方法注入或者字段注入方式依赖,则可以Spring可以正常处理,如果是通过构造方法方式注入(比如在beanA的构造方法中注入beanB),则会Spring无法解决,会抛出循环依赖异常;
第二种:beanA与beanB其中一个是单例对象,另外一个是原型对象,这种情况下bean也可以正常完成初始化,同样的,如果是通过构造方法方式注入(比如在beanA的构造方法中注入beanB),则会Spring无法解决,会抛出循环依赖异常;
第三种:beanA与beanB其中一个是原型对象,则无法完成bean的初始化,会抛出循环依赖异常;
下面详细说一下第一种情况Spring是如何解决循环依赖的(第二种情况和第一种类似,不再赘述):
在图中的15.2.1,createBeanInstance(beanName, mbd, args)完成对bean的实例化(创建) ---> addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))将raw bean封装到singletonFactory保存到singletonFactories中 ---> populateBean(beanName, mbd, instanceWrapper)为bean填充属性,此时发现beanA依赖于beanB,则getBean(beanB),发现为null,则与上面beanA的过程一样,创建beanB,并进行依赖注入,此时发现beanB依赖于beanA,然后有调用getBean方法获取beanA,因为beanA正在创建,且在beanA创建过程中允许循环依赖,所以可以在singletonFactories获得到持有beanA引用的singletonFactory,然后调用singletonFactory的getObject方法拿到之前暴露出来的beanA,然后,beanB继续下面的属性填充,属性填充完成后,继续调用初始化方法对bean进行初始化,beanB初始化完成后,添加到singletonObjects中;beanA终于可以继续往下填充属性了,顺利拿到beanB,继续完成下面的初始化工作,然后添加到singletonObjects中,到这里,循环引用的处理过程就完成了。
9.Spring何时为bean实例创建代理对象?
在initializeBean方法的最后(图中的15.2.3),调用applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)方法,内部调用processor.postProcessAfterInitialization(result, beanName)为bean创建代理对象并返回(如果必要的话),Spring Aop创建代理对象的详细过程可以看我的另一个博文:Spring源码分析之AOP
4.Spring中的SPI
4.1 基于SPI机制扩展接口源码分析
比较重要的有 如下10个接口 ,来看一下定义:
1.BeanDefinitionRegistryPostProcessor :
- /**
- * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
- * the registration of further bean definitions <i>before</i> regular
- * BeanFactoryPostProcessor detection kicks in. In particular,
- * BeanDefinitionRegistryPostProcessor may register further bean definitions
- * which in turn define BeanFactoryPostProcessor instances.
- * 对标准的BeanFactoryPostProcessor SPI的扩展,允许在常规的BeanFactoryPostProcessor
- * 检测开始之前注册更多的bean definitions,特别是
- * BeanDefinitionRegistryPostProcessor可以注册更多的bean definitions,作为回报
- * 这些更多的bean中有BeanFactoryPostProcessor类型的bean,后续这些更多的bean中
- * BeanFactoryPostProcessor类型的bean的postProcessBeanFactory()方法也会被调用
- *
- * @author Juergen Hoeller
- * @since 3.0.1
- * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
- *
- * 典型的例子就是ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry(...)方法会扫描,
- * 注册组件beans包含的bean及通过@Import导入的bean,这些bean中如果有BeanFactoryPostProcessor类型的,
- * 则后续这些bean的postProcessBeanFactory(...)方法也会被调用
- *
- */
- public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
- /**
- * Modify the application context's internal bean definition registry after its
- * standard initialization. All regular bean definitions will have been loaded,
- * but no beans will have been instantiated yet. This allows for adding further
- * bean definitions before the next post-processing phase kicks in.
- * 在bean definition registry完成标准的初始化后修改应用上下文中内部的bean定义registry
- * 所有常规的bean definitions将会被加载,但还不会有bean被实例化,这时,允许在下一个后置处理阶段开始之前
- * 添加更多的bean definition
- * @param registry the bean definition registry used by the application context
- * @throws org.springframework.beans.BeansException in case of errors
- */
- void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
- }
2.ImportSelector:
- /**
- * Interface to be implemented by types that determine which @{@link Configuration}
- * class(es) should be imported based on a given selection criteria, usually one or
- * more annotation attributes.
- * 由确定哪个@Configuration类的类型实现的接口,应该基于给定的条件导入,通常有一个或多个注解属性
- *
- * <p>An {@link ImportSelector} may implement any of the following
- * {@link org.springframework.beans.factory.Aware Aware} interfaces,
- * and their respective methods will be called prior to {@link #selectImports}:
- * <ul>
- * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
- * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
- * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
- * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
- * </ul>
- * 一个ImportSelector可以实现下面任何的Aware接口,他们各自的方法将会在selectImports(...)方法
- * 调用前被调用
- *
- * <p>Alternatively, the class may provide a single constructor with one or more of
- * the following supported parameter types:
- * <ul>
- * <li>{@link org.springframework.core.env.Environment Environment}</li>
- * <li>{@link org.springframework.beans.factory.BeanFactory BeanFactory}</li>
- * <li>{@link java.lang.ClassLoader ClassLoader}</li>
- * <li>{@link org.springframework.core.io.ResourceLoader ResourceLoader}</li>
- * </ul>
- *
- * <p>{@code ImportSelector} implementations are usually processed in the same way
- * as regular {@code @Import} annotations, however, it is also possible to defer
- * selection of imports until all {@code @Configuration} classes have been processed
- * (see {@link DeferredImportSelector} for details).
- * ImportSelector接口的实现类通过以相同的方式被处理,通常是@Import注解方式,不过,
- * 也可以延迟imports选择直到@Configuration类被处理完成后
- *
- * @author Chris Beams
- * @since 3.1
- * @see DeferredImportSelector
- * @see Import
- * @see ImportBeanDefinitionRegistrar
- * @see Configuration
- *
- * 例:通过一个注解,一个ImportSelector的实现类导入ImportSelectorModel(需要导入给Spring的bean)
- * 1. @Target(ElementType.TYPE)
- * @Retention(RetentionPolicy.RUNTIME)
- * @Documented
- * @Import(ImportSelectorTest.class)
- * public @interface EnableSelectImportTest {
- * }
- *
- * 2.
- * public class ImportSelectorTest implements ImportSelector {
- * @Override
- * public String[] selectImports(AnnotationMetadata importingClassMetadata) {
- * return new String[]{"com.wym.test.importtest.selector.ImportSelectorModel"};
- * }
- * }
- * 3.
- * @Configuration
- * @EnableSelectImportTest
- * public class ImportSelectorApp {
- *
- * public static void main(String[] args) {
- * AnnotationConfigApplicationContext applicationContext =
- * new AnnotationConfigApplicationContext(ImportSelectorApp.class);
- * System.out.println("ImportSelectorModel=====" + applicationContext.getBean(ImportSelectorModel.class));
- * }
- * }
- */
- public interface ImportSelector {
- /**
- * Select and return the names of which class(es) should be imported based on
- * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
- * 根据导入的@Configuration类的AnnotationMetadata选择并返回要导入的类名(全类名)
- */
- String[] selectImports(AnnotationMetadata importingClassMetadata);
- }
3.BeanFactoryPostProcessor :
4.ImportBeanDefinitionRegistrar:
- /**
- * Interface to be implemented by types that register additional bean definitions when
- * processing @{@link Configuration} classes. Useful when operating at the bean definition
- * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
- * 当处理@Configuration类,由注册额外bean definitions的类型实现的接口
- * 适用于bean definition的操作级别(相对于@Bean方法/实例级别) 是期望的或必要的
- *
- * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
- * may be provided to the @{@link Import} annotation (or may also be returned from an
- * {@code ImportSelector}).
- * 连同@Configuration类和ImportSelector实现类,此类型的类以通过@Import注解被导入
- *
- * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
- * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
- * methods will be called prior to {@link #registerBeanDefinitions}:
- * ImportBeanDefinitionRegistrar的实现类可能实现下面任意的Aware接口,
- * 他们各自的方法将会在registerBeanDefinitions(...)方法被调用前调用
- * <ul>
- * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
- * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
- * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
- * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
- * </ul>
- *
- * <p>Alternatively, the class may provide a single constructor with one or more of
- * the following supported parameter types:
- * 或者,这个类可以提供一个有参构造方法,构造方法的参数可以是一个或多个下面支持的参数类型
- * <ul>
- * <li>{@link org.springframework.core.env.Environment Environment}</li>
- * <li>{@link org.springframework.beans.factory.BeanFactory BeanFactory}</li>
- * <li>{@link java.lang.ClassLoader ClassLoader}</li>
- * <li>{@link org.springframework.core.io.ResourceLoader ResourceLoader}</li>
- * </ul>
- *
- * <p>See implementations and associated unit tests for usage examples.
- *
- * @author Chris Beams
- * @author Juergen Hoeller
- * @since 3.1
- * @see Import
- * @see ImportSelector
- * @see Configuration
- *
- * 典型应用:Spring AOP
- * AspectJAutoProxyRegistrar通过实现registerBeanDefinitions方法将
- * AnnotationAwareAspectJAutoProxyCreator.class注册到容器,获取@EnableAspectJAutoProxy注解
- * 信息,设置proxyTargetClass和exposeProxy属性
- */
- public interface ImportBeanDefinitionRegistrar {
- /**
- * Register bean definitions as necessary based on the given annotation metadata of
- * the importing {@code @Configuration} class.
- * 基于导入@Configuration类的注解元数据,根据需要注册bean definition
- * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
- * registered here, due to lifecycle constraints related to {@code @Configuration}
- * class processing.
- * 注意:BeanDefinitionRegistryPostProcessor类型并不在这里被注册,由于与@Configuration类
- * 相关生命周期限制正在处理
- * <p>The default implementation delegates to
- * {@link #registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)}.
- * @param importingClassMetadata annotation metadata of the importing class
- * @param registry current bean definition registry
- * @param importBeanNameGenerator the bean name generator strategy for imported beans:
- * {@link ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR} by default, or a
- * user-provided one if {@link ConfigurationClassPostProcessor#setBeanNameGenerator}
- * has been set. In the latter case, the passed-in strategy will be the same used for
- * component scanning in the containing application context (otherwise, the default
- * component-scan naming strategy is {@link AnnotationBeanNameGenerator#INSTANCE}).
- * @since 5.2
- * @see ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR
- * @see ConfigurationClassPostProcessor#setBeanNameGenerator
- */
- default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
- BeanNameGenerator importBeanNameGenerator) {
- registerBeanDefinitions(importingClassMetadata, registry);
- }
- /**
- * Register bean definitions as necessary based on the given annotation metadata of
- * the importing {@code @Configuration} class.
- * 基于导入@Configuration类的注解元数据,根据需要注册bean definition
- * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
- * registered here, due to lifecycle constraints related to {@code @Configuration}
- * class processing.
- * <p>The default implementation is empty.
- * @param importingClassMetadata annotation metadata of the importing class
- * @param registry current bean definition registry
- */
- default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- }
- }
5.InstantiationAwareBeanPostProcessor:
- /**
- * Subinterface of {@link BeanPostProcessor} that adds a before-instantiation callback,
- * and a callback after instantiation but before explicit properties are set or
- * autowiring occurs.
- *
- * <p>BeanPostProcessor的子接口,添加在bean实例化之前和
- * 实例化之后但是未完成明确的属性填充和自动注入时的回调
- *
- * <p>Typically used to suppress default instantiation for specific target beans,
- * for example to create proxies with special TargetSources (pooling targets,
- * lazily initializing targets, etc), or to implement additional injection strategies
- * such as field injection.
- *
- * <p>典型的应用是对特定的目标bean压制默认的实例化,例如:用特定的TargetSources创建代理,
- * 或者去实现额外的注入策略,比如:字段注入
- *
- * <p><b>NOTE:</b> This interface is a special purpose interface, mainly for
- * internal use within the framework. It is recommended to implement the plain
- * {@link BeanPostProcessor} interface as far as possible, or to derive from
- * {@link InstantiationAwareBeanPostProcessorAdapter} in order to be shielded
- * from extensions to this interface.
- *
- * <p>注意:这个接口是一个有特殊目的的接口,主要是供框架内部使用.建议尽可能使用简单的
- * BeanPostProcessor接口,或者继承InstantiationAwareBeanPostProcessorAdapter
- * 以此来屏蔽从本接口进行扩展
- *
- * @author Juergen Hoeller
- * @author Rod Johnson
- * @since 1.2
- * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#setCustomTargetSourceCreators
- * @see org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator
- *
- * 本接口的典型应用在与其postProcessProperties方法,其他方法暂时未被使用
- * 1.AutowiredAnnotationBeanPostProcessor通过postProcessProperties方法完成对
- * 加了@Autowired和@Value注解的字段或方法完成自动注入
- * 2.CommonAnnotationBeanPostProcessor通过postProcessProperties方法完成对@Resource
- * 注解的自动注入
- */
- public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
- /**
- * Apply this BeanPostProcessor <i>before the target bean gets instantiated</i>.
- * The returned bean object may be a proxy to use instead of the target bean,
- * effectively suppressing default instantiation of the target bean.
- * <p>If a non-null object is returned by this method, the bean creation process
- * will be short-circuited. The only further processing applied is the
- * {@link #postProcessAfterInitialization} callback from the configured
- * {@link BeanPostProcessor BeanPostProcessors}.
- * <p>This callback will be applied to bean definitions with their bean class,
- * as well as to factory-method definitions in which case the returned bean type
- * will be passed in here.
- * <p>Post-processors may implement the extended
- * {@link SmartInstantiationAwareBeanPostProcessor} interface in order
- * to predict the type of the bean object that they are going to return here.
- * <p>The default implementation returns {@code null}.
- *
- * <p>在目标bean实例化之前应用BeanPostProcessor,返回bean可能是目标bean的代理对象,
- * 有效的抑制目标bean的默认实例化
- * 如果该方法返回一个非空对象,这个bean的创建过程将会被短路(不会执行后面的依赖注入,初始化方法)
- * 唯一进一步处理的是BeanPostProcessor的postProcessAfterInitialization方法调用
- * <p>这个回调将被应用于有bean class的bean definitions,以及工厂方法定义,在这种情况下
- * 返回的bean类型将会通过
- * <p>Post-processors可以实现扩展接口:SmartInstantiationAwareBeanPostProcessor
- * 为了预测将在这里返回的bean对象的类型
- * 本接口默认返回null
- * @param beanClass the class of the bean to be instantiated
- * @param beanName the name of the bean
- * @return the bean object to expose instead of a default instance of the target bean,
- * or {@code null} to proceed with default instantiation
- * @throws org.springframework.beans.BeansException in case of errors
- * @see #postProcessAfterInstantiation
- * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getBeanClass()
- * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getFactoryMethodName()
- */
- @Nullable
- default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
- return null;
- }
- /**
- * Perform operations after the bean has been instantiated, via a constructor or factory method,
- * but before Spring property population (from explicit properties or autowiring) occurs.
- * <p>This is the ideal callback for performing custom field injection on the given bean
- * instance, right before Spring's autowiring kicks in.
- * <p>The default implementation returns {@code true}.
- *
- * <p>在bean通过构造器或工厂方法实例化后执行操作,但是在Spring属性注入之前发生
- * 这对于对给定bean实例执行自定义的字段注入是一个理想的回调,在Spring自动注入开始之前执行
- * @param bean the bean instance created, with properties not having been set yet
- * @param beanName the name of the bean
- * @return {@code true} if properties should be set on the bean; {@code false}
- * if property population should be skipped. Normal implementations should return {@code true}.
- * Returning {@code false} will also prevent any subsequent InstantiationAwareBeanPostProcessor
- * instances being invoked on this bean instance.
- * 如果应该由Spring将属性设置给bean,则返回true;如果返回false,Spring的属性注入将会被跳过,通常的实现是返回true
- * 返回false,将会导致该bean避免对接下来任何的InstantiationAwareBeanPostProcessor的调用
- * @throws org.springframework.beans.BeansException in case of errors
- * @see #postProcessBeforeInstantiation
- */
- default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
- return true;
- }
- /**
- * Post-process the given property values before the factory applies them
- * to the given bean, without any need for property descriptors.
- * <p>Implementations should return {@code null} (the default) if they provide a custom
- * {@link #postProcessPropertyValues} implementation, and {@code pvs} otherwise.
- * In a future version of this interface (with {@link #postProcessPropertyValues} removed),
- * the default implementation will return the given {@code pvs} as-is directly.
- *
- * <p>在工厂应用给定的属性值到给定的bean之前,后置处理给定的属性值,不需要任何属性描述器
- * 该方法的实现默认返回null
- * 以后的版本将会移除本接口的postProcessPropertyValues方法,默认的实现是将pvs原样返回
- * @param pvs the property values that the factory is about to apply (never {@code null})
- * 工厂即将应用的属性值
- * @param bean the bean instance created, but whose properties have not yet been set
- * bean实例已经被创建,只是还没有设置属性
- * @param beanName the name of the bean
- * @return the actual property values to apply to the given bean (can be the passed-in
- * PropertyValues instance), or {@code null} which proceeds with the existing properties
- * but specifically continues with a call to {@link #postProcessPropertyValues}
- * (requiring initialized {@code PropertyDescriptor}s for the current bean class)
- * 实际应用到给定bean的属性
- * @throws org.springframework.beans.BeansException in case of errors
- * @since 5.1
- * @see #postProcessPropertyValues
- */
- @Nullable
- default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
- throws BeansException {
- return null;
- }
- /**
- * Post-process the given property values before the factory applies them
- * to the given bean. Allows for checking whether all dependencies have been
- * satisfied, for example based on a "Required" annotation on bean property setters.
- * <p>Also allows for replacing the property values to apply, typically through
- * creating a new MutablePropertyValues instance based on the original PropertyValues,
- * adding or removing specific values.
- * <p>方法已过时,不再分析
- * <p>The default implementation returns the given {@code pvs} as-is.
- * @param pvs the property values that the factory is about to apply (never {@code null})
- * @param pds the relevant property descriptors for the target bean (with ignored
- * dependency types - which the factory handles specifically - already filtered out)
- * @param bean the bean instance created, but whose properties have not yet been set
- * @param beanName the name of the bean
- * @return the actual property values to apply to the given bean (can be the passed-in
- * PropertyValues instance), or {@code null} to skip property population
- * @throws org.springframework.beans.BeansException in case of errors
- * @see #postProcessProperties
- * @see org.springframework.beans.MutablePropertyValues
- * @deprecated as of 5.1, in favor of {@link #postProcessProperties(PropertyValues, Object, String)}
- */
- @Deprecated
- @Nullable
- default PropertyValues postProcessPropertyValues(
- PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
- return pvs;
- }
- }
6.BeanFactoryAware:
7.BeanPostProcessor:
8.ApplicationContextAware
- /**
- * Interface to be implemented by any object that wishes to be notified
- * of the {@link ApplicationContext} that it runs in.
- *
- * <p>被那些想要被通知自己运行在哪个ApplicationContext的bean实现的接口
- *
- * <p>Implementing this interface makes sense for example when an object
- * requires access to a set of collaborating beans. Note that configuration
- * via bean references is preferable to implementing this interface just
- * for bean lookup purposes.
- *
- * <p>当一个对象要求访问一组与其协作的bean时,实现这个接口是有意义的.
- * 注意:在只是为了查找bean时,通过bean引用(自动注入)比实现本接口更可取
- *
- * <p>This interface can also be implemented if an object needs access to file
- * resources, i.e. wants to call {@code getResource}, wants to publish
- * an application event, or requires access to the MessageSource. However,
- * it is preferable to implement the more specific {@link ResourceLoaderAware},
- * {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface
- * in such a specific scenario.
- *
- * <p>如果一个对象需要访问文件资源,比如,想要调用getResource,想要发布一个应用
- * 事件,或者要求访问MessageSource,可以通过实现本接口来实现.
- * 然而,在这样一个指定的场景中,实现ResourceLoaderAware、ApplicationEventPublisherAware
- * 或MessageSourceAware接口是更可取的选择
- *
- * <p>Note that file resource dependencies can also be exposed as bean properties
- * of type {@link org.springframework.core.io.Resource}, populated via Strings
- * with automatic type conversion by the bean factory. This removes the need
- * for implementing any callback interface just for the purpose of accessing
- * a specific file resource.
- *
- * <p>注意:文件资源依赖也可以通过Resource类型的bean属性被暴露,Spring使用自动类型转换
- * 将字符串转换为Resource.这样只是为了访问特定的文件资源,就不需要实现任何回调接口.
- *
- * <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a
- * convenience base class for application objects, implementing this interface.
- *
- * <p>ApplicationObjectSupport是一个实现了本接口的一个应用程序对象的方便基类
- *
- * <p>For a list of all bean lifecycle methods, see the
- * {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- * @author Chris Beams
- * @see ResourceLoaderAware
- * @see ApplicationEventPublisherAware
- * @see MessageSourceAware
- * @see org.springframework.context.support.ApplicationObjectSupport
- * @see org.springframework.beans.factory.BeanFactoryAware
- */
- public interface ApplicationContextAware extends Aware {
- /**
- * Set the ApplicationContext that this object runs in.
- * Normally this call will be used to initialize the object.
- * <p>Invoked after population of normal bean properties but before an init callback such
- * as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
- * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
- * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
- * {@link MessageSourceAware}, if applicable.
- *
- * <p>设置此对象运行的ApplicationContext.
- * 通常此调用用来实例化对象,在常规的bean属性填充完成后,初始化方法调用前调用,在ResourceLoaderAware#setResourceLoader
- * ApplicationEventPublisherAware#setApplicationEventPublisher, 和MessageSourceAware方法调用后调用
- *
- * @param applicationContext the ApplicationContext object to be used by this object
- * @throws ApplicationContextException in case of context initialization errors
- * @throws BeansException if thrown by application context methods
- * @see org.springframework.beans.factory.BeanInitializationException
- */
- void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
- }
9.InitializingBean:
10.SmartInstantiationAwareBeanPostProcessor:
4.2 SPI在Spring生命周期的作用
以上就是基于注解的Spring初始化的全部内容了。因个人能力有限,如果有错误之处,还请指出,谢谢!
Spring IOC初始化深度解析的更多相关文章
- spring源码深度解析— IOC 之 默认标签解析(上)
概述 接前两篇文章 spring源码深度解析—Spring的整体架构和环境搭建 和 spring源码深度解析— IOC 之 容器的基本实现 本文主要研究Spring标签的解析,Spring的标签 ...
- spring源码深度解析— IOC 之 开启 bean 的加载
概述 前面我们已经分析了spring对于xml配置文件的解析,将分析的信息组装成 BeanDefinition,并将其保存注册到相应的 BeanDefinitionRegistry 中.至此,Spri ...
- spring源码深度解析— IOC 之 容器的基本实现
概述 上一篇我们搭建完Spring源码阅读环境,spring源码深度解析—Spring的整体架构和环境搭建 这篇我们开始真正的阅读Spring的源码,分析spring的源码之前我们先来简单回顾下spr ...
- spring源码深度解析— IOC 之 默认标签解析(下)
在spring源码深度解析— IOC 之 默认标签解析(上)中我们已经完成了从xml配置文件到BeanDefinition的转换,转换后的实例是GenericBeanDefinition的实例.本文主 ...
- Spring IOC设计原理解析:本文乃学习整理参考而来
Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...
- Spring IoC源码解析之invokeBeanFactoryPostProcessors
一.Bean工厂的后置处理器 Bean工厂的后置处理器:BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)和BeanDefinitionRegistr ...
- Spring IoC源码解析之getBean
一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到 ...
- Spring系列(三):Spring IoC源码解析
一.Spring容器类继承图 二.容器前期准备 IoC源码解析入口: /** * @desc: ioc原理解析 启动 * @author: toby * @date: 2019/7/22 22:20 ...
- Spring IoC 默认标签解析
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...
随机推荐
- Docker安装方法整理
目录 安装准备 在线安装 离线安装 Raspbian便捷脚本安装 卸载 安装准备: 卸载旧版本 较旧版本的Docker被称为docker或docker-engine.如果已安装,请卸载它们: sudo ...
- js实现一个简单的链式操作
如何实现一个链式操作 function person() {} person.prototype = { setname: function(name) { this.name = name retu ...
- .NET项目中实现多工程文件共用的方法
一处开发,多处同步编辑使用,并且发布时各个项目均可独立 一.直接编辑项目工程文件 .csproj 具体实现为:编辑 .csproj 文件,在<ItemGroup>中添加新的 <Con ...
- ubuntu & centos RTL88x2BU 无线网卡驱动(v5.1.7_19806) 安装
前提 大部分情况都是因为当前系统的内核不满足驱动文件的编译条件,可以通过驱动文件中的文档来确定是否要升级内核还是降级内核, 对于升级内核只需要下载指定的内核版本安装即可,降级内核(暂时不清楚是否会 ...
- win10+MinGw+ffmpeg 编译
一.安装MinGw+msys 下载 mingw-get-setup.exe 并安装,安装完成会弹出以下界面. 选中红色框几个选项,点击Installation->Apply Changes 进行 ...
- Java实现多态的机制是什么?
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型 ...
- CSPS模拟 56
前十基本都A题了,只有$Dybala$.$Naito$和弱比$yxs$没有A题 $T1 Merchant$ 明明学过$nth element$但是由于不懂原理导致我用了个鬼畜的${U队}$来维护前$K ...
- 无聊的 邮递员 插头dp
邮递员想知道,如果他每天都用不同路线走过10×20个点阵邮筒,他必须活过多少个世纪才能走遍所有方案? 7:00 改完T1,开始肝插头dp 7:10 放弃,颓博客 7:20 学习插头dp 7:21 放弃 ...
- csps60爆零记
整场考试心态是崩的,T1水题打了半天表,将近两个小时才A掉. T2数据结构题想麻烦了,码了5.1K(数据结构选手) 等到最后一刻我发现T3水题,生无可恋.jpg 然后吃屎地在暴力中输出了下标,(%%% ...
- vue自定义长按指令
1.前言 在word中,当我们需要删除一大段文本的时候,我们按一下键盘上的退格键,就会删除一个字,当我们长按住退格键时,就会连续不停的删除,这就是键盘按键的长按功能.那么我们也想在网页中让一个按钮也具 ...