谈谈Spring Bean

news2025/1/13 7:45:16

一、IoC 容器

IoC 容器是 Spring 的核心,Spring 通过 IoC 容器来管理对象的实例化和初始化(这些对象就是 Spring Bean),以及对象从创建到销毁的整个生命周期。也就是管理对象和依赖,以及依赖的注入等等。

Spring 提供 2 种不同类型的 IoC 容器:BeanFactory 和 ApplicationContext 容器。

1.1 BeanFactory 容器

BeanFactory 是一个管理 Bean 的工厂,它主要负责初始化各种 Bean, 并调用它们的生命周期方法。BeanFactory 是最简单的 Bean 容器,它由 org.springframework.beans.factory.BeanFactory 接口定义实现。提供了容器最基本的功能。

目前 BeanFactory 没多少人用,主要是为了能够兼容 Spring 集成的第三方框架,所以目前仍然保留了该接口。下面是官网的解释

The BeanFactory and related interfaces, such as BeanFactoryAware, InitializingBean, DisposableBean, are still present in Spring for the purposes of backward compatibility with the large number of third-party frameworks that integrate with Spring.

Beanfactory 是 org.springframework.beans 的顶级接口。

1.2 ApplicationContext 容器

ApplicationContext 容器几乎涵盖所有 BeanFactory 容器的功能,它继承了 BeanFactory 接口,由org.springframework.context.ApplicationContext 接口定义实现。在 BeanFactory 的基础上增加了AOP、事务支持等等功能。现在Spring 实际开发中基本上使用的是 ApplicationContext 容器。

ApplicationContext 是 org.springframework.context 的顶级接口。

ApplicationContext 有两个常用的实现类,分别是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 。

1.2.1 ClassPathXmlApplicationContext 类

看名字就知道它是从类路径 ClassPath 中寻找 XML 配置文件,来完成 ApplicationContext 实例化工作:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("configlocation");
//configlocation 是指定 Spring 配置文件(XML)的名称和位置,比如 Beans.xml
1.2.2 FileSystemXmlApplicationContext

该类是从文件系统中寻找 XML 配置文件:

ApplicationContext applicationContext = new FileSystemXmlApplicationContext("configlocation");
//configlocation 是从非类路径外中获取 XML 的名称和位置,比如 ”F:/workCode/Beans.xml“

它们都是通过 XML 配置文件来加载 Bean 的。

二、 Spring Bean 的定义

看官网定义:

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

Bean 是由 Spring IoC 容器管理的对象,容器就能通过反射的形式将容器中准备好的对象注入(这里使用的是反射给属性赋值)到需求的组件中去,简单来说,Spring IoC 容器可以看作是一个工厂,Bean 相当于工厂的产品。 Spring 配置文件则告诉容器需要哪些 Bean,以及需要哪种方式来装配 Bean。

Bean 其实就是一个 Java 对象,它是根据 bean 规范编写出来的类,并且由容器生成的对象就是一个 bean。

Bean 规范:

  1. 所有属性是 private
  2. 提供默认构造方法
  3. 提供 getter 和 setter
  4. 实现 serializable 接口

它和 POJO 其实是一样的,只不过是遵循 Bean 规范的 POJO 。

Spring 配置文件

spring 配置文件主要支持两种格式:XML 和 Properties 格式

  • Properties 配置文件主要以 key-value 键值对的形式存在,不能进行其他操作,使用于简单的属性配置
  • XML 配置文件是树形结构,文件结构清晰,但是内容比较繁琐,使用于大型复杂项目

一般来说,Spring 的配置文件使用 XML 格式。 XML 配置文件的根元素是,该元素下包含多个子元素。每个都定义了一个 bean ,并描述了该 Bean 如何被装配到 Spring 容器中。

元素的常用属性:

属性名称描述
idBean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成。id 的值必须以字母开始,可以使用字母、数字、下划线等符号。
namename 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开。Spring 容器可以通过 name 属性配置和管理容器中的 Bean。
class该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,即类的全限定名。
scope用于设定 Bean 实例的作用域,属性值可以为 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton
constructor-arg元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型
property元素的子元素,用于调用 Bean 实例中的 setter 方法来属性赋值,从而完成依赖注入。该元素的 name 属性用于指定 Bean 实例中相应的属性名
ref 和 等元素的子元索,该元素中的 bean 属性用于指定对某个 Bean 实例的引用
value 和 等元素的子元素,用于直接指定一个常量值
list用于封装 List 或数组类型的依赖注入
set用于封装 Set 类型的依赖注入
map用于封装 Map 类型的依赖注入
entry 元素的子元素,用于设置一个键值对。其 key 属性指定字符串类型的键值,ref 或 value 子元素指定其值
init-method容器加载 Bean 时调用该方法,类似于 Servlet 中的 init() 方法
destroy-method容器删除 Bean 时调用该方法,类似于 Servlet 中的 destroy() 方法。该方法只在 scope=singleton 时有效
lazy-init懒加载,值为 true,容器在首次请求时才会创建 Bean 实例;值为 false,容器在启动时创建 Bean 实例。该方法只在 scope=singleton 时有效

三、 Spring Bean 的作用域

Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。Spring 5 支持 6 种作用域。

3.1 singleton

默认的作用域,单例模式。表示在 Spring 容器中只有一个 Bean 实例,Bean 以单例的方式存在。在容器启动前就创建好了对象,任何时间获取都是之前创建好的那个对象。配置方式可以缺省,因为是默认值。<bean class="..."></bean>

3.2 prototype

原型作用域,多实例模式。每次调用 Bean 时都会创建一个新实例。Bean 以多实例的方式存在。容器启动默认不会创建多实例 bean,每次获取都会创建一个新的实例 bean 。配置方式为 <bean class="..." scope="prototype"></bean>

3.3 request

在 web 环境下,每次 HTTP 请求都会创建一个 Bean 实例,该作用域只在当前 HTTP Request 内有效。 配置方式为 <bean class="..." scope="request"></bean>

3.4 session

在 web 环境下,每次 HTTP 会话共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域只在当前 HTTP Session 内有效。配置方式为 <bean class="..." scope="session"></bean>

3.5 application

在web 环境下,同一个 web application 共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。

3.6 websocket

在web 环境下,同一个 websocket 共享一个 Bean 实例,该作用域在整个 websocket 中有效。

四、Spring Bean 的注册方式

Bean 的初始化主要分为两个过程:Bean 的注册和 Bean 的实例化。Bean 的注册主要是指 Spring 通过读取配置文件获取各个 bean 的声明信息,并且对这些信息进行注册的过程。

4.1 XML 配置文件注册方式

在 XML 中配置好后进行注册

<bean id="person" class="org.springframework.beans.Person">
   <property name="id" value="1"/>
   <property name="name" value="Java"/>
</bean>

4.2 Java 注解注册方式

可以使用 @Component 或 @Configuration + @Bean 来注册 Bean

@Component
public class Person {
   private Integer id;
   private String name
   // 忽略其他方法
}
@Configuration               //可以理解为 XML 配置文件中的 <beans> 标签
public class Person {
   @Bean                     //可以理解为 XML 配置文件中的 <bean> 标签
   public Person  person(){
      return new Person();
   }
   // 忽略其他方法
}

4.3 Java API 注册方式

使用 BeanDefinitionRegistry.registerBeanDefinition() 方法来注册 Bean, 代码如下:

public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		RootBeanDefinition personBean = new RootBeanDefinition(Person.class);
		// 新增 Bean
		registry.registerBeanDefinition("person", personBean);
	}
}

五、Spring Bean 的生命周期

Spring 中的 Bean 的生命周期比较复杂,可以表示为: Bean 的定义 -> Bean 的初始化 -> Bean 的使用 -> Bean 的销毁

Spring 是根据 Bean 的作用域来管理,对于单实例 singleton 作用域的 Bean, Spring 能够精确地知道 这个 Bean 的完整生命周期;而对于 prototype 作用域的 Bean, Spring 只负责创建, 当容器创建了 Bean 的实例后,Bean 的实例就交给客户端管理,Spring 容器将不再跟踪其生命周期。

首先,从上面几节中看到,关于 Bean 的定义和初始化中的注册都在配置文件中或者其他方式提前写好。下面我们直接从 Bean 初始化中的实例化开始看,一般会有以下几个过程:

5.1 实例化 Bean

Spring 启动, 查找并加载需要被 Spring 管理的 Bean , 并实例化 Bean ,实例化就是通过容器生成一个 Bean。实例化 Bean 方式主要有三种:类的无参构造方法、静态工厂、实例工厂。

5.1.1 无参构造方法创建

在配置文件 XML 中配置 bean, 默认使用了无参构造器创建 bean

<bean id="bean" class="com.spring.demo.Bean"></bean>

然后再通过 getBean() 方法来获取实例化的 bean

ApplicationContext context = new ClasspathXmlApplicationContext("Bean.xml");
Bean b = (Bean)context.getBean("Bean.xml");
5.1.2 静态工厂方法创建

同样也是需要在 XML 中配置 bean :

<bean id="bean" class="com.spring.demo.BeanFactory" factory-method="getBean"></bean>

id 和 class 都定位到某个工厂类,factory-method 表示调用到该类 BeanFactory 下的方法来创建对象,而且这个 getBean 方法必须是静态方法。

同样是用 getBean() 方法获取实例化的 bean ,就不赘余了。

5.1.3 实例工厂方法创建

同样的,配置 XML 文件

<bean id="beanfactory" class="com.spring.demo.BeanFactory"></bean>
<bean id="bean" factory-bean="beanfactory" factory-method="getbean"></bean>

实例工厂和静态工厂的区别在于,该实例化方式工厂方法不需要是静态的,需要先创建对象(在工厂类中新建一个对象),然后再通过对象调用其方法创建 bean。

5.2 设置属性值(依赖注入)

这个阶段需要利用依赖注入完成 Bean 中的所有属性值的配置注入。容器的注入方法主要有构造方法和 Setter 方法注入。

5.2.1 构造方法注入

注入方式是使用 标签来实现构造函数的注入,在该标签中,包含这样几种属性:

  • value: 用于注入基本数据类型以及字符串类型的值
  • ref: 注入已经定义好的 Bean
  • type: 用来指定对应的构造函数
  • index: 若构造函数有多个参数的时候,可以使用index 属性指定参数的位置,给参数的位置进行排序
<bean id="" class="">
    <constructor-arg index="0" value=""></constructor-arg>
    <constructor-arg index="1" ref=""></constructor-arg>
</bean>
5.2.2 Setter 方法注入

Setter 方法注入的方式是目前 Spring 主流的注入方式,它可以利用 Java Bean 规范所定义的 Setter/Getter 方法来完成注入,可读性和灵活性都很高,它不需要使用声明式构造方法,而是使用 Setter 注入直接设置相关的值。

<bean id="person" class="org.springframework.beans.Person">
    <property name="id" value="1"/>
    <property name="name" value="Java"/>
</bean>

在 Spring 实例化 Bean 的过程中,首先会调用默认的构造方法实例化 Bean 的对象,然后通过 Java 的反射机制调用 set 方法进行属性的注入。因此,setter 注入要求 Bean 的对应类必须满足一下要求:

  • 必须提供一个默认的无参构造方法
  • 必须为需要注入的属性提供对应的 setter 方法

5.3 调用 Aware 的相关方法

5.3.1 调用 BeanNameAware 的 setBeanName() 方法

如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。

5.3.2 调用 BeanFactoryAware 的 setBeanFactory() 方法

如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

5.3.3 调用 ApplicationContextAware 的 setApplicationContext()方法

如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

5.4 调用 BeanPostProcessor 的预初始化方法

如果 Bean 实现了 BeanPostProcessor 接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

5.5 调用InitializingBean 的 afterPropertiesSet() 方法和定制的初始化方法

InitializingBean 是一个接口,它有一个 afterPropertiesSet() 方法,在 Bean 初始化时会判断当前 Bean 是否实现了 InitializingBean,如果实现了则调用 afterPropertiesSet() 方法,进行初始化工作;然后再检查是否也指定了 init-method,如果指定了则通过反射机制调用指定的 init-method 方法,它的实现源码如下:

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
        throws Throwable {
    // 判断当前 Bean 是否实现了 InitializingBean,如果是的话需要调用 afterPropertiesSet()
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isTraceEnabled()) {
            logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        if (System.getSecurityManager() != null) { // 安全模式
            try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((InitializingBean) bean).afterPropertiesSet(); // 属性初始化
                    return null;
                }, getAccessControlContext());
            } catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        } else {
            ((InitializingBean) bean).afterPropertiesSet(); // 属性初始化
        }
    }
    // 判断是否指定了 init-method()
    if (mbd != null && bean.getClass() != NullBean.class) {
        String initMethodName = mbd.getInitMethodName();
        if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            // 利用反射机制执行指定方法
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

5.6 调用 BeanPostProcessor 的后初始化方法

如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

5.7 Bean 的使用

如果在 中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

5.8 Bean 的销毁

在 Spring 容器关闭时会执行销毁方法,但是 Spring 容器不会自动去调用销毁方法,而是需要我们主动的调用。

如果是 BeanFactory 容器,那么我们需要主动调用 destroySingletons() 方法,通知 BeanFactory 容器去执行相应的销毁方法;如果是 ApplicationContext 容器,那么我们需要主动调用 registerShutdownHook() 方法,告知 ApplicationContext 容器执行相应的销毁方法。

5.9 小结

一般情况下,会在 Bean 被初始化后和被销毁前执行一些相关操作。

Spring 官方提供了 3 种方法实现初始化回调和销毁回调:

  1. 实现 InitializingBean 和 DisposableBean 接口;
  2. 在 XML 中配置 init-method 和 destory-method;
  3. 使用 @PostConstruct 和 @PreDestory 注解。
    在一个 Bean 中有多种生命周期回调方法时,优先级为:注解 > 接口 > XML。

不建议使用接口和注解,这会让 pojo 类和 Spring 框架紧耦合。

六、Spring Bean 自动装配

Bean 的装配可以理解为依赖关系注入,Bean 的装配方式也就是 Bean 的依赖注入方式。Spring 容器支持多种装配 Bean 的方式,如基于 XML 的 Bean 装配、基于 Annotation 的 Bean 装配和自动装配等。基于 XML 的装配方式主要分为两种,在 5.2 设置属性值 中提到的过。

自动装配就是指 Spring 容器在不使用 和 标签的情况下,可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。使用自动装配需要配置 元素的 autowire 属性。autowire 属性有五个值,具体说明如下表所示。

名称说明
no默认值,表示不使用自动装配,Bean 依赖必须通过 ref 元素定义。
byName根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。
byType根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。
constructor类似于 byType,根据构造方法参数的数据类型,进行 byType 模式的自动装配。
autodetect(3.0版本不支持)如果 Bean 中有默认的构造方法,则用 constructor 模式,否则用 byType 模式。

5.10 基于注解装配 Bean

尽管可以使用 XML 来装配 Bean , 但是如果应用中 Bean 数量过多,会导致 XML 配置文件过于臃肿,对后期维护带来一定的困难

Java 从 JDK 5.0 以后,提供了 Annotation(注解)功能,Spring 2.5 版本开始也提供了对 Annotation 技术的全面支持,我们可以使用注解来配置依赖注入。Spring 默认不使用注解装配 Bean,因此需要在配置文件中添加 < context:annotation-config >,启用注解。

Spring 中常用的注解如下。

5.10.1 @Component

可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。

5.10.2 @Repository

用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

5.10.3 @Service

通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

5.10.4 @Controller

通常作用在控制层(如 Struts2 的 Action、SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

5.10.5 @Autowired

可以应用到 Bean 的属性变量、属性的 setter 方法、非 setter 方法及构造函数等,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。

5.10.6 @Resource

作用与 Autowired 相同,区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。

@Resource 中有两个重要属性:name 和 type。

Spring 将 name 属性解析为 Bean 的实例名称,type 属性解析为 Bean 的实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。

5.10.7 @Qualifier

与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。

参考文章

  1. The IoC container

  2. spring bean是什么

  3. POJO、JavaBean、Spring Bean 的解释和区别

  4. 底层源码分析 Spring 的核心功能和执行流程?

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

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

相关文章

重学MySQL之关系型数据库和非关系型数据库

1 关系型数据库 1.1 关系型数据库的特性 1.1.1 事务的特性 事务&#xff0c;是指一个操作序列&#xff0c;这些操作要么都执行&#xff0c;或者都不执行&#xff0c;而且这一序列是无法分隔的独立操作单位。也就是符合原子性&#xff08;Atomicity&#xff09;、 一致性&…

C语言——结构体类型(二)【结构体内存对齐,结构体数组】

&#x1f4dd;前言&#xff1a; 上一讲结构体类型&#xff08;一&#xff09;中&#xff0c;我们讲述了有关结构体定义&#xff0c;创建&#xff0c;初始化和引用的内容&#xff0c;这一讲&#xff0c;我们进一步学习结构体的相关知识&#xff1a; 1&#xff0c;结构体内存对齐…

Delphi 11.3配置android环境

电脑安装dephi11.3的时候&#xff0c;勾选android sdk&#xff0c;但是软件安装好以后&#xff0c;还有问题 在Delphi—tool —options 里边&#xff0c;Deployment下SDKManager 中&#xff0c;看到SDk里边的感叹号&#xff0c;说明android sdk没有安装好 解决方法有2种 第一种…

MySQL从0到1全教程【1】MySQL数据库的基本概念以及MySQL8.0版本的部署

1 MySQL数据库的相关概念 1.1 数据库中的专业术语 1.1.1 数据库 (DB) 数据库是指:保存有组织的数据的容器(通常是一个文数据库 (database)件或一组文件)。 1.1.2 数据库管理系统 (DBMS) 数据库管理系统(DBMS)又称为数据库软件(产品)&#xff0c;用于管理DB中的数据 注意:…

小H靶场笔记:Empire-Breakout

Empire&#xff1a;Breakout January 11, 2024 11:54 AM Tags&#xff1a;brainfuck编码&#xff1b;tar解压变更目录权限&#xff1b;Webmin&#xff1b;Usermin Owner&#xff1a;只惠摸鱼 信息收集 使用arp-scan和namp扫描C段存活主机&#xff0c;探测靶机ip&#xff1a;1…

横版动作闯关游戏:幽灵之歌 GHOST SONG 中文版

在洛里安荒凉的卫星上&#xff0c;一件长期休眠的死亡服从沉睡中醒来。踏上发现自我、古老谜团和宇宙骇物的氛围2D冒险之旅。探索蜿蜒的洞穴&#xff0c;获得新的能力来揭开这个外星世界埋藏已久的秘密。 游戏特点 发现地下之物 探索这个广阔而美丽如画&#xff0c;充满密室和诡…

Graham扫描凸包算法

凸包&#xff08;Convex Hull&#xff09;是包含给定点集合的最小凸多边形。凸包算法有多种实现方法&#xff0c;其中包括基于递增极角排序、Graham扫描、Jarvis步进法等。下面&#xff0c;我将提供一个简单的凸包算法实现&#xff0c;基于Graham扫描算法。 Graham扫描算法是一…

关于PhpStorm的安装激活与汉化

访问官网下载PhpStorm https://www.jetbrains.com/phpstorm/download/#sectionwindows 点击download 下载好后&#xff0c;双击exe安装程序 点击下一步 选择安装位置 前两个肯定需要勾选&#xff1a; 创建桌面快捷方式&#xff1b;创建关联php&#xff1b; 根据以往经验&am…

CES 2024丨引领变革,美格智能为智能终端带来生成式AI能力

作为电子行业的“风向标”&#xff0c;CES 2024&#xff08;国际消费电子展&#xff09;于1月9日至12日在美国拉斯维加斯举办。本届展会可谓是AI的盛宴&#xff0c;芯片、AI PC、智能家居、汽车科技、消费电子等领域与AI相关的前沿成果接连发布&#xff0c;引领人工智能领域的科…

【数据库】视图索引执行计划多表查询笔试题

文章目录 一、视图1.1 概念1.2 视图与数据表的区别1.3 优点1.4 语法1.5 实例 二、索引2.1 什么是索引2.2.为什么要使用索引2.3 优缺点2.4 何时不使用索引2.5 索引何时失效2.6 索引分类2.6.1.普通索引2.6.2.唯一索引2.6.3.主键索引2.6.4.组合索引2.6.5.全文索引 三、执行计划3.1…

leaflet学习笔记-缓冲区绘制(六)

前言 在GIS开发中&#xff0c;缓冲区的绘制和使用是非常广泛的&#xff0c;一般情况下就是对缓冲区范围内的要素做分析使用&#xff0c;也会有一些其他的操作&#xff0c;下面我就记录一下使用leafletturf.js完成缓冲区的绘制操作 turf.js简介 Turf.js 是一个用于地理空间计…

上架苹果APP的时候在哪里填写APP的隐私政策信息

在如今高度重视数据隐私的时代&#xff0c;开发并上架一个iOS APP时提供透明的隐私政策是非常重要的。苹果公司对此有严格的规定&#xff0c;任何上架至App Store的应用都必须包含一个隐私政策。以下是您在上架苹果APP时填写隐私政策信息的详细步骤和必须注意的事项。 准备隐私…

考古学家 - 华为OD统一考试

OD统一考试 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 有一个考古学家发现一个石碑&#xff0c;但是很可惜发现时其已经断成多段。 原地发现N个断口整齐的石碑碎片&#xff0c;为了破解石碑内容&#xff0c;考古学家希望有程序能帮忙计算复原后的石…

最新PyCharm安装详细教程及pycharm配置_pycharm安装教程

目录 一、PyCharm简介及其下载网站 二、单击网站的Downloads&#xff0c;进入二级页面&#xff0c;选择对应的操作系统下载PyCharm 三、PyCharm的安装程序的安装及其配置(configuration) 1、运行PyCharm Setup 2、安装位置设置 3、安装选项设置 4、开始菜单中PyCharm快捷方式的…

Unity中向量的点乘、叉乘区别和作用以及经典案例

文章目录 点乘&#xff08;Dot Product&#xff09;叉乘&#xff08;Cross Product&#xff09;向量归一化&#xff08;Normalize&#xff09;其他作用 unity开发中我们要计算角度&#xff0c;判断位置&#xff0c;常用点乘、叉乘、归一化等等&#xff0c;我们看看他们的使用案…

Android 通知简介

Android 通知简介 1. 基本通知 图1: 基本通知详情 小图标 : 必须提供,通过 setSmallIcon( ) 进行设置.应用名称 : 由系统提供.时间戳 : 由系统提供,也可隐藏时间.大图标(可选) : 可选内容(通常仅用于联系人照片,请勿将其用于应用图标),通过setLargeIcon( ) 进行设置.标题 : 可选…

中国社科院大学与美国杜兰大学金融管理硕士项目——熬过寒冬,春日暖阳已不远

在金融领域&#xff0c;寒冬似乎成了无法避免的阶段。然而&#xff0c;对于那些坚守岗位的金融从业者来说&#xff0c;熬过寒冬并非无望。正如冬去春来&#xff0c;只要我们采取明智的策略&#xff0c;迈出坚定的步伐&#xff0c;春日的暖阳已在不远方照耀。社科院与美国杜兰大…

SpringBoot+Hutool实现图片验证码

图片验证码在注册、登录、交易、交互等各类场景中都发挥着巨大作用&#xff0c;能够防止操作者利用机器进行暴力破解、恶意注册、滥用服务、批量化操作和自动发布等行为。 创建一个实体类封装&#xff0c;给前端返回的验证码数据&#xff1a; Data public class ValidateCodeV…

JS栈和堆:数据是如何存储的

JS栈和堆&#xff1a;数据是如何存储的 背景JavaScript 是什么类型的语言JavaScript 的数据类型内存空间栈空间和堆空间再谈闭包 背景 JS有多种数据类型&#xff1a;数字型&#xff0c;字符串型&#xff0c;数组型等&#xff0c;虽然 JavaScript 并不需要直接去管理内存&#…

都2024年了,FP卖家还不知道AB站怎么玩?

自从开始写FP独立站各种运营技巧和黑科技的文章&#xff0c;经常都会有朋友来私V&#xff0c;询问怎么进行AB站跳转。 可能是现在平台对于FP商家的限制越来越多&#xff0c;再加上如今到处都是“内卷”的电商环境&#xff0c;让FP商家生存越来越艰难&#xff0c;今天就着重讲一…