Spring容器启动流程——refresh()单个方法分析

news2024/11/16 15:36:34

文章目录

  • Spring启动过程
    • this()方法
    • refresh()
      • prepareRefresh()
      • obtainFreshBeanFactory()
      • prepareBeanFactory()
      • postProcessBeanFactory()
      • invokeBeanFactoryPostProcessors
      • registerBeanPostProcessors
      • initMessageSource()
      • initApplicationEventMulticaster()
      • onRefresh()
      • registerListeners()
      • finishBeanFactoryInitialization
      • finishRefresh()



Spring启动过程

Spring启动过程 在线流程图

在这里插入图片描述


运行下面代码启动Spring的时候,会经过下面的一些步骤:

创建一个BeanFactory --> 解析指定的配置类 —> 进行包扫描 —> 生成BeanDefinition --> BeanDefinitionMap、BeanpostProcessor、单例池

public static void main(String[] args) {

    // 创建一个Spring容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

    UserService userService = (UserService) context.getBean("userService");
    userService.test();
}



创建一个DefaultListableBeanFactory,在创建AnnotationConfigApplicationContext这个类时,会先初始化它的父类,在父类的构造函数中会创建一个DefaultListableBeanFactory对象,并赋值给BeanFactory属性

// GenericApplicationContext就是AnnotationConfigApplicationContext的父类
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

	private final DefaultListableBeanFactory beanFactory;

	......


	/**
	 * beanDefinitionMap、beanDefinitionNames等等常见的集合都是在DefaultListableBeanFactory这个类中的
	 * Create a new GenericApplicationContext.
	 */
	public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
	}
    
    ......
}



this()方法

这里主要就是给BeanFactory设置一些内容

接下来回到创建AnnotationConfigApplicationContext类的构造方法中

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    // 构造DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner
    this();
    // 通过上一步创建的reader,把将参数传递过来的componentClasses生成BeanDefinition,并存入BeanDefinitionMap中
    register(componentClasses);
    refresh();
}


// 上面this()方法会到这里来
public AnnotationConfigApplicationContext() {
		StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
		// 重点就是会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
		this.reader = new AnnotatedBeanDefinitionReader(this);
		createAnnotatedBeanDefReader.end();
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}



在创建AnnotatedBeanDefinitionReader类的构造方法中,最终会调用到AnnotationConfigUtils.registerAnnotationConfigProcessors()方法中,在该方法中就会往BeanFactory中注册很多常见的BeanPostProcessor。比如判断一个类能不能进行依赖注入、专门解析配置类的、@Autowired和@Resource注解相关的、事件相关的

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    BeanDefinitionRegistry registry, @Nullable Object source) {

    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {

        // 设置beanFactory的OrderComparator为AnnotationAwareOrderComparator
        // 它是一个Comparator,是一个比较器,可以用来进行排序,比如new ArrayList<>().sort(Comparator);
        if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
            beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
        }
        // 用来判断某个Bean能不能用来进行依赖注入
        if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        }
    }

    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

    // 注册ConfigurationClassPostProcessor类型的BeanDefinition
    // 它就是用来解析配置类的
    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));
    }

    // 注册AutowiredAnnotationBeanPostProcessor类型的BeanDefinition
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注册CommonAnnotationBeanPostProcessor类型的BeanDefinition
    // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注册PersistenceAnnotationBeanPostProcessor类型的BeanDefinition
    // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition();
        try {
            def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                                                AnnotationConfigUtils.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
        }
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注册EventListenerMethodProcessor类型的BeanDefinition,用来处理@EventListener注解的
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }

    // 注册DefaultEventListenerFactory类型的BeanDefinition,用来处理@EventListener注解的
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}



接下来是创建ClassPathBeanDefinitionScanner扫描器会进行的一些操作,这里就只是对@Component注解进行扫描的功能,在ClassPathScanningCandidateComponentProvider.registerDefaultFilters()方法中

protected void registerDefaultFilters() {

    // 注册@Component对应的AnnotationTypeFilter
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));

    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();

    try {
        this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }

    try {
        this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}



refresh()

上面只是给BeanFactory设置了一些内容,但肯定是还没有设置完的。接下来的重点就是在refresh()方法中

接下来方便写笔记,我们把AnnotationConfigApplicationContext成为Spring容器,把AnnotationConfigWebApplicationContext成为SPringMVC容器

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    // 构造DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner
    this();
    // 通过上一步创建的reader,把将参数传递过来的componentClasses生成BeanDefinition,并存入BeanDefinitionMap中
    register(componentClasses);
    refresh();
}
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // Prepare this context for refreshing.
        // 往Environment中添加一些环境变量,并检查是否有必须的环境变量
        // 在Spring容器中没有重写该方法,所以执行的是一个空方法,而在Springmvc容器中重写了该方法,会把ServletContext中的参数对设置到Environment
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        // 这里会判断能否刷新,并且返回一个BeanFactory, 刷新不代表完全情况,主要是先执行Bean的销毁,
        // 然后重新生成一个BeanFactory,再在接下来的步骤中重新去扫描等等
        // 我们这里使用Spring容器的父类的该方法的实现中,它不允许我们重复刷新,SpringMVC的那个类允许重复刷新,重复刷新就是调用refresh()方法
        // Spring容器这里其实就是把上一步构造方法中创建的BeanFactory对象直接返回
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        // 准备BeanFactory
        // 1. 设置BeanFactory的类加载器、SpringEL表达式解析器、类型转化注册器
        // 2. 添加三个BeanPostProcessor,注意是具体的BeanPostProcessor实例对象
        // 3. 记录ignoreDependencyInterface
        // 4. 记录ResolvableDependency
        // 5. 添加三个单例Bean
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            // 子类来设置一下BeanFactory
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");

            // Invoke factory processors registered as beans in the context.
            // BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理
            // 默认情况下:
            // 此时beanFactory的beanDefinitionMap中有6个BeanDefinition,5个基础BeanDefinition+AppConfig的BeanDefinition
            // 而这6个中只有一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor
            // 这里会执行ConfigurationClassPostProcessor进行@Component的扫描,扫描得到BeanDefinition,并注册到beanFactory中
            // 注意:扫描的过程中可能又会扫描出其他的BeanFactoryPostProcessor,那么这些BeanFactoryPostProcessor也得在这一步执行
            invokeBeanFactoryPostProcessors(beanFactory);  // scanner.scan()

            // Register bean processors that intercept bean creation.
            // 将扫描到的BeanPostProcessors实例化并排序,并添加到BeanFactory的beanPostProcessors属性中去
            registerBeanPostProcessors(beanFactory);

            beanPostProcess.end();

            // Initialize message source for this context.
            // 设置ApplicationContext的MessageSource,要么是用户设置的,要么是DelegatingMessageSource
            initMessageSource();

            // Initialize event multicaster for this context.
            // 设置ApplicationContext的applicationEventMulticaster,要么是用户设置的,要么是SimpleApplicationEventMulticaster
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            // 给子类的模板方法
            onRefresh();

            // Check for listener beans and register them.
            // 把定义的ApplicationListener的Bean对象,设置到ApplicationContext中去,并执行在此之前所发布的事件
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            // 完成Bean工厂的初始化,在这个方法内部会去实例化非懒加载的单例Bean
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            ......
        }

        finally {
            ......
        }
    }
}



prepareRefresh()

往Environment中添加一些环境变量,并检查是否有必须的环境变量

父类中定义的方法模板,子类去实现的重写的,AnnotationConfigApplicationContext没有重写该方法,但是SpringMVC的那个类重写了。比如子类可以把ServletContext中的参数对设置到Environment

还会对Environment进行有没有启动时必须要有的环境变量

// 创建一个Spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
// Spring启动时 测试设置必须要有的环境变量
context.getEnvironment().setRequiredProperties("aaaa");

context.refresh();

还有一些其他的监听器相关的



obtainFreshBeanFactory()

也是一个父类的模板方法,根据子类自己的具体实现去运行

这里会判断能否刷新,并且返回一个BeanFactory, 刷新不代表完全情况,主要是先执行Bean的销毁,然后重新生成一个BeanFactory,再在接下来的步骤中重新去扫描等等
我们这里使用Spring的AnnotationConfigApplicationContext容器的父类的该方法的实现中,它不允许我们重复刷新,SpringMVC的AnnotationConfigWebApplicationContext类允许重复刷新,重复刷新就是使用context重复调用refresh()方法

Spring的AnnotationConfigApplicationContext容器这里其实就是把构造方法中创建的BeanFactory对象直接返回

AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
context.refresh();
context.refresh();
context.refresh();



prepareBeanFactory()

  • 把类加载器设置到BeanFactory的属性中
  • 如果支持EL表达式,那么就去创建一个SpringEL表达式解析器
  • 注册一些默认的类型转换器
  • 添加一些BeanPostProcessor,用来处理一些Aware回调
  • 添加一些Aware类型的接口到ignoredDependencyInterfaces这个Set集合中,Spring原始依赖注入时会判断忽略这个集合中的类型
  • 往resolvableDependencies这个Map中添加四个对象,依赖注入在DefaultListableBeanFactory.findAutowireCandidates()方法根据类型找bean对象会用到这个Map
  • 往BeanFactory中添加ApplicationListenerDetector监听器
  • Aspectj相关的代码,不用太理会
  • 添加一些环境变量相关的bean进单例池中



postProcessBeanFactory()

也是一个父类的模板方法,根据子类自己的具体实现去运行



invokeBeanFactoryPostProcessors

BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理

这里会把BeanFactory当做方法参数传进去,在该方法中会去进行包扫描scanner.scan(),把扫描得到的BeanDefinition放到BeanFactory中。

在上面笔记 《Bean生命周期——生成BeanDefinition》 这一节有详细的介绍



registerBeanPostProcessors

将扫描到的BeanPostProcessors实例化并排序,并添加到BeanFactory的beanPostProcessors属性中去

上一个invokeBeanFactoryPostProcessors()方法会去进行包扫描,会扫描到我们自己定义的一些BeanPostProcessor,通过本方法添加进BeanFactory中去。



initMessageSource()

我们知道ApplicationContext相比较于BeanFactory而言,它是支持国际化功能的。

我们在使用国际化功能时,是会使用@Bean自己在配置类中写一个返回MessageSource的方法的。

因为之前的步骤已经包扫描完成了,这里会通过beanFactory.getBean("messageSource",MessageSource.class)得到这个MessageSource的Bean对象,然后赋值给BeanFactory的messageSource属性。

// 如果我们自己没有定义,Spring则默认生成一个
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;



initApplicationEventMulticaster()

和上一个国际化方法一样,这个是事件发布器。我们知道ApplicationContext相比较于BeanFactory而言,它是支持事件发布功能的。

我们在使用国际化功能时,是会使用@Bean自己在配置类中写一个返回ApplicationEventMulticaster的方法的。

因为之前的步骤已经包扫描完成了,这里会通过beanFactory.getBean("applicationEventMulticaster",ApplicationEventMulticaster.class)得到这个ApplicationEventMulticaster的Bean对象,然后赋值给BeanFactory的applicationEventMulticaster属性。

// 如果我们自己没有定义,Spring则默认生成一个
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);



onRefresh()

也是一个父类的模板方法,根据子类自己的具体实现去运行



registerListeners()

把程序员自己定义的ApplicationListener的Bean对象,设置到ApplicationContext中去,并执行在此之前所发布的事件



finishBeanFactoryInitialization

完成Bean工厂的初始化,在这个方法内部会去实例化非懒加载的单例Bean

在上面笔记 《Bean生命周期——Spring容器启动时创建单例Bean》 这一节有详细的介绍



finishRefresh()

Spring容器启动完成后如果我们想要运行某些代码,就是通过此方法实现的

这里会通过beanFactory.getBean("lifecycleProcessor",LifecycleProcessor.class)得到这个LifecycleProcessor的Bean对象,然后赋值给BeanFactory的lifecycleProcessor属性。

然后在Spring容器启动完成后就会调用这个Bean对象的onRefresh()方法,也就是最终会调用start()方法

protected void finishRefresh() {
   ...

   // 设置lifecycleProcessor,默认为DefaultLifecycleProcessor
   initLifecycleProcessor();

   // 调用LifecycleBean的start()
   getLifecycleProcessor().onRefresh();

   ...
}
@Component
public class HsLifecycle implements SmartLifecycle{

    @Override
    public void start() {
        // Spring容器启动完成就会调用该方法
        System.out.println("start...");
    }

    @Override
    public void stop() {

    }

    @Override
    public boolean isRunning() {
        // 这个返回值为true才会调用stop()方法,为false则会调用start()方法
        return false;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1859375.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面向对象六大设计原则--依赖倒置

目录 六大原则 定义 概念 Java语言中的表现 优点 在设计模式中体现 工厂方法模式 观察者模式 状态模式 示例 手机模块设计 五个示例 一、读取数据 二、发送消息 三、支付方式 四、日志记录 五、数据持久化 使用汽车驾驶进行说明 依赖的三种写法 1.构造函数…

从0开始C++(一):从C到C++

目录 c的基本介绍 C语言和C 的区别 面向过程和面向对象的区别 引用 引用使用的注意事项 赋值 终端输入 cin getline string字符串类 遍历方式 字符串和数字转换 函数 内联函数 函数重载overload 小练习&#xff1a; 参考代码 c的基本介绍 C是一种通用的高级编…

学习金字塔模型

学习金字塔模型由美国缅因州巴特尔教育研究所&#xff08;National Training Laboratories, Bethel, Maine&#xff09;开发&#xff0c;它展示了不同学习活动的平均知识保留率。这种方法可以帮助人们理解不同学习方式的有效性&#xff0c;从而更好地选择适合的学习方法。学习金…

数据库断言-数据库更新

数据库更新的步骤和查询sql的步骤一致 1、连接数据库 驱动管理器调用连接数据库方法&#xff08;传入url&#xff0c;user&#xff0c;password&#xff09;&#xff0c;赋值给变量 2、操作数据库 connection调用参数化方法&#xff0c;对sql语法进行检查&#xff0c;存储s…

新质生产力潮水里:谁在为中小企业搭起一座桥?

与其说华为云为中小企业提供的是一个个更具性价比和产业适配度的产品&#xff0c;更本质来看&#xff0c;其通过618营销季为中小企业提供了一个数字化转型升级的契机&#xff0c;基于此&#xff0c;企业可以在云计算和AI时代实现内在变革&#xff0c;焕发新的生机与活力。 作者…

Tomcat get请求传数组集合参数

前言 最近做项目&#xff0c;需要通过GET传参&#xff0c;来实现查询的能力&#xff0c;本来是RPC调用&#xff0c;直接参数序列化即可。但是服务最近修改为HTTP&#xff0c;本来Spring Cloud的feign也可以直接传参数&#xff0c;但是当使用Nginx访问时参数到底传啥呢&#xf…

微软将停用 App Center:React Native 开发人员应该使用以下工具

再见,应用中心!你好,EAS + Sentry + GitHub Actions! React Native 开发人员大遗憾:微软宣布将于 2025 年 3 月 31 日关闭Visual Studio App Center。 多年来, Infinite Red一直将 App Center 作为许多 React Native 咨询客户的 CI/CD 流程和开发工作流程的关键部分。它有…

零基础MySQL完整学习笔记

零基础MySQL完整学习笔记 1. 基础操作(必须会!)1.1 修改密码(4种方法)1.2 创建新用户1.3 导入数据库 2. SQL四种语言介绍2.1 DDL(数据库定义语言)2.2 DML(数据操纵语言)2.3 DCL(数据库控制语言)2.4 TCL(事务控制语言) 3. 数据库操作3.1 创建数据库3.2 查询数据库3.3 删除数据库…

SpringCloud分布式微服务链路追踪方案:Zipkin

创作博客的目的是希望将自己掌握的知识系统地整理一下&#xff0c;并以博客的形式记录下来。这不仅是为了帮助其他有需要的人查阅相关内容&#xff0c;也是为了自己能够更好地巩固和加深对这些知识的理解。创作的时候也是对自己所学的一次复盘和总结&#xff0c;在创作的过程中…

python爬虫需要什么HTTP代理?

用来爬虫的话&#xff0c;还是建议用高匿名代理&#xff0c;但显然题主用了高匿名代理还是出现了一部分问题&#xff0c;我们可以先找到问题关键再解决它&#xff0c;一般爬虫用了高匿名代理出现被封会有以下几种原因&#xff1a; 1.代理IP的质量不过关 一般来说每个网站都有…

AI写文章生成器,这些工具都可以一键智能生成文章

在AI技术快速发展的今天&#xff0c;AI写作生成器成为我们创作内容的重要工具&#xff0c;它可以提高我们的写作效率&#xff0c;节省时间和精力。下面小编就来和大家分享几款优秀的AI写作生成器&#xff0c;帮助你快速生成高质量的文章。 1.专业AI写作工具-文章在线生成器 专…

ChatGPT对那些带有残疾迹象的简历有偏见——但它可以改善

ChatGPT对那些带有残疾迹象的简历有偏见——但它可以改善 去年&#xff0c;华盛顿大学(University of Washington)研究生凯特•格拉兹科(Kate Glazko)在寻找研究实习机会时注意到&#xff0c;招聘人员在网上发布消息称&#xff0c;他们使用OpenAI的ChatGPT和其他人工智能工具来…

如何模拟一个具有网络管理功能的被测件的一些思路

不知道大家有没有遇到过这个问题&#xff1f; 当我们在学习如何测试网络管理时&#xff0c;难题不在于如何编写测试脚本&#xff0c;而是编写完测试脚本后&#xff0c;没有真实被测件来让我们执行测试脚本&#xff0c;进而调试脚本。这也是我在给大家讲CANoe工具和CAPL编程语言…

Android Media Framework(八)OMXNodeInstance - Ⅰ

OpenMAX框架的学习有两大难点&#xff0c;一是组件的状态切换与buffer的流转过程&#xff0c;这部分内容我们已经在IL Spec中学习过了&#xff1b;二是OMX组件使用的buffer类型与buffer分配过程&#xff0c;这一节我们来重点剖析OMX组件使用的buffer类型。 1、引言 在实际应用…

我原以为政务类网站不追求漂亮,打脸啦,漂亮得颠覆你认知。

我原本以为政务类网站一定时沉稳、工整、信息量大的&#xff0c;这些和漂流都关联不上&#xff0c;直到最近看了一些网站&#xff0c;发现我的认识狭隘了。 政务类网站的设计风格通常需要注重以下几个方面&#xff1a; 稳重和专业感&#xff1a; 政务类网站需要给人以稳重、正…

c++分隔字符串

可以使用getline函数。 有两个版本&#xff1a; 至于为什么可以使用getline函数返回值作为while的判断条件&#xff0c;cprimer中表述如下&#xff1a;

51-60 CVPR 2024 最佳论文 | Generative Image Dynamics

在2023年11月&#xff0c;谷歌研究院发布了一项令人瞩目的研究成果——Generative Image Dynamics&#xff08;生成图像动力学&#xff09;。这项技术的核心是将静态的图片转化为动态的、无缝循环的视频&#xff0c;而且更令人兴奋的是&#xff0c;这些生成的视频还具有交互性。…

【Web APIs】JavaScript 事件基础 ② ( “ 事件 “ 开发步骤 | 常见鼠标 “ 事件 “ )

文章目录 一、" 事件 " 开发步骤1、" 事件 " 开发步骤2、完整代码示例 二、常见鼠标 " 事件 "1、常见鼠标 " 事件 "2、鼠标 " 事件 " 代码示例 Web APIs 博客相关参考文档 : WebAPIs 参考文档 : https://developer.mozilla…

计算机组成原理 | CPU子系统(1)基本概述

基本结构模型 运算与缓存部件 数据寄存部件 PSW不是很清楚 存储器是什么&#xff1f;属于那个结构里&#xff1f; 时序处理部件 cpu是大脑&#xff0c;控制器是神经元 ①通过硬件产生控制信号 ②通过软件产生控制信号 外频&#xff08;系统时钟信号&#xff09;&#xff0c;…

Springboot整合cxf进行WebService发布和WebService调用

import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; /** 测试接口 author Μr.ηobοdy date 2019-12-29 */ WebService(name “UserService”, // 暴露服务名称 targetNamespace “http://servic…