扒一扒Bean注入到Spring的那些姿势

这篇文章我准备来扒一扒Bean注入到Spring的那些姿势。

其实关于Bean注入Spring容器的方式网上也有很多相关文章,但是很多文章可能会存在以下常见的问题

  • 注入方式总结的不全

  • 没有分析可以使用这些注入方式背后的原因

  • 没有这些注入方式在源码中的应用示例

  • ...

所以本文就带着解决上述的问题的目的来重新梳理一下Bean注入到Spring的那些姿势。

配置文件

配置文件的方式就是以外部化的配置方式来声明Spring Bean,在Spring容器启动时指定配置文件。配置文件方式现在用的不多了,但是为了文章的完整性和连续性,这里我还是列出来了,知道的小伙伴可以自行跳过这节。

配置文件的类型Spring主要支持xml和properties两种类型。

xml

在XmlBeanInjectionDemo.xml文件中声明一个class为类型为User的Bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.sanyou.spring.bean.injection.User"/></beans>

User

@Data
@ToString
public class User {private String username;}

测试:

public class XmlBeanInjectionDemo {public static void main(String[] args) {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:XmlBeanInjectionDemo.xml");applicationContext.refresh();User user = applicationContext.getBean(User.class);System.out.println(user);}}

结果:

User(username=null)

可以看出成功将User注入到Spring中,由于没有设置username属性值,所以是null。

properties

除了xml,spring还支持properties配置文件声明Bean的方式。

如下,在PropertiesBeanInjectionDemo.properties文件中声明了class类型为User的Bean,并且设置User的username属性为sanyou。

user.(class) = com.sanyou.spring.bean.injection.User
user.username = sanyou

测试:

public class PropertiesBeanInjectionDemo {public static void main(String[] args) {GenericApplicationContext applicationContext = new GenericApplicationContext();//创建一个PropertiesBeanDefinitionReader,可以从properties读取Bean的信息,将读到的Bean信息放到applicationContext中PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(applicationContext);//创建一个properties文件对应的Resource对象Resource classPathResource = new ClassPathResource("PropertiesBeanInjectionDemo.properties");//加载配置文件propReader.loadBeanDefinitions(classPathResource);applicationContext.refresh();User user = applicationContext.getBean(User.class);System.out.println(user);}}

结果:

User(username=sanyou)

成功获取到User对象,并且username的属性为properties设置的sanyou。

除了可以配置属性之外还支持其它的配置,如何配置可以查看PropertiesBeanDefinitionReader类上的注释。

图片

注解声明

上一节介绍了通过配置文件的方式来声明Bean,但是配置文件这种方式最大的缺点就是不方便,因为随着项目的不断扩大,可能会产生大量的配置文件。为了解决这个问题,Spring在2.x的版本中开始支持注解的方式来声明Bean。

@Component + @ComponentScan

这种方式其实就不用多说,在项目中自定义的业务类就是通过@Component及其派生注解(@Service、@Controller等)来注入到Spring容器中的。

在SpringBoot环境底下,一般情况下不需要我们主动调用@ComponentScan注解,因为@SpringBootApplication会调用@ComponentScan注解,扫描启动引导类(加了@SpringBootApplication注解的类)所在的包及其子包下所有加了@Component注解及其派生注解的类,注入到Spring容器中。

图片

@Bean

虽然上面@Component + @ComponentScan的这种方式可以将Bean注入到Spring中,但是有个问题那就是对于第三方jar包来说,如果这个类没加@Component注解,那么@ComponentScan就扫不到,这样就无法注入到Spring容器中,所以Spring提供了一种@Bean的方式来声明Bean。

比如,在使用MybatisPlus的分页插件的时候,就可以按如下方式这么来声明。

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;
}

此时就能将MybatisPlusInterceptor这个Bean注入到Spring容器中。

@Import

@Import注解也可以用来将Bean注入到Spring容器中,@Import注解导入的类可以分为三种情况:

  • 普通类

  • 类实现了ImportSelector接口

  • 类实现了ImportBeanDefinitionRegistrar接口

普通类

普通类其实就很简单,就是将@Import导入的类注入到Spring容器中,这没什么好说的。

类实现了ImportSelector接口
public interface ImportSelector {String[] selectImports(AnnotationMetadata importingClassMetadata);@Nullabledefault Predicate<String> getExclusionFilter() {return null;}}

当@Import导入的类实现了ImportSelector接口的时候,Spring就会调用selectImports方法的实现,获取一批类的全限定名,最终这些类就会被注册到Spring容器中。

比如如下代码中,UserImportSelector实现了ImportSelector,selectImports方法返回User的全限定名

public class UserImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {System.out.println("调用 UserImportSelector 的 selectImports 方法获取一批类限定名");return new String[]{"com.sanyou.spring.bean.injection.User"};}}

当使用@Import注解导入UserImportSelector这个类的时候,其实最终就会把User注入到Spring容器中,如下测试

@Import(UserImportSelector.class)
public class ImportSelectorDemo {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();//将 ImportSelectorDemo 注册到容器中applicationContext.register(ImportSelectorDemo.class);applicationContext.refresh();User user = applicationContext.getBean(User.class);System.out.println(user);}}

运行结果

User(username=null)

对于类实现了ImportBeanDefinitionRegistrar接口的情况,这个后面说。

一般来说,@Import都是配合@EnableXX这类注解来使用的,比如常见的@EnableScheduling、@EnableAsync注解等,其实最终都是靠@Import来实现的。

图片

@EnableScheduling

图片

@EnableAsync

讲完通过注解的方式来声明Bean之后,可以来思考一个问题,那就是既然注解方式这么简单,为什么Spring还写一堆代码来支持配置文件这种声明的方式?

其实答案很简单,跟Spring的发展历程有关。Spring在创建之初Java还不支持注解,所以只能通过配置文件的方式来声明Bean,在Java1.5版本开始支持注解之后,Spring才开始支持通过注解的方式来声明Bean。

注册BeanDefinition

在说注册BeanDefinition之前,先来聊聊什么是BeanDefinition?

BeanDefinition是Spring Bean创建环节中很重要的一个东西,它封装了Bean创建过程中所需要的元信息。

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {//设置Bean classNamevoid setBeanClassName(@Nullable String beanClassName);//获取Bean className@NullableString getBeanClassName();//设置是否是懒加载void setLazyInit(boolean lazyInit);//判断是否是懒加载boolean isLazyInit();//判断是否是单例boolean isSingleton();}

如上代码是BeanDefinition接口的部分方法,从这方法的定义名称可以看出,一个Bean所创建过程中所需要的一些信息都可以从BeanDefinition中获取,比如这个Bean的class类型,这个Bean是否是懒加载,这个Bean是否是单例的等等,因为有了这些信息,Spring才知道要创建一个什么样的Bean。

有了BeanDefinition这个概念之后,再来看一下配置文件和注解声明这些方式往Spring容器注入Bean的原理。

图片

Bean注入到Spring原理

如图为Bean注入到Spring大致原理图,整个过程大致分为以下几个步骤

  • 通过BeanDefinitionReader组件读取配置文件或者注解的信息,为每一个Bean生成一个BeanDefinition

  • BeanDefinition生成之后,添加到BeanDefinitionRegistry中,BeanDefinitionRegistry就是用来保存BeanDefinition

  • 当需要创建Bean对象时,会从BeanDefinitionRegistry中拿出需要创建的Bean对应的BeanDefinition,根据BeanDefinition的信息来生成Bean

  • 当生成的Bean是单例的时候,Spring会将Bean保存到SingletonBeanRegistry中,也就是平时说的三级缓存中的第一级缓存中,以免重复创建,需要使用的时候直接从SingletonBeanRegistry中查找

好了,通过以上分析我们知道,配置文件和注解声明的方式其实都是声明Bean的一种方式,最终都会转换成BeanDefinition,Spring是基于BeanDefinition的信息来创建Bean。

既然Spring最终是基于BeanDefinition的信息来创建Bean,那么我们是不是可以跳过配置文件和注解声明的方式,直接通过手动创建和注册BeanDefinition的方式实现往Spring容器中注入呢?

答案是可以的。

前面说过,BeanDefinition最终会被注册到BeanDefinitionRegistry中,那么如何拿到BeanDefinitionRegistry呢?主要有以下两种方式:

  • ImportBeanDefinitionRegistrar

  • BeanDefinitionRegistryPostProcessor

ImportBeanDefinitionRegistrar

上面在说@Import的时候,关于导入的类实现了ImportBeanDefinitionRegistrar接口的情况没有说,主要是因为在这里说比较合适

public interface ImportBeanDefinitionRegistrar {default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,BeanNameGenerator importBeanNameGenerator) {registerBeanDefinitions(importingClassMetadata, registry);}default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {}}

ImportBeanDefinitionRegistrar中有两个方法,方法的参数就是BeanDefinitionRegistry。当@Import导入的类实现了ImportBeanDefinitionRegistrar接口之后,Spring就会调用registerBeanDefinitions方法,传入BeanDefinitionRegistry。

来个Demo

UserImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar

public class UserImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {//构建一个 BeanDefinition , Bean的类型为 UserAbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class)//设置User这个Bean的属性username的值为三友的java日记.addPropertyValue("username", "三友的java日记").getBeanDefinition();//把User的BeanDefinition注入到BeanDefinitionRegistry中registry.registerBeanDefinition("user", beanDefinition);}}

测试类

@Import(UserImportBeanDefinitionRegistrar.class)
public class UserImportBeanDefinitionRegistrarDemo {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();applicationContext.register(UserImportBeanDefinitionRegistrarDemo.class);applicationContext.refresh();User user = applicationContext.getBean(User.class);System.out.println(user);}}

结果

User(username=三友的java日记)

从结果可以看出,成功将User注入到了Spring容器中。

上面的例子中有行代码

applicationContext.register(UserImportBeanDefinitionRegistrarDemo.class);

这行代码的意思就是把UserImportBeanDefinitionRegistrarDemo这个Bean注册到Spring容器中,所以这里其实也算一种将Bean注入到Spring的方式,原理也跟上面一样,会为UserImportBeanDefinitionRegistrarDemo生成一个BeanDefinition注册到Spring容器中。

BeanDefinitionRegistryPostProcessor

除了ImportBeanDefinitionRegistrar可以拿到BeanDefinitionRegistry之外,还可以通过BeanDefinitionRegistryPostProcessor拿到BeanDefinitionRegistry

图片

BeanDefinitionRegistryPostProcessor

这种方式就不演示了。

手动注册BeanDefinition这种方式还是比较常见的。就比如说OpenFeign在启用过程中,会为每个标注了@FeignClient注解的接口创建一个BeanDefinition,然后再往Spring中的注册的,如下是OpenFeign注册FeignClient的部分代码

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {//构建BeanDefinition,class类型为FeignClientFactoryBeanBeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);String alias = contextId + "FeignClient";AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });//注册BeanDefinitionBeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}
}

注册创建完成的Bean

上一节说可以跳过配置文件或者是注解,直接通过注册BeanDefinition以达到将Bean注入到Spring中的目的。

既然已经可以跳过配置文件或者是注解,那么我们可不可以更激进一步,跳过注册BeanDefinition这一步,直接往Spring中注册一个已经创建好的Bean呢?

答案依然是可以的。

因为上面在提到当创建的Bean是单例的时候,会将这个创建完成的Bean保存到SingletonBeanRegistry中,需要用到直接从SingletonBeanRegistry中查找。既然最终是从SingletonBeanRegistry中查找的Bean,那么直接注入一个创建好的Bean有什么不可以呢?

既然可以,那么如何拿到SingletonBeanRegistry呢?

其实拿到SingletonBeanRegistry的方法其实很多,因为ConfigurableListableBeanFactory就继承了SingletonBeanRegistry接口,所以只要能拿到ConfigurableListableBeanFactory就相当于拿到了SingletonBeanRegistry。

图片

ConfigurableListableBeanFactory类图

而ConfigurableListableBeanFactory可以通过BeanFactoryPostProcessor来获取

图片

BeanFactoryPostProcessor

来个Demo

RegisterUserBeanFactoryPostProcessor实现BeanFactoryPostProcessor, 往Spring容器中添加一个手动创建的User对象

public class RegisterUserBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {//创建一个User对象User user = new User();user.setUsername("三友的java日记");//将这个User对象注入到Spring容器中beanFactory.registerSingleton("user", user);}}

测试

public class RegisterUserDemo {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();applicationContext.register(RegisterUserBeanFactoryPostProcessor.class);applicationContext.refresh();User user = applicationContext.getBean(User.class);System.out.println(user);}}

结果

User(username=三友的java日记)

从结果还是可以看出,成功从Spring容器中获取到了User对象。

这种直接将创建好的Bean注入到Spring容器中在Spring框架内部使用的还是比较多的,Spring的一些内建的Bean就是通过这个方式注入到Spring中的。

图片

如上图,在SpringBoot项目启动的过程中会往Spring容器中添加两个创建好的Bean,如果你的程序需要使用到这些Bean,就可以通过依赖注入的方式获取到。

虽然基于这种方式可以将Bean注入到Spring容器,但是这种方式注入的Bean是不经过Bean的生命周期的,也就是说这个Bean中诸如@Autowired等注解和Bean生命周期相关的回调都不会生效的,注入到Spring时Bean是什么样就是什么样,Spring不做处理,仅仅只是做一个保存作用。

FactoryBean

FactoryBean是一种特殊的Bean的类型,通过FactoryBean也可以将Bean注入到Spring容器中。

图片

FactoryBean

当我们通过配置文件、注解声明或者是注册BeanDenifition的方式,往Spring容器中注入了一个class类型为FactoryBean类型的Bean时候,其实真正注入的Bean类型为getObjectType方法返回的类型,并且Bean的对象是通过getObject方法返回的。

来个Demo

UserFactoryBean实现了FactoryBean,getObjectType返回了User类型,所以这个UserFactoryBean会往Spring容器中注入User这个Bean,并且User对象是通过getObject()方法的实现返回的。

public class UserFactoryBean implements FactoryBean<User> {@Overridepublic User getObject() throws Exception {User user = new User();user.setUsername("三友的java日记");return user;}@Overridepublic Class<?> getObjectType() {return User.class;}
}

测试

public class UserFactoryBeanDemo {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();//将UserFactoryBean注入到Spring容器中applicationContext.register(UserFactoryBean.class);applicationContext.refresh();User user = applicationContext.getBean(User.class);System.out.println(user);}}

结果

User(username=三友的java日记)

成功通过UserFactoryBean将User这个Bean注入到Spring容器中了。

FactoryBean这中注入的方式使用也是非常多的,就拿上面举例的OpenFeign来说,OpenFeign为每个FeignClient的接口创建的BeanDefinition的Bean的class类型FeignClientFactoryBean就是FactoryBean的实现。

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {// FeignClient接口类型private Class<?> type;@Overridepublic Object getObject() throws Exception {return getTarget();}@Overridepublic Class<?> getObjectType() {return type;}
}

getObject()方法就会返回接口的动态代理的对象,并且这个代理对象是由Feign创建的,这也就实现了Feign和Spring的整合。

总结

通过以上分析可以看出,将Bean注入到Spring容器中大致可以分为5类:

  • 配置文件

  • 注解声明

  • 注册BeanDefinition

  • 注册创建完成的Bean

  • FactoryBean

以上几种注入的方式,在日常业务开发中,基本上都是使用注解声明的方式注入Spring中的;在第三方框架在和Spring整合时,注册BeanDefinition和FactoryBean这些注入方式也会使用的比较多;至于配置文件和注册创建完成的Bean的方式,有但是不多。

最后,本文所有的示例代码地址:

https://github.com/sanyou3/spring-bean-injection.git

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

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

相关文章

Navicat 下载

1 中文网站 Navicat 中国 | 支持 MySQL、Redis、MariaDB、MongoDB、SQL Server、SQLite、Oracle 和 PostgreSQL 的数据库管理 下载链接&#xff1a; https://download.navicat.com.cn/download/navicat163_premium_cs_x86.exe 2 下载其他版本(找到发行说明--版本号--替换版本…

spring boot项目未将resource目录标志为资源目录导致配置文件无效因而运行报错问题

能编译&#xff0c;但不能运行。感觉配置文件没有生效。 将程序代码发给同事&#xff0c;我自己能跑&#xff0c;他不能跑&#xff0c;提示无法构造redis对象。redis的链接写在配置文件里&#xff0c;其实是可以连接的。然后从GIT库下载代码&#xff0c;也同样不能跑。同事的操…

某高品质房产企业:借助NineData平台,统一数据库访问权限,保障业务安全

该企业是中国领先的优质房产品开发及生活综合服务供应商。在 2022 年取得了亮眼的业绩表现&#xff0c;销售额市场占有率跻身全国前五。业务涵盖房产开发、房产代建、城市更新、科技装修等多个领域。 2023 年&#xff0c;该企业和玖章算术&#xff08;浙江&#xff09;科技有限…

连线长光卫星:吉林一号的在线产品与生态体系!

我们在《连线长光卫星&#xff1a;探索卫星应用的更多可能&#xff01;》一文中&#xff0c;通过直播连线嘉宾的分享&#xff0c;让大家了解到了长光卫星的生产基地、三次技术飞跃、亚米级影像产品、150公里大幅宽卫星、卫星在灾害监测及经济分析等多个场景中的应用。 这里我们…

Midjourney绘画提示词Prompt参考教程

Midjourney绘画提示词Prompt参考教程&#xff1a;无需魔法使用。 一、AI工具 SparkAi&#xff1a; SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常…

ProPresenter 7 for Mac:Mac电脑好用的文稿演示软件

ProPresenter 7是一款专业的多媒体演示软件&#xff0c;主要用于创建和展示高质量的教堂媒体演示、音乐歌词、幻灯片和视频等。它是一款功能强大且易于使用的软件&#xff0c;被广泛应用于教堂、会议、演唱会和其他场合。以下是ProPresenter 7的一些主要特点和功能&#xff1a;…

window上Clion配置C++版本的opencv

window上Clion配置opencv 注意版本一定要对的上&#xff0c;否则可能会出错&#xff0c;亲测 widnows 11mingw 8.1.0opencv 4.5.5 mingw8.1下载地址https://sourceforge.net/projects/mingw/ 配置环境变量 cmake下载 安装完添加环境变量 来到官网&#xff0c;下载 windows 对…

GNU工具链

1. GNU介绍 工具链典型的例子就是GNU工具链。 GNU工具链是由GNU项目产生的各种编程工具的集合&#xff0c;用于开发应用程序与操作系统。 GNU工具链在针对嵌入式系统的Linux内核、BSD及其它软件的开发中起着至关重要的作用。 GNU工具链中的部分工具也被Mac OS X, Microsoft W…

【C++】类和对象一

今天来到了类和对象部分&#xff0c;我们知道C语言是面向过程编程&#xff0c;而C是面向对象编程&#xff0c;那么怎么个具体实现方法呢&#xff1f;简单来说&#xff0c;就是C语言对结构体的定义和对结构体的操作是分开的&#xff0c;这样就显得过程很独立&#xff1b;而C是把…

windows11记事本应用程序无法打开,未响应,崩溃,卡死

windows11记事本应用程序无法打开&#xff0c;未响应&#xff0c;崩溃&#xff0c;卡死 文章目录 问题描述搜索引擎&#xff08;度娘&#xff09;卸载后如何安装问题未解决另一个解决方案&#xff1a;步骤&#xff1a;1.设置 → 语音和区域 → 输入2.选择“高级键盘设置”3.替…

LeetCode - 622. 设计循环队列(C语言,顺序存储结构,配图)

目录 ​编辑定义结构体&#xff1a; 1. MyCircularQueue(k): 构造器&#xff0c;设置队列长度为 k 2. Front: 从队首获取元素。如果队列为空&#xff0c;返回 -1 3. Rear: 获取队尾元素。如果队列为空&#xff0c;返回 -1 4. enQueue(value): 向循环队列插入一个元素。…

基于springboot实现在线外卖平台系统项目【项目源码】

基于springboot实现在线外卖平台管理系统演示 Java技术 Java是由SUN公司推出&#xff0c;该公司于2010年被oracle公司收购。Java本是印度尼西亚的一个叫做爪洼岛的英文名称&#xff0c;也因此得来java是一杯正冒着热气咖啡的标识。Java语言在移动互联网的大背景下具备了显著的…

运动耳机哪个牌子好?盘点最值得入手的五款运动耳机

现在&#xff0c;不入耳的运动耳机成了许多运动爱好者的首选&#xff0c;我也不例外&#xff0c;不得不说骨传导耳机跟运动真的很搭&#xff0c;不仅佩戴稳固不掉落&#xff0c;而且防水好&#xff0c;可以说是最值得入手的运动耳机&#xff0c;为了避免大家在选购运动耳机的时…

电脑磁盘怎么设置密码?磁盘加密软件哪个好?

电脑磁盘经常被用于存放各种重要数据&#xff0c;而为了避免数据泄露&#xff0c;我们需要为磁盘设置密码&#xff0c;以防止未授权人员使用磁盘。那么&#xff0c;电脑磁盘怎么设置密码呢&#xff1f;下面我们就一起来了解一下。 如何设置磁盘密码&#xff1f; 想要为磁盘设置…

计算机毕业设计项目选题推荐(免费领源码)java+springboot+mysql社区团购APP 02043

目录 摘要 1 绪论 1.1 研究背景 1.2国内外研究现状 1.3本课题主要工作 1.4论文结构与章节安排 2 社区团购系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.…

简于外 强于内,联想全新ThinkCentre M90a Pro Gen4以强劲性能开启商

近日&#xff0c;联想发布了最新一代商用台式一体机联想ThinkCentre M90a Pro Gen4。作为联想ThinkCentre M大师系列的旗舰产品&#xff0c;其配备了优质的显示屏&#xff0c;拥有强大的性能和稳定安全的特性&#xff0c;能够满足多样的工作场合&#xff0c;为商用一体机的行业…

Java核心知识点整理大全6-笔记

目录 4.1.4. 线程生命周期(状态) 4.1.4.1. 新建状态&#xff08;NEW&#xff09; 4.1.4.2. 就绪状态&#xff08;RUNNABLE&#xff09;&#xff1a; 4.1.4.3. 运行状态&#xff08;RUNNING&#xff09;&#xff1a; 4.1.4.4. 阻塞状态&#xff08;BLOCKED&#xff09;&#xff…

五、程序员指南:数据平面开发套件

服务质量 (QoS) 框架 本章介绍 DPDK 服务质量 (QoS) 框架。 21.1 带有 QoS 支持的数据包流水线 下图显示了一个具有 QoS 支持的复杂数据包处理流水线的示例 表21.1&#xff1a;带有 QoS 支持的复杂数据包处理流水线 这个流水线可以使用可重用的 DPDK 软件库构建。在这个流…

HarmonyOS ArkTS语言,运行Hello World(一)

一、下载与安装DevEco Studio 在HarmonyOS应用开发学习之前&#xff0c;需要进行一些准备工作&#xff0c;首先需要完成开发工具DevEco Studio的下载与安装以及环境配置。 进入DevEco Studio下载官网&#xff0c;单击“立即下载”进入下载页面。 DevEco Studio提供了Windows…

【深度学习】不用Conda在PP飞桨Al Studio三个步骤安装永久PyTorch环境

在 PaddlePaddle AI Studio 中使用 Python 虚拟环境安装 PyTorch 免责声明 在阅读和实践本文提供的内容之前&#xff0c;请注意以下免责声明&#xff1a; 侵权问题: 本文提供的信息仅供学习参考&#xff0c;不用做任何商业用途&#xff0c;如造成侵权&#xff0c;请私信我&am…