《Spring系列》第5章 refresh()
前言
Spring
框架中最重要的肯定是IOC容器,那么其如何进行初始化,就是通过refresh()
这个方法,无论是单独使用Spring框架,还是SpringBoot
,最终都会通过执行到这个方法,那么下面会介绍一下这个方法
一、IOC容器初始化过程
对于单独测试Spirng框架的场景,有两个常见的高级容器 AnnotationConfigApplicationContext
和 ClassPathXmlApplicationContext
,在这两个类的构造方法,都会使用到refresh()
来刷新容器
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext
会通过扫描配置类来创建IOC容器
@Testpublic void m1() {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectConfig.class);MathCalculator mathCalculator = context.getBean(MathCalculator.class);int ans = mathCalculator.div(10, 5);System.out.println("结果:" + ans);}public AnnotationConfigApplicationContext(Class<?>... componentClasses) {// 调用无参构造方法this();// 注册新的组件register(componentClasses);// 刷新容器refresh();}
构造方法
public AnnotationConfigApplicationContext() {StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");// 创建注解Readerthis.reader = new AnnotatedBeanDefinitionReader(this);createAnnotatedBeanDefReader.end();// 创建类扫描this.scanner = new ClassPathBeanDefinitionScanner(this);
}
创建的AnnotatedBeanDefinitionReader
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {this(registry, getOrCreateEnvironment(registry));
}public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");Assert.notNull(environment, "Environment must not be null");this.registry = registry;this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);// 这里最后一行很关键,注册了一些核心的BeanPostProcessorAnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
下面的方法就是容器启动时初始化,注册一些基础使用的类,其中有一个需要着重看一眼的就是负责处理@Configuration
的后置处理器ConfigurationClassPostProcessor
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {registerAnnotationConfigProcessors(registry, null);
}public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);// 这里是最重要的,就是注册用来处理@Configuration注解的后置处理器if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}// 省略了一些代码return beanDefs;
}
ClassPathXmlApplicationContext
加载bean的xml文件,底层也是通过refresh()
方式
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();}
}
二、refresh()
方法简介
refresh()
为ConfigurableApplicationContext
中的接口,子类AbstractApplicationContext
重写了该方法,负责初始化IOC容器
该方法中总共包含12个方法,大体的工作内容介绍:
- 刷新前的准备工作
- 获取Bean工厂
- 工厂前期的准备
- 子类重写方法,可以对Bean工厂进行扩展
- 执行BeanFactory的后置处理器
- 注册Bean的后置处理器
- 初始化消息源,负责国际化
- 初始化上下文事件
- 子类重写方法,初始化其它特殊bean
- 注册监听器
- 工厂基本创建完毕,剩余的收尾工作
- 刷新完成,剩余的收尾工作
@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// Prepare this context for refreshing. // <1> 准备预处理:记录容器的启动时间startupDate, 标记容器为激活,初始化上下文环境如文件路径信息,验证必填属性是否填写prepareRefresh();// <2> 告诉子类去刷新bean工厂,此方法解析配置文件并将bean信息存储到beanDefinition中,注册到BeanFactory//(但是未被初始化,仅将信息写到了beanDefination的map中)**重点方法,下面的操作都基于这个beanFactory进行的ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.// <3> 设置beanFactory的基本属性:类加载器,添加多个beanPostProcesserprepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.// <4> 扩展机制: 允许上下文子类中对bean工厂进行后处理postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");/**************************以上是BeanFactory的创建及预准备工作 ****************/// Invoke factory processors registered as beans in the context.// <5> 调用BeanFactoryPostProcessor各个实现类的方法 invokeBeanFactoryPostProcessors(beanFactory);// <6> 注册 BeanPostProcessor 的实现类registerBeanPostProcessors(beanFactory);beanPostProcess.end();// <7> 初始化ApplicationContext的MessageSource组件(资源文件),如国际化文件,消息解析,绑定等initMessageSource();// <8> 初始化ApplicationContext事件广播器initApplicationEventMulticaster();// <9> 在特定上下文子类中初始化其他特殊beanonRefresh();// <10> 获取所有的事件监听器,并将监听器注册到事件广播器registerListeners();// <11> 初始化所有不是懒加载的singleton beanfinishBeanFactoryInitialization(beanFactory);// <12> 广播事件:ApplicationContext初始化完成finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// 重置 Spring 核心中的公共自省缓存,因为我们可能不再需要单例 bean 的元数据......// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();contextRefresh.end();}}}
<1> prepareRefresh() 刷新准备
1、prepareRefresh()刷新前的预处理;1)、initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置方法;2)、getEnvironment().validateRequiredProperties();检验属性的合法等3)、earlyApplicationEvents= new LinkedHashSet<>();保存容器中的一些早期的事件;
/*** Prepare this context for refreshing, setting its startup date and 容器刷新的准备工作,设置启动时间* active flag as well as performing any initialization of property sources. 以及初始化属性源*/protected void prepareRefresh() {// Switch to active.this.startupDate = System.currentTimeMillis(); // 获取当前时间// 设置关闭状态false 启动状态truethis.closed.set(false);this.active.set(true);// 输出日志if (logger.isDebugEnabled()) {if (logger.isTraceEnabled()) {logger.trace("Refreshing " + this);}else {logger.debug("Refreshing " + getDisplayName());}}// Initialize any placeholder property sources in the context environment.// 初始化一些属性在容器环境中 留给子类重写initPropertySources();// Validate that all properties marked as required are resolvable:// see ConfigurablePropertyResolver#setRequiredProperties// 验证属性的是否合法getEnvironment().validateRequiredProperties();// Store pre-refresh ApplicationListeners...if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);}else {// Reset local application listeners to pre-refresh state.this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}// Allow for the collection of early ApplicationEvents,// to be published once the multicaster is available...// 通过集合保存早期的容器事件// 等派发起好了以后,在派发出去this.earlyApplicationEvents = new LinkedHashSet<>();}
a> initPropertySources()
是个空方法,留给子类重写自定义
/*** <p>Replace any stub property sources with actual instances.* @see org.springframework.core.env.PropertySource.StubPropertySource* @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources*/protected void initPropertySources() {// For subclasses: do nothing by default.}
b> validateRequiredProperties()
验证请求属性,方法很简单,循环遍历,判断是否为Null,存在则直接抛出异常MissingRequiredPropertiesException
,该异常为自定义异常类
public void validateRequiredProperties() {// 创建异常信息MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();Iterator var2 = this.requiredProperties.iterator();// 遍历 找到空的keywhile(var2.hasNext()) {String key = (String)var2.next();if (this.getProperty(key) == null) {ex.addMissingRequiredProperty(key);}}// 存在空的直接抛出异常if (!ex.getMissingRequiredProperties().isEmpty()) {throw ex;}}
<2> obtainFreshBeanFactory() 获取bean工厂
该方法很简单,获取到对应的bean工厂,类型为DefaultListableBeanFactory
,也是Spring中功能最全的IOC容器
/*** Tell the subclass to refresh the internal bean factory.* @return the fresh BeanFactory instance 返回容器示例* @see #refreshBeanFactory()* @see #getBeanFactory()*/protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 刷新容器refreshBeanFactory();// 通过getter方法获取到Bean工厂返回,其实就是默认的DefaultListableBeanFactoryreturn getBeanFactory();}
a> refreshBeanFactory()
在GenericApplicationContext
类初始化的时候,就创建了一个默认的BeanFactory
,然后这里会设置Id
- 创建的beanFactory是
DefaultListableBeanFactory
类型 - 该beanFactory在类初始化的时候就创建了
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {// 构造方法public GenericApplicationContext() {// 这里也代表了DefaultListableBeanFactory是Spring中最核心的类this.beanFactory = new DefaultListableBeanFactory();}/*** Do nothing: We hold a single internal BeanFactory and rely on callers* to register beans through our public methods (or the BeanFactory's).* @see #registerBeanDefinition*/@Overrideprotected final void refreshBeanFactory() throws IllegalStateException {if (!this.refreshed.compareAndSet(false, true)) {throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");}// 这个id就是 org.springframework.context.annotation.AnnotationConfigApplicationContext@5b0abc94this.beanFactory.setSerializationId(getId());} // ...
}
<3> prepareBeanFactory() 准备工厂
3、prepareBeanFactory(beanFactory);BeanFactory的预准备工作(BeanFactory进行一些设置);1)、设置BeanFactory的类加载器、支持表达式解析器...2)、添加部分BeanPostProcessor【ApplicationContextAwareProcessor】3)、设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware、xxx;4)、注册可以解析的自动装配;我们能直接在任何组件中自动注入:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext5)、添加BeanPostProcessor【ApplicationListenerDetector】6)、添加编译时的AspectJ7)、给BeanFactory中注册一些能用的组件;environment【ConfigurableEnvironment】、systemProperties【Map<String, Object>】、systemEnvironment【Map<String, Object>】
/*** Configure the factory's standard context characteristics, 配置工厂启动时上下文特征* such as the context's ClassLoader and post-processors. 例如容器的类加载器和后置处理器* @param beanFactory the BeanFactory to configure*/protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {// Tell the internal bean factory to use the context's class loader etc.// 1.设置Bean的类加载器beanFactory.setBeanClassLoader(getClassLoader());if (!shouldIgnoreSpel) {// 设置支持相关表达式语言的解析器beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));}// 添加属性的编辑注册器beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// Configure the bean factory with context callbacks.// 2.添加部分后置处理器【ApplicationContextAwareProcessor】beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));// 3.设置忽略自动装配的接口beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);// BeanFactory interface not registered as resolvable type in a plain factory.// MessageSource registered (and found for autowiring) as a bean.// 4.注册可以解析的自动装配beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);// Register early post-processor for detecting inner beans as ApplicationListeners.// 5.添加BeanPostProcessor【ApplicationListenerDetector】事件监听beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// Detect a LoadTimeWeaver and prepare for weaving, if found.if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));// Set a temporary ClassLoader for type matching.beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}// Register default environment beans.// 给BeanFactory中注册一些能用的组件if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());}if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());}if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());}if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());}}
<4> postProcessBeanFactory() 子类重写扩展
提供的扩展机制,子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置
/*** Modify the application context's internal bean factory after its standard* initialization. All bean definitions will have been loaded, but no beans* will have been instantiated yet. This allows for registering special* BeanPostProcessors etc in certain ApplicationContext implementations.* @param beanFactory the bean factory used by the application context*/protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}
<5> invokeBeanFactoryPostProcessors()
a> 方法入口
这个方法会继续调用一个包装类中的方法,负责执行后置处理器,其中的参数getBeanFactoryPostProcessors()
会获取后置处理器,这里获取的后置处理器是框架初始化需要的,会被优先执行
但需要注意的是,并不是Spring框架需要的,在实际测试过程中,如果使用SpringBoot
,可以成功获取到,如果单独启动Spring,则啥也没有
/*** Instantiate and invoke all registered BeanFactoryPostProcessor beans,* respecting explicit order if given.* <p>Must be called before singleton instantiation.*/protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {// 执行后置处理器PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}}
上面两张图也说明了问题,如果想在Spring框架基础上封装其它框架,例如经典的SpringBoot
,Spring会先执行SpringBoot
初始化需要的后置处理器,保证核心框架先顺利启动,然后再执行其它依赖和手写的后置处理器
当然侧面证明了Spring
提供的良好的扩展性能,再启动Spring的时候,优先执行二次封装的代码,最后在是其它依赖和自己写的代码
b> 接口介绍
下面方法会用到这2个接口,这里先介绍一下
BeanFactoryPostProcessor
BeanFactoryPostProcessor
的执行时机为容器初始化后,实例化Bean前,Spring允许在这个时候可以对Bean工厂进行修改,一种良好的扩展性能
public interface BeanFactoryPostProcessor {// 通过参数ConfigurableListableBeanFactory可以获取到定义信息void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
使用示例
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("调用MyBeanFactoryPostProcessor的postProcessBeanFactory");BeanDefinition bd = beanFactory.getBeanDefinition("mathCalculator");System.out.println("属性值------>" + bd.getPropertyValues().toString());MutablePropertyValues pv = bd.getPropertyValues();if (pv.contains("remark")) {pv.addPropertyValue("remark", "把备注信息修改一下");}bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);}
}
BeanDefinitionRegistryPostProcessor
- 通过下面的代码,看到它继承了
BeanFactoryPostProcessor
- 它会在所有的bean定义信息还未被加载,将要被加载的时候执行,可以对
BeanDefiniton
进行增删改查
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.* @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;
}
c> 方法分析
- 首先参数传递过来的后置处理器,该参数上面已经介绍了,这些是框架启动需要的,例如
SpringBoot
,如果是单独的Spring
启动则为空集合 - 判断是否实现了
BeanDefinitionRegistry
该接口,只有IOC容器实现了该接口,才代表允许其它代码操作BeanDefinition
,看成一个开关入口 - 执行第1步:首先执行
SpringBoot
框架需要的BeanDefinitionRegistryPostProcessor
,需要先把框架处理完毕 - 执行第2步:执行其它依赖和开发人员写的
BeanDefinitionRegistryPostProcessor
- 优先执行实现了
PriorityOrdered
接口的 - 次之执行实现了
Ordered
接口的 - 执行剩余的后置处理器
- 优先执行实现了
- 接下来会先执行一批
BeanFactoryPostProcessor
,上面也介绍了这2个接口是继承关系,所以重写子类方法必须也带上父类方法,上面执行的BeanDefinitionRegistryPostProcessor
后置处理器,肯定也有BeanFactoryPostProcessor
的方法,这里需要执行,这里启动就是2部分- 先执行上面这些实现了
BeanDefinitionRegistryPostProcessor
接口,里面肯定重写了父类BeanFactoryPostProcessor
,需要先执行 - 参数传递过来的
BeanFactoryPostProcessor
,这些也需要先执行
- 先执行上面这些实现了
- 到此上面已经把
BeanDefinitionRegistryPostProcessor
处理完毕,接下来就是BeanFactoryPostProcessor
,这里会和上面一样,先处理PriorityOrdered
,次之Ordered
,最后剩余的 - 总结:代码非常长,理解了很简答,总体思想就是先执行框架启动需要的,然后在执行自己写的,先执行
BeanDefinitionRegistryPostProcessor
,后BeanFactoryPostProcessor
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {// Invoke BeanDefinitionRegistryPostProcessors first, if any.// 执行BeanDefinitionRegistryPostProcessors的实现类【首先调用】Set<String> processedBeans = new HashSet<>();// 实现该接口才允许操作BeanDefinition,当然DefaultListableBeanFactory肯定实现了if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();// <1> 处理参数传递过来的后置处理器,这些是Spring启动需要的// 按照实现接口分类存进去, 并且优先执行实现了BeanDefinitionRegistryPostProcessor的后置方法,系统类的先执行for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;registryProcessor.postProcessBeanDefinitionRegistry(registry);registryProcessors.add(registryProcessor);}else {regularPostProcessors.add(postProcessor);}}// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let the bean factory post-processors apply to them!// Separate between BeanDefinitionRegistryPostProcessors that implement// PriorityOrdered, Ordered, and the rest.// 定义一个变量,用于后续分批执行代码List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.// 第一步 执行实现了 PriorityOrdered 优先级排序接口的BeanDefinitionRegistryPostProcessors子类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, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.// 第二步 执行实现了Ordered顺序接口的BeanDefinitionRegistryPostProcessorspostProcessorNames = 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, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.// 最后 执行剩余的BeanDefinitionRegistryPostProcessorsboolean 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, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();}// Now, invoke the postProcessBeanFactory callback of all processors handled so far.// 调用到目前为止处理的所有处理器的 postProcessBeanFactory() 回调// 这里会有一个疑问: postProcessBeanFactory()不是下面很多行代码负责执行吗?咋这里就执行了// 首先 registryProcessors是上面实现了子类接口,那么父类肯定也给重写,那么这里先调用// 其次 regularPostProcessors到目前位置有数据,肯定是框架启动需要的后置处理器,也要先执行invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);}else {// Invoke factory processors registered with the context instance.// 否则执行框架启动需要的后置处理器invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);}// 获取所有的 BeanFactoryPostProcessor 后置处理器String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,// Ordered, and the rest.List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<String> orderedPostProcessorNames = new ArrayList<>();List<String> nonOrderedPostProcessorNames = new ArrayList<>();for (String ppName : postProcessorNames) {if (processedBeans.contains(ppName)) {// skip - already processed in first phase above}else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.// 第一步 执行实现 PriorityOrdered 接口的 BeanFactoryPostProcessorssortPostProcessors(priorityOrderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);// Next, invoke the BeanFactoryPostProcessors that implement Ordered.// 第二步 执行实现 Ordered 接口的 BeanFactoryPostProcessorsList<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String postProcessorName : orderedPostProcessorNames) {orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}sortPostProcessors(orderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);// Finally, invoke all other BeanFactoryPostProcessors.// 第三步 执行剩余的List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String postProcessorName : nonOrderedPostProcessorNames) {nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);// Clear cached merged bean definitions since the post-processors might have// modified the original metadata, e.g. replacing placeholders in values...beanFactory.clearMetadataCache();}
d> 重要Debug
如下截图所示,Spring
框架负责解析@Configuration
配置类的是后置处理器ConfigurationClassPostProcessor
,它会负责扫描各种类,把bean包装成BeanDefinition
存入IOC容器,具体后续会介绍ConfigurationClassPostProcessor
<6> registerBeanPostProcessors()
顾名思义,注册Bean后置处理器,它负责拦截Bean的创建,这样我们就可以通过该拦截对Bean添加自定义逻辑,例如Aop功能,就是通过注解添加了一个后置处理器,然后在创建Bean的时候,对其进行代理,达到增强的目的
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
- 获取所有的
BeanPostProcessor
后置处理器 - 先注册
PriorityOrdered
优先级接口的BeanPostProcessor
- 再注册
Ordered
接口的 - 最后注册没有实现任何优先级接口的
- 最终注册
MergedBeanDefinitionPostProcessor
- 注册一个
ApplicationListenerDetector
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {// 1.获取所有实现 BeanPostProcessor 接口的后置处理器String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);// 2.记录数量 = 框架中已经存在的 + 稍后会注入的数量 + 下一行代码注入的1个后置处理器int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;// 新注册的处理器 BeanPostProcessorChecker 主要用于记录信息beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));// Separate between BeanPostProcessors that implement PriorityOrdered,// Ordered, and the rest.// 3.分类存储// 实现PriorityOrdered接口的List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();// 实现Ordered接口的name集合List<String> orderedPostProcessorNames = new ArrayList<>();// 定义普通的name集合List<String> nonOrderedPostProcessorNames = new ArrayList<>();for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);priorityOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}// 4.先注册实现 PriorityOrdered 接口的// First, register the BeanPostProcessors that implement PriorityOrdered.sortPostProcessors(priorityOrderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);// 5.次之注册实现 Ordered 接口的// Next, register the BeanPostProcessors that implement Ordered.List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String ppName : orderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);orderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}sortPostProcessors(orderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, orderedPostProcessors);// 5.最后注册普通的后置处理器// Now, register all regular BeanPostProcessors.List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String ppName : nonOrderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);nonOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);// 6.注册实现了 MergedBeanDefinitionPostProcessor 接口的// Finally, re-register all internal BeanPostProcessors.sortPostProcessors(internalPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, internalPostProcessors);// Re-register post-processor for detecting inner beans as ApplicationListeners,// moving it to the end of the processor chain (for picking up proxies etc).// 7.重新注册用于将内部 bean 检测为 ApplicationListeners 的后处理器,将其移动到处理器链的末尾(用于获取代理等)beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
<7> initMessageSource() 国际化
MessageSource
是负责国际化功能的,下面的代码大体很简单,如果已经存在国际化组件,就一顿操作处理,没有就注册个新的
那么想一下,之前怎么可能会有该bean?那肯定是开发人员自定义的国际化组件,如果存在则在第<5>步的时候已经解析完毕,执行到该方法,肯定就可以获取到
那么在把下面这个方法概述一下:如果开发人员自定义了国际化组件,那就用自己写的,按照固定的格式处理一下,否则就用默认的国际化组件
这同样体现了Spring框架良好的扩展机制,后续会编写单独的文章介绍MessageSource
protected void initMessageSource() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();// 如果存在该bean,则代表开发人员自定义了国家化组件if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);// Make MessageSource aware of parent MessageSource.if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;if (hms.getParentMessageSource() == null) {// Only set parent context as parent MessageSource if no parent MessageSource// registered already.hms.setParentMessageSource(getInternalParentMessageSource());}}if (logger.isTraceEnabled()) {logger.trace("Using MessageSource [" + this.messageSource + "]");}}else { // 否则使用默认的国际化组件// Use empty MessageSource to be able to accept getMessage calls.DelegatingMessageSource dms = new DelegatingMessageSource();dms.setParentMessageSource(getInternalParentMessageSource());this.messageSource = dms;beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);if (logger.isTraceEnabled()) {logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");}}
}
<8> initApplicationEventMulticaster()
下面这个方法大体逻辑和第<7>步一致,如果存在该bean,则代表开发人员自定义了该功能,则处理一下,否则注册一个默认的
ApplicationEventMulticaster
是Spring中事件广播器接口,负责事件的广播发布。
通过名字可以看出它就是一个广播站,可以把监听器注册到我这里来,有事件通过我进行发布。也可以理解为MQ种的发布-订阅
模式,监听器在这里进行订阅,有消息通过我进行发布
protected void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);if (logger.isTraceEnabled()) {logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");}}else {this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);if (logger.isTraceEnabled()) {logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}}
}
ApplicationEventMulticaster
上面看懂了该组件的功能,这里看接口就好理解了,负责注册监听器的,负责发布事件的
public interface ApplicationEventMulticaster {// 注册监听器void addApplicationListener(ApplicationListener<?> listener);void addApplicationListenerBean(String listenerBeanName);// 移除监听器void removeApplicationListener(ApplicationListener<?> listener);void removeApplicationListenerBean(String listenerBeanName);void removeAllListeners();// 发布事件void multicastEvent(ApplicationEvent event);void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);}
<9> onRefresh() 子类重写扩展
在特定上下文子类中初始化其他特殊bean
protected void onRefresh() throws BeansException {// For subclasses: do nothing by default.
}
<10> registerListeners()
在上面第<8>步,注册了一个事件广播器
,该方法负责把监听器注册到里面
protected void registerListeners() {// 1.向事件分发器注册监听器,这些是通过配置文件读取的// Register statically specified listeners first.for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);}// 2.向事件分发器注册外部加入的监听器,但不实例化// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let post-processors apply to them!String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);for (String listenerBeanName : listenerBeanNames) {getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);}// 3.发布早期的事件// Publish early application events now that we finally have a multicaster...Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;this.earlyApplicationEvents = null;if (earlyEventsToProcess != null) {for (ApplicationEvent earlyEvent : earlyEventsToProcess) {getApplicationEventMulticaster().multicastEvent(earlyEvent);}}
}
<11> finishBeanFactoryInitialization()
通过名字可以看出是完成BeanFactory
的初始化,一个收尾方法,相当于一个bean工厂大部分已经完成了,还剩余零零散散的辅助bean需要依次注入
在这个方法中需要关注的是最后1行,这里会实例化未设置懒加载的单例bean
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// 1.为此上下文初始化转换服务// Initialize conversion service for this context.if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));}// Register a default embedded value resolver if no bean post-processor// (such as a PropertyPlaceholderConfigurer bean) registered any before:// at this point, primarily for resolution in annotation attribute values.// 2.如果之前没有注册任何 bean 后处理器(例如 PropertyPlaceholderConfigurer bean),则注册一个默认的嵌入值解析器:此时,主要用于注释属性值的解析if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));}// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.// 3.通过 getBean() 尽早的注册LoadTimeWeaverAwareString[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);for (String weaverAwareName : weaverAwareNames) {getBean(weaverAwareName);}// Stop using the temporary ClassLoader for type matching. // 4.停止使用临时类加载器进行类型匹配beanFactory.setTempClassLoader(null);// Allow for caching all bean definition metadata, not expecting further changes. // 5.允许缓存所有 bean 定义元数据,而不是期望进一步的更改beanFactory.freezeConfiguration();// 6.Instantiate all remaining (non-lazy-init) singletons.// 初始化未懒加载的单例beanbeanFactory.preInstantiateSingletons();
}
a> preInstantiateSingletons
子类DefaultListableBeanFactory
重写了该方法
功能:循环所有的Bean名称,遍历判断,然后实例化,最后调用回调
// DefaultListableBeanFactory @Overridepublic void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.// 1.获取全部的BeanNameList<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...// 2.实例化所有非懒加载的单实例Beanfor (String beanName : beanNames) RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// 非抽象 && 单实例 && 非懒加载if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {// 类型判断是否为FactoryBean, FactoryBean在后续章节也有讲解if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else {// 普通的Bean在这里直接获取getBean(beanName);}}}// Trigger post-initialization callback for all applicable beans...// 触发所有适用 bean 的初始化后回调for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}}}}
<12> finishRefresh()
上面已经把BeanFactory
初始化完毕,那么这个IOC容器就接近尾声了,接下来就是一些后续工作
protected void finishRefresh() {// Clear context-level resource caches (such as ASM metadata from scanning).// 清除上下文级资源缓存clearResourceCaches();// Initialize lifecycle processor for this context.// 为此上下文初始化生命周期处理器initLifecycleProcessor();// Propagate refresh to lifecycle processor first.getLifecycleProcessor().onRefresh();// Publish the final event.publishEvent(new ContextRefreshedEvent(this));// Participate in LiveBeansView MBean, if active.LiveBeansView.registerApplicationContext(this);
}
ingleton) {
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
## <12> finishRefresh()上面已经把`BeanFactory`初始化完毕,那么这个IOC容器就接近尾声了,接下来就是一些后续工作```java
protected void finishRefresh() {// Clear context-level resource caches (such as ASM metadata from scanning).// 清除上下文级资源缓存clearResourceCaches();// Initialize lifecycle processor for this context.// 为此上下文初始化生命周期处理器initLifecycleProcessor();// Propagate refresh to lifecycle processor first.getLifecycleProcessor().onRefresh();// Publish the final event.publishEvent(new ContextRefreshedEvent(this));// Participate in LiveBeansView MBean, if active.LiveBeansView.registerApplicationContext(this);
}
相关文章:

《Spring系列》第5章 refresh()
前言 Spring框架中最重要的肯定是IOC容器,那么其如何进行初始化,就是通过refresh()这个方法,无论是单独使用Spring框架,还是SpringBoot,最终都会通过执行到这个方法,那么下面会介绍一下这个方法 一、IOC容…...

ThreeJS-缩放、旋转(四)
代码: <template> <div id"three_div"> </div> </template> <script> import * as THREE from "three"; import {OrbitControls } from three/examples/jsm/controls/OrbitControls export default { name: &quo…...

数据更新 | CnOpenData法拍房数据
法拍房数据 一、数据简介 法拍房,即“法院拍卖房产”,是被法院强制执行拍卖的房屋 。当债务人(业主)无力履行借款合约或无法清偿债务时,而被债权人经司法程序向法院申请强制执行,将债务人名下房屋拍卖&…...

【Spring从成神到升仙系列 五】从根上剖析 Spring 循环依赖
👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主📕系列专栏:Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙…...

设计模式之代理模式(C++)
作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 一、代理模式是什么? 代理模式是一种结构型的软件设计模式,在不改变原代码前提下,提供一个代理…...

c++11 标准模板(STL)(std::unordered_multimap)(三)
定义于头文件 <unordered_map> template< class Key, class T, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator< std::pair<const Key, T> > > class unordered…...

Linux进程控制-2
紧接着上篇博客出发,我们接着来讲述Linux中进程控制的内容。 目录 1.等待 1.1具体操作 1.等待 进程等待主要的作用在于:父进程创建子进程之后,等待子进程退出,获取子进程的退出码,释放子进程的资源,避…...

快速排序算法
一:快速排序思想 假设我们现在对“6 1 2 7 9 3 4 5 10 8”这个10个数进行排序。首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,就是一个用来参照的数,待会你就知道它用来做啥的了)。为了方便ÿ…...

中华好诗词大学季第二季(四)
第七期 1,二十四友一朝尽,爱妾坠楼何足言出自许浑的《金谷园》,“爱妾”指的是谁 2,李白在《九月十日即事》借菊花表达自己的惋惜之情,请问九月十日是什么节日 A 后登高 B 菊花节 C 小重阳 3,贾宝玉在大观园里面题了“曲径通幽”…...

分布式系统容灾部署方案
本文主要以OceanBase部署来说明分布式系统容灾部署方案 分布式系统提供持续可用的服务尤为重要。 好的分布式系统根据需求提供不同等级的的高可用与容灾级别。 而在分布式系统中,数据库系统又是最核心最关键的系统。 我们以数据库分布式系统为主,考虑…...

Python 爬虫性能相关总结
这里我们通过请求网页例子来一步步理解爬虫性能 当我们有一个列表存放了一些url需要我们获取相关数据,我们首先想到的是循环 简单的循环串行 这一种方法相对来说是最慢的,因为一个一个循环,耗时是最长的,是所有的时间总和 代码…...

Baumer工业相机堡盟工业相机如何设置网口的IP地址(工业相机连接的网口设置IP地址步骤)
Baumer工业相机堡盟工业相机如何设置网口的IP地址(工业相机连接的网口设置IP地址步骤)Baumer工业相机Baumer工业相机设置网络端口IP地址匹配设置网络端口IP地址和工业相机IP地址匹配第一次打开CameraExplorer软件确认问题为IP地址不匹配问题打开网络连接…...

Android MediaCodec设置H264 Profile到High
H264 High Profile压缩率高,能降低码率,这里记录下MediaCodec Profile设置到High遇到的一些问题。 Android 4.1 就引入了MediaCodecInfo.CodecProfileLevel类,下面截取H264(AVC)的Profile和Level定义: /** Copyright (C) 2012 The Android O…...

QT之QSysInfo(查看电脑信息)
文章目录前言一、API使用总结前言 QSysInfo是Qt中用于获取有关运行应用程序的系统信息的类。 我们可以获取以下信息: 返回系统产品类型,如ios,windows,Linux等 返回当前系统的产品版本。 返回当前系统的内核类型。 返回当前系统的…...

中国塑料编织袋产业竞争状况及投资前景预测报告2023-2029年
中国塑料编织袋产业竞争状况及投资前景预测报告2023-2029年 KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK 《报告编号》: BG451639 《出版时间》: 2023年4月 《出版机构》: 中智正业研究院 免费售后 服务一年,具体内容及订购流程欢迎咨询客服人员 内容简介&…...

从头用脚分析FFmpeg源码 - av_read_frame
av_read_frame作用 /*** Return the next frame of a stream.* This function returns what is stored in the file, and does not validate* that what is there are valid frames for the decoder. It will split what is* stored in the file into frames and return one f…...

第17章_触发器
第17章_触发器 🏠个人主页:shark-Gao 🧑个人简介:大家好,我是shark-Gao,一个想要与大家共同进步的男人😉😉 🎉目前状况:23届毕业生,目前在某公…...

3956. 截断数组
3956. 截断数组 - AcWing题库 3956. 截断数组 【题目描述】 给定一个长度为 nn 的数组 a1,a2,…,ana1,a2,…,an。 现在,要将该数组从中间截断,得到三个非空子数组。 要求,三个子数组内各元素之和都相等。 请问,共有多少种不同…...

React Labs: 我们最近在做什么——2023 年 3 月
原文:https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023 React Server Components React Server Components(下文简称 RSC) 是由 React 团队设计的新应用程序架构。 我们首先在一个介绍性演讲和一个RFC中分享了我们对 RSC 的…...

文件系统设计详解
抽象的文件系统以目录的形式来组织文件,我们可以利用该文件系统来读取某个文件的内容,也可以对目录或者文件实施监控并及时获取变化的通知。 IChangeToken IChangeToken对象就是一个与某组监控数据相关联的“令牌”(Token)&#x…...

好看~立马启动python实现美女通通下
人生苦短,我用python一、环境版本使用二、代码实现思路三、代码展示:导入模块伪装(请求头)四、部分好看截图,更多的就自己去采集噜~吃饭放松的时候哇一不小心看见了很多好看的东西 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 独乐乐不如众乐乐…...

Git 安装设置
1、安装 安装以下三个软件: Git-2.13.3-64-bit.exe TortoiseGit-2.4.0.2-64bit.msi TortoiseGit-LanguagePack-2.4.0.0-64bit-zh_CN.msi 安装过程中不用填写、不用选择,全部点"下一步",完成后需要重启机器。 2、基本设…...

Python-闭包
介绍 Python的闭包是一种高级的编程技巧,它可以在函数内部定义另一个函数,并返回该函数的引用。这个内部函数可以访问外部函数的变量和参数,即使外部函数已经执行完毕 好处 1)闭包可以避免全局变量的污染,使得代码更…...

Gitlab中Pipeline语法四
Gitlab中Pipeline语法 cache cache:paths 在job build中定义缓存,将会缓存target目录下的所有*.jar文件当全局定义了cache:paths会被job中覆盖.以下实例将缓存target目录 buld:script: buildcache:paths:- target/*.jar#设置key可以解决cache被覆盖 cache:paths:- my/files…...

Go语言精修(尚硅谷笔记)第五章
五、程序流程控制 5.1 单分支控制 package main import ("fmt" )func main() {//请大家看个案例[ifDemo.go]://编写一个程序,可以输入人的年龄,如果该同志的年龄大于18岁,则输出 "你年龄大//于18,要对自己的行为负责!"//分析 //1.年龄 > var age int…...

三、MySQL 高级(DML 增删改)
三、MySQL 高级(DML 增删改) 3.1 DML 数据操纵语言 DML(Data Manipulation Language)DML对数据库中表记录的执行操作 插入(INSERT) 插入单行数据 插入多行数据 将查询结果插入到新表 更新(…...

面向AI编程的本质是什么?
面向AI编程的本质是什么? 面向AI编程的本质是编程的第五代编程语言,与自然语言非常相似,但是是有区别的。 因此出现了针对与AI通话的提示工程。 简单地回顾一下编程语言的发展史, 第一代编程语言是机器语言,它直接使…...

深入浅出——深度学习训练中的warmup
❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…...

你知道如何用C语言将格式化数据和字符串相互转换吗?
今天重点介绍2个函数,分别是sprintf和sscanf,用来将格式化数据和字符串相互转换。它们的作用分别是: sprintf函数用于将格式化数据转换成字符串。sscanf函数用于将字符串转换成格式化数据。 接下来是第一个大问题:我怎么记忆呢&…...

免费一键生成原创文章-原创文章批量生成
免费使用一键生成原创文章,轻松解决写作难题! 您是否因为写作枯竭、文章档次不高,而感到烦恼?现在,我们有一个免费的文章创作工具,帮助您无需付出太多的努力就能高效地创造原创文章。 一键生成࿱…...