Spring IoC DI

1. IoC

1.1 容器

容器是用来容纳某种物品的(基本)装置。——来自:百度百科

生活中的水杯,垃圾桶,冰箱等等,都是容器

代码中的容器,如:List/Map -> 数据存储容器;Tomcat -> Web 容器

1.2 IoC

IoC 是 Sping 的核心思想,前面在类上面添加 @RestController 和 @Controller 注解,就是把这个对象交给 Sping 管理,Spring 框架启动时,就会加载该类,把对象交给 Spring 管理,就是 IoC 思想

IoC:Inversion of Control(控制反转),也就是说 Spring 是一个 “控制反转” 的容器

控制反转就是控制权反转,也就是获得依赖对象的过程被反转了

当需要某个对象时,传统开发模式中需要自己通过 new 创建对象,现在不需要再进行创建,把创建对象的任务交给容器,程序中只需要依赖注入(Dependency Injection,DI)就可以了

这个容器称为:IoC 容器,Spring 是一个 IoC 容器,所以有时 Spring 也称为 Spring 容器

控制反转是一种思想,在生活中也是处处体现

⽐如⾃动驾驶, 传统驾驶⽅式,⻋辆的横向和纵向驾驶控制权由驾驶员来控制,现在交给了驾驶⾃ 动化系统来控制,这也是控制反转思想在⽣活中的实现.

⽐如招聘,企业的员⼯招聘,⼊职,解雇等控制权,由⽼板转交给给HR(⼈⼒资源)来处理

1.3 IoC 应用

现有需求:造一辆车

1.3.1 传统程序开发

实现思路:

先设计轮子(Tire),然后根据轮子大小设计底盘(Bottom),接着根据底盘设计车身(Framework),最后根据车身设计好整个汽车(Car)

这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子

实现代码:

1.3.2 问题分析

此时用户说需要生成不同尺寸的轮胎,就需要传参

可以看出程序的问题是:当最底层代码改动之后,整个调用链上的所有代码都需要修改

程序的耦合度非常高

1.3.3 解决方案

在上⾯的程序中,我们是根据轮⼦的尺⼨设计的底盘,轮⼦的尺⼨⼀改,底盘的设计就得修改.同样因 为我们是根据底盘设计的⻋⾝,那么⻋⾝也得改,同理汽⻋设计也得改,也就是整个设计⼏乎都得改

我们尝试换⼀种思路,我们先设计汽⻋的⼤概样⼦,然后根据汽⻋的样⼦来设计⻋⾝,根据⻋⾝来设计 底盘,最后根据底盘来设计轮⼦.这时候,依赖关系就倒置过来了:轮⼦依赖底盘,底盘依赖⻋⾝, ⻋⾝依赖汽⻋

1.3.4 IoC 程序

经过以上调整,无论底层类如何变化,整个调用链不用做任何改变,这样就完成了代码之间的解耦,从而实现了更加灵活、通用的程序设计了

1.3.5 IoC 优势

在传统的代码中对象创建顺序是:Car->Framework->Bottom->Tire

改进之后解耦的代码的对象创建顺序是:Tire->Bottom->Framework->Car

我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是Car控制并创建了 Framework,Framework创建并创建了Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由 当前类控制了.

这样的话,即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是IoC的实现思想

上面就是控制反转的示例,而控制反转容器(IoC 容器)如下图所示

上面容器内的代码就是 IoC 做的工作

从上⾯也可以看出来,IoC容器具备以下优点:

资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集 中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度

  • 资源集中管理: IoC容器会帮我们管理⼀些资源(对象等),我们需要使⽤时,只需要从IoC容器中去取就可以了
  • 我们在创建实例的时候不需要了解其中的细节,降低了使⽤资源双⽅的依赖程度,也就是耦合度

Spring 就是一种 IoC 容器,帮助我们来做了这些资源管理

1.4 DI 介绍

DI:Dependency Injection(依赖注入)

容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入

程序运⾏时需要某个资源,此时容器就为其提供这个资源

从这点来看, 依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,依赖注⼊是 从应⽤程序的⻆度来描述,就是指通过引⼊IoC容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解 耦

上述示例中,是通过构造函数的方式,把依赖对象注入到需要使用的对象中的

IoC 是一种思想,也是“目标”,而思想只是一种指导原则,最终还是要有可行的落地方案,而 DI 就属于具体的实现,所以也可以说 DI 就是 IoC 的一种实现

2. IoC & DI 使用

既然 Spring 是一个 IoC(控制反转) 容器,作为容器,它就具备两个最基础的功能:存 和 取

Spring 容器管理的主要是对象,这些对象被称为 “Bean”,我们把这些对象交由 Spring 管理,由 Spring 来负责对象的创建和销毁,我们的程序代码只需要告诉 Spring 哪些需要存,以及如何从 Spring 中取出对象

以前面写的图书管理系统为例:

目标:把 BookDao、BookService 交给 Spring 管理,完成 Controller 层,Service 层,Dao 层的解耦

步骤:

  1. Service 层及 Dao 层的实现类,交给 Spring 管理,使用注解 @Component
  2. 在 Controller 层和 Service 层注入运行时依赖的对象,使用注解 @Autowired

实现:

1) 把 BookDao 交给 Spring 管理,由 Spring 来管理对象

2) 把 BookService 交给 Spring 管理,由 Spring 来管理对象

3) 删除创建 BookDao 的代码,从 Spring 中获取对象

4) 删除创建 BookService 的代码,从 Spring 中获取对象

5) 使用 Postman 测试

3. IoC 详解

3.1 Bean 的存储

共有两类注解类型可以实现

  • 类注解:@Controller、@Service、@Repository、@Component、@Configuration
  • 方法注解:@Bean

3.1.1 @Controller(控制器存储)

在传统代码中:

想在 main 中调用 UserController 中的 say 方法需要如下图

使用 @Controller 存储 bean 的代码如下所示:

当我们将 @Controller 注释掉后:

ApplicationContext 翻译过来就是:Spring 上下文

因为对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就得先得到 Spring 的上下文

获取 bean 对象的其他方式

上述时根据类型来查找对象,ApplicationContext 也提供了其他获取 bean 的方式,ApplicationContext 获取 bean 对象的功能,是父类 BeanFactory 提供的功能

public interface BeanFactory {// 以上省略...// 1. 根据 bean 名称获取 beanObject getBean(String var1) throws BeansException;// 2. 根据 bean 名称和类型获取 bean<T> T getBean(String var1, Class<T> var2) throws BeansException;// 3. 按 bean 名称和构造函数参数动态创建 bean,只适用于具有原型(prototype)作用域的 beanObject getBean(String var1, Object... var2) throws BeansException;// 4. 根据类型获取 bean<T> T getBean(Class<T> var1) throws BeansException;// 5. 按 bean 名称和构造函数参数动态创建 bean,只适用于具有原型(prototype)作用域的 bean<T> T getBean(Class<T> var1, Object... var2) throws BeansException;// 以下省略...
}

常用的是上述 1、2、4 三种方式,获取到的 bean 都是一样的

其中 1、2 都涉及到根据名称来获取对象,在 Spring 中会给每个对象起一个名字,根据 Bean 的名称(BeanId)就可以获取到对应的对象(类似于每一个学生都有唯一的学号)

第 4 种方式只适合该类只有一个对象的情况

Bean 命名约定:

官方文档说明:BeanOverview::SpringFramework

程序开发人员不需要为 bean 指定名称(BeanId),如果没有显式的提供名称(BeanId),Spring 容器将为该 bean 生成唯一的名称

命名约定使用 Java 标准约定作为实例字段名,也就是说,bean 名称以小写字母开头,然后使用驼峰式大小写

比如:

类名:UserController,Bean 的名称为:userController

类名:AccountManager,Bean 的名称为:accountManager

类名:AccountService,Bean 的名称为:accountService

也有一些特殊情况,当有多个字符并且第一个和第二个字符都是大写时,将保留原始的大小写,这些规则与 java.beans.Introspector.decapitalize(Spring 在这里使用的)定义的规则相同

比如:

类名:UController,Bean 的名称为:UController

类名:AManager,Bean 的名称为:AManager

根据这个命名规则来获取 Bean:

import com.example.springioc.controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;@SpringBootApplication
public class SpringIoCApplication {public static void main(String[] args) {// 获取 Spring 上下文对象ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args); // ApplicationContext可以理解为 Spring 的容器// 方法一// 从 Spring 上下文中获取对象UserController bean = context.getBean(UserController.class);// 使用对象bean.say();// 方法二UserController userController = (UserController)context.getBean("userController");userController.say();// 方法三UserController userController1 = context.getBean("userController", UserController.class);userController1.say();}
}

运行结果:

打印这三个对象:

地址一样,说明对象是同一个(单例模式)

3.1.2 其他注解演示:

3.2 为什么这么多类注解

这个也是和前面说的应用分层是呼应的,让程序员看到类注解之后,就能直接了解当前类的用途

  • @Controller:控制层,接收请求,对请求进行处理,并进行响应
  • @Service:业务逻辑层,处理具体的业务逻辑
  • @Repository:数据访问层,也成为持久层,负责数据访问操作
  • @Configuration:配置层,处理项目中的一些配置信息

程序的应用分层调用流程如下:

类注解之间的关系

查看 @Controller / @Service / @Repository / @Configuration 等注解的源码:

其实这些注解里面都有一个注解 @Component,说明它们本身就属于注解 @Component 的“子类”

@Component 是一个元注解,可以用来注解其他类注解

@Controller、@Service、#Repository 等,这些注解被称为 @Component 的衍生注解

3.3 方法注解 @Bean

类注解是添加到某个类上的,但是存在两个问题:

  1. 使用外部包里的类,没办法添加类注解
  2. 一个类,需要多个对象,比如:多个数据源

上面场景下,就需要使用方法注解 @Bean

下面假设一个 UserInfo 类,是第三方包中的类,无法直接在其中添加类注解:

import lombok.Data;@Data
public class UserInfo {private String name;private Integer age;
}

tip:创建该对象的三种方法:

①:调用 set 方法

public class UserInfoComponent {public UserInfo userInfo() {UserInfo userInfo = new UserInfo();userInfo.setName("zhangsan");userInfo.setAge(18);return userInfo;}
}

②:构建构造方法

@Data
public class UserInfo {private String name;private Integer age;public UserInfo(String name, Integer age) {this.name = name;this.age = age;}
}

③:使用 lombok 中的注解生成构造方法(和 ② 一样,只不过使用注解实现)

@AllArgsConstructor // 全参构造函数
@NoArgsConstructor // 无参构造函数
@Data
public class UserInfo {private String name;private Integer age;
}

如下方法构造了该对象:

import com.example.springioc.model.UserInfo;public class UserInfoComponent {public UserInfo userInfo() {return new UserInfo("zhangsan", 18);}
}

想要将该方法创建的对象交给 Spring 管理,只需给该方法添加 @Bean 注解即可

public class UserInfoComponent {@Beanpublic UserInfo userInfo() {return new UserInfo("zhangsan", 18);}
}

测试能否获取该对象:

发现报错了,没有找到该对象

3.3.1 方法注解要配合类注解使用

这是因为 @Bean 需要配合五个类注解使用,任意一个均可:

@Component
public class UserInfoComponent {@Beanpublic UserInfo userInfo() {return new UserInfo("zhangsan", 18);}
}

加上类注解时候,Spring 才知道它需要帮我们管理该对象,管理该类中的 @Bean 方法

再次运行:

3.3.2 定义多个对象

@Component
public class UserInfoComponent {@Beanpublic UserInfo userInfo() {return new UserInfo("zhangsan", 18);}@Beanpublic UserInfo userInfo1() {return new UserInfo("lisi", 20);}
}

运行:

错误提示为:期望有一个对象,结果找到两个

这是因为,我们是根据类型来找的对象,我们可以改为根据名称(该名称与方法名一致)来找:

tip:也可以根据 名称 + 类型 的方式来获取

3.3.3 重命名 Bean

可以通过设置 name 属性给 Bean 对象进行重命名操作,如下:

查看 @Bean 源码:

可以看到名称存放于一个数组中,因此可以给 Bean 命名多个名称:

    @Bean(name = {"zhangsan", "userInfo"})public UserInfo userInfo() {return new UserInfo("zhangsan", 18);}@Bean("lisi")public UserInfo userInfo1() {return new UserInfo("lisi", 20);}

当只重命名一个名称时,name= 可以省略,如上 userInfo1

3.4 扫描路径

使用 @Controller、@Service、@Repository、@Component、@Configuration 声明的 bean 生效的前提条件是:能够被 Spring 扫描到,如下例:

再次运行前面已经成功的代码

错误信息是找不到名称为 'zhangsan' 的对象

这是就是因为 Spring 没有扫描到,需要配置扫描路径,让 Spring 扫描到这些注解

可以通过 @ComponnetScan 来配置路径,如下:

这种做法仅了解,不推荐使用

tip:为什么前面没有配置 @ComponentScan 注解也可以呢?

@ComponentScan 注解虽然没有显式配置,但是实际上已经包含在启动类声明注解 @SpringBootApplication 中了

默认扫描的范围是 SpringBoot 启动类所在的包及其子包

推荐做法:把启动类放在我们希望扫描的包的路径下,这样我们定义的 bean 都能被扫描到

4. DI 详解

DI 即依赖注入(Dependency Injection),依赖注入是一个过程,是指 IoC 容器在创建 Bean 时,去提供运行时所依赖的资源,而资源指的就是对象(简单来说就是把对象取出来放到某个类的属性中)

在上面示例中,使用了 @Autowired 这个注解,完成了依赖注入的操作

关于依赖注入,Spring 给我们提供了三种方式:

  • 属性注入(FieldInjection)
  • 构造方法注入(ConstructorInjection)
  • Setter 注入(SetterInjection)

4.1 属性注入

使用 @Autowired 将 Service 类注入到 Controller 类中

Service 类实现代码如下:

import org.springframework.stereotype.Service;@Service
public class UserService {public void say() {System.out.println("UserService say...");}
}

Controller 类的实现代码如下:

import com.example.springioc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller // 该注解帮我们 new 的对象就放在容器中(ApplicationContext)
public class UserController {@Autowiredprivate UserService userService;public void say() {System.out.println("UserController say...");userService.say();}
}

获取 Controller 中的 say() 方法:

@SpringBootApplication
public class SpringIoCApplication {public static void main(String[] args) {// 获取 Spring 上下文对象ApplicationContext context = SpringApplication.run(SpringIoCApplication.class, args); // ApplicationContext可以理解为 Spring 的容器// 从 Spring 上下文中获取对象UserController bean = context.getBean(UserController.class);// 使用对象bean.say();}
}

运行结果:

若是去掉 @Autowired,再运行:

4.2 构造方法注入

构造方法注入是在类的构造方法中实现注入,如下:

若是类中有多个构造方法时:

这是因为我们将该对象交给 Spring 管理时,它会 new 一个对象,但是有多个构造方法时,它默认调用无参的构造方法,通过打印展示:

此时需要在希望 Spring 调用的构造方法前面加上 @Autowired 来指定

tip:上面使用的是有参数的构造方法,但是我们并没有给它传入参数,参数哪来的?

是因为我们交给 Spring 管理的对象,如果有参数,这个参数可以自己指定,如果未指定,Spring 会根据名称或者类型,从容器中查找对象,并注入进来,如下例:

4.3 Setter 注入

Setter 注入和属性的 Setter 方法实现类似,只不过在设置 set 方法的时候需要加上 @Autowired 注解,如下:

    private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}

也需要加上 @Autowired,否则会报空指针异常

4.4 三种注入优缺点分析

属性注入

优点:简洁,使用方便

缺点:

  • 只能用于 IoC 容器,非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)
  • 不能注入一个 Final 修饰的属性

构造函数注入(Spring 4.X 推荐,注意此处是 Spring 版本,不是 Spring Boot 版本)

优点:

  • 可以注入 Final 修饰的属性
  • 注入的对象不会被修改
  • 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法
  • 通用性好,构造方法是 JDK 支持的,所以更换任何框架都适用

缺点:注入多个对象时,代码会比较繁琐


Setter 注入(Spring 3.X 推荐)

优点:方便在类实例之后,重新对该对象进行配置或者注入

缺点:

  • 不能注入一个 Final 修饰的属性
  • 注入对象可能会被修改,因为 Setter 方法可能会被多次调用,有被修改的风险

4.5 @Autowired 存在问题

同一类型有多个对象,按照名称注入:

若该类型只有一个对象,则直接注入(不管名称是否一样):

若是同一类型有多个对象,并且名称匹配不上,就会造成启动报错:

为解决上述问题,Spring 提供了以下几种解决方案:

  • @Primary(Spring 提供)
  • @Qualifier(Spring 提供)
  • @Resource(JDK提供)

1. 使用 @Primary 注解:

当存在多个相同类型的 Bean 注入时,加上 @Primary 注解,来确定默认的实现

2. 使用 @Qualifier 注解:

指定当前要注入的 bean 对象,在 @Qualifier 的 value 属性中,指定注入的 bean 名称

@Qualifier 注解不能单独使用,必须配合 @Autowired 使用

当出现下面情况时:

若同时使用了方案一和方案二,则 @Qualifier 优先级更高,如下运行结果:

3. 使用 @Resource 注解:

是按照 bean 的名称进行注入,通过 name 属性指定要注入的 bean 的名称

4.6 常见面试题:@Autowired 和 @Resource 的区别

1. @Autowired 是根据类型匹配,@Resource 是根据名称匹配

两者基本原则都是根据类型匹配,但是 @Autowired 不能指定名称

2. @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解

tip:Autowired 的装配顺序

5. 总结

5.1 Spring、Spring Boot、Spring MVC 的关系及区别

Spring: 简单来说,Spring是⼀个开发应⽤框架,什么样的框架呢,有这么⼏个标签:轻量级、⼀站式、模块化,其⽬的是⽤于简化企业级应⽤程序开发.

Spring的主要功能:管理对象,以及对象之间的依赖关系,⾯向切⾯编程,数据库事务管理,数据访 问,web框架⽀持等.

但是Spring具备⾼度可开放性,并不强制依赖Spring,开发者可以⾃由选择Spring的部分或者全 部,Spring可以⽆缝继承第三⽅框架,⽐如数据访问框架(Hibernate、JPA),web框架(如Struts、 JSF)

Spring MVC:SpringMVC是Spring的⼀个⼦框架,Spring诞⽣之后,⼤家觉得很好⽤,于是按照MVC 模式设计了⼀个MVC框架(⼀些⽤Spring解耦的组件),主要⽤于开发WEB应⽤和网络接⼝,所以, Spring MVC是⼀个Web框架.

Spring MVC基于Spring进⾏开发的,天⽣的与Spring框架集成.可以让我们更简洁的进⾏Web层 开发,⽀持灵活的URL到⻚⾯控制器的映射,提供了强⼤的约定⼤于配置的契约式编程⽀持,⾮常 容易与其他视图框架集成,如Velocity、FreeMarker等

Spring Boot: Spring Boot是对Spring的⼀个封装,为了简化Spring应⽤的开发⽽出现的,中⼩型 企业,没有成本研究⾃⼰的框架,使⽤SpringBoot可以更加快速的搭建框架,降级开发成本,让开发 ⼈员更加专注于Spring应⽤的开发,⽽⽆需过多关注XML的配置和⼀些底层的实现.

Spring Boot 是个脚⼿架,插拔式搭建项⽬,可以快速的集成其他框架进来.

⽐如想使⽤SpringBoot开发Web项⽬,只需要引⼊SpringMVC框架即可,Web开发的⼯作是 SpringMVC完成的,⽽不是SpringBoot,想完成数据访问,只需要引⼊Mybatis框架即可.

Spring Boot只是辅助简化项⽬开发的,让开发变得更加简单,甚⾄不需要额外的web服务器,直接 ⽣成jar包执⾏即可.

最后⼀句话总结:SpringMVC和SpringBoot都属于Spring,SpringMVC是基于Spring的⼀个 MVC框架,⽽SpringBoot是基于Spring的⼀套快速开发整合包.

5.2 常见注解有哪些?分别是什么作用?

Web url 映射:@RequestMapping

参数接收和接口响应:@RequestParam、@RequestBody、@ResponseBody

bean 的存储:@Controller、@Service、@Repository、@Component、@Configuration、@Bean

bean 的获取:@Autowired、@Primary、@Qualifier、@Resource

@RestController:

  1. 标识一个类为 RESTful Web 服务的控制器,被 Spring 容器自动识别管理。
  2. 自动将方法返回值转换为 HTTP 响应体,无需再使用@ResponseBody注解,可将对象序列化为 JSON 或 XML 格式返回给客户端。
  3. 支持多种 HTTP 方法,通过在方法上使用特定注解可指定处理的请求路径和方法。

@GetMapping:

  1. 标注方法处理 HTTP GET 请求,当客户端发送 GET 请求到指定路径时执行该方法。
  2. @RequestMapping(method = RequestMethod.GET)的简化形式,简化请求映射,提高开发效率。

@PostMapping:

  1. 标注方法处理 HTTP POST 请求,当客户端发送 POST 请求到指定路径时执行该方法。
  2. 通常用于创建新资源的操作,如在用户管理系统中处理用户注册请求。

@PathVariable

  1. 获取路径中的变量值:当你的 RESTful API 的 URL 中包含动态部分时,可以使用@PathVariable来获取这个动态部分的值。例如,假设你有一个 URL /users/{userId},其中{userId}是一个路径变量。使用@PathVariable("userId")可以将这个路径变量的值提取出来,传递给处理该请求的方法。
  2. 增强 API 的灵活性:通过使用路径变量,可以创建更加灵活的 API。不同的资源可以通过不同的路径变量来区分,而不需要为每个资源都定义一个独立的 URL。例如,可以使用路径变量来表示不同的用户 ID、订单 ID 等,从而实现对特定资源的访问。
  3. 简化参数传递:使用@PathVariable可以避免在 URL 中使用查询参数来传递变量值,使 URL 更加简洁和直观。同时,也可以减少方法参数的数量,使代码更加清晰易读。

@RequestPart:

  1. 用于处理多部分请求中的特定部分,可接收包含文件和其他数据的复杂请求,支持多种媒体类型,如接收 JSON 数据或图片文件等。
  2. @RestController和 Spring 的请求处理机制配合使用,在 Spring MVC 框架中实现灵活的请求处理,方便文件上传和数据处理集成,可在控制器方法中用多个带此注解的参数接收不同部分数据进行处理

@CookieValue:

  1. 可从请求中方便地提取特定 Cookie 的值并绑定到方法参数上,简化 Cookie 处理逻辑。
  2. 用于在 Web 应用中实现个性化功能,如基于 Cookie 进行用户认证和状态跟踪,还能实现跨页面的数据传递。

@RequestHeader:

  1. 用于从 HTTP 请求中提取特定请求头的值并绑定到方法参数上,可根据不同请求头进行灵活的业务逻辑处理,比如根据Content-Type确定如何解析请求体数据。
  2. 可实现安全验证和授权,如通过获取Authorization请求头进行身份验证;还能进行内容协商和响应定制,根据Accept请求头返回客户端期望的响应格式。

@ComponentScan:

  1. 自动扫描指定包及其子包,发现并注册如@Controller@Service等标注的自定义组件,简化组件配置,避免手动在 XML 配置文件中逐个注册的繁琐过程。
  2. 构建应用的对象图,建立组件之间的依赖关系,实现依赖注入,同时促进松散耦合架构,提高代码可维护性和可测试性。

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

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

相关文章

Python异常检测 - LSTM(长短期记忆网络)

系列文章目录 Python异常检测- Isolation Forest&#xff08;孤立森林&#xff09; python异常检测 - 随机离群选择Stochastic Outlier Selection (SOS) python异常检测-局部异常因子&#xff08;LOF&#xff09;算法 Python异常检测- DBSCAN Python异常检测- 单类支持向量机(…

DolphinDB 与南方科技大学联合授课啦!

11月1日&#xff0c;南方科技大学商学院和 DolphinDB 联合举办了高校课程讲座。讲座由南方科技大学商学院高级研究学者冯鹏举主持&#xff0c;DolphinDB 创始人兼 CEO 周小华博士、某百亿私募数据平台架构师潜蛟老师进行精彩演讲。 Part 1 : 大数据时代下数据库架构革新与生态…

3.PyCharm工具

第三方IDE&#xff0c;集成开发工具&#xff0c;官网下载。 社区版本&#xff0c;免费使用。 创建项目

微信小程序——用户隐私保护指引填写(详细版)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【在Linux世界中追寻伟大的One Piece】poll代码改写

目录 1 -> poll代码改写 1 -> poll代码改写 结合select代码&#xff0c;将select server更改成为pollserver&#xff0c;不是一件困难的事情。 #pragma once#include <iostream> #include <string> #include <poll.h> #include <memory> #inc…

安利一款开源企业级的报表系统SpringReport

SpringReport是一款企业级的报表系统&#xff0c;支持在线设计报表&#xff0c;并绑定动态数据源&#xff0c;无需写代码即可快速生成想要的报表&#xff0c;可以支持excel报表和word报表两种格式&#xff0c;同时还可以支持excel多人协同编辑&#xff0c;后续考虑实现大屏设计…

【11月10日最新】V2.6.1版本植物大战僵尸杂交版分享与下载

&#x1f447;下载链接&#xff1a; 点击下载 更新内容 植物大战僵尸杂交版2.6.1版本的更新内容主要包括以下几个方面&#xff1a; 梦幻联动&#xff1a; 与UP主轻柔北风合作&#xff0c;推出了“植物大战僵尸贴吧版”。联动植物包括石果子与雷蘑菇杂交的雷果子&#xff0c;…

Jenkins找不到maven构建项目

有的可能没有出现maven这个选项 解决办法&#xff1a;需要安装Maven项目插件 输入​Maven Integration plugin​

路过宝安乌石岩庙记

​每周带娃从上屋地铁去罗租大道的七彩城堡儿童乐园玩&#xff0c;路上都会经过乌石岩庙附近。听说香火很繁盛&#xff0c;娃说也想去看看&#xff0c;于是来到了乌石岩庙。 石岩乌石岩庙 广东省深圳市宝安区老街一区94号 ​从百度知悉&#xff1a;乌石岩庙&#xff0c;又称“…

练习LabVIEW第四十四题

学习目标&#xff1a; 计算学生三门课(语文&#xff0c;数学&#xff0c;英语)的平均分&#xff0c;并根据平均分划分成绩等级。要求输出等级A,B,C,D,E。90分以上为A&#xff0c;80&#xff5e;89为B&#xff0c;70&#xff5e;79为C&#xff0c;60&#xff5e;69为D&#xff…

软考高级架构 - 8.1 - 系统质量属性与架构评估 - 超详细讲解+精简总结

第8章 系统质量属性与架构评估 软件系统属性包括功能属性和质量属性&#xff0c;而软件架构重点关注质量属性。 8.1 软件系统质量属性 8.1.1 概述 软件系统的质量反映了其与需求的一致性&#xff0c;即&#xff1a;软件系统的质量高低取决于它是否能满足用户提出的需求&#…

最详细【Elasticsearch】Elasticsearch Java API + Spring Boot集成 实战入门(基础篇)

Elasticsearch Java API Spring Boot集成 实战入门&#xff08;基础篇&#xff09; 一、初始Elasticseach1、什么是Elasticseach2、Elasticsearch生态2、Elasticsearch结构3、Elasticsearch核心概念4、Elasticsearch 实现全文检索的原理 二、Elasticsearch入门1、入门-环境安装…

类文件结构详解

回顾一下字节码 在 Java 中&#xff0c;JVM 可以理解的代码就叫做字节码&#xff08;即扩展名为 .class 的文件&#xff09;&#xff0c;它不面向任何特定的处理器&#xff0c;只面向虚拟机。Java 语言通过字节码的方式&#xff0c;在一定程度上解决了传统解释型语言执行效率低…

RabbitMQ的应用

七种工作模式介绍 1.Simple(简单模式) P&#xff1a;生产者&#xff0c;也就是要发送信息的程序 C&#xff1a;消费者&#xff0c;消息的接收者 Queue&#xff1a;消息队列。图中黄色背景部分&#xff0c;类似一个邮箱&#xff0c;可以缓存发送信息&#xff1b;生产者向其中…

科研绘图系列:R语言组合堆积图(stacked plot)

文章目录 介绍加载R包数据数据预处理画图1画图2组合图形系统信息介绍 堆积图(Stacked Chart),也称为堆叠图,是一种常用的数据可视化图表,主要用于展示不同类别的数据量在总体中的分布情况。堆积图可以是柱状图、条形图或面积图的形式,其中各个类别的数据量被叠加在一起,…

测试概念以及测试bug

关于测试的概念 什么是需求&#xff1f; 需求分为用户需求和软件需求。 软件需求可以作为开发和测试工作的依据&#xff0c;而用户需求不一定是合理的&#xff0c;这里的不合理有很多的角度&#xff1a;技术角度上&#xff0c;市场需求上&#xff0c;投入成本和收益比噔噔。…

单体架构的 IM 系统设计

先直接抛出业务背景&#xff01; 有一款游戏&#xff0c;日活跃量&#xff08;DAU&#xff09;在两千左右&#xff0c;虽然 DAU 不高&#xff0c;但这两千用户的忠诚度非常高&#xff0c;而且会持续为游戏充值&#xff1b;为了进一步提高用户体验&#xff0c;继续增强用户的忠…

Node.js——fs模块-文件删除

1、在Node.js中&#xff0c;我们可以使用unlink或unlinkSync来删除文件。 2、语法&#xff1a; fs.unlink(path,callback) fs.unlinkSync(path) 参数说明&#xff1a; path 文件路径 callback 操作后的回调函数 本文的分享到此结束&#xff0c;欢迎大家评论区一同讨论学…

Halcon 从XML中读取配置参数

1、XML示例 以下是一个XML配置文件的示例,该文件包含了AOI(自动光学检测)算法的环境参数和相机逻辑参数: <AOI><!--AOI算法参数 20241106--><Env><!--环境参数--><Param name="GPUName" value="NVIDIA GeForce RTX 405…

数据冒险-add x1, x1, x2 add x1, x1, x3 add x1, x1, x4

第一张图没有传递机制 竞争情况分析 读后写&#xff08;RAW&#xff09;竞争&#xff1a;当某条指令需要读取一个寄存器的值&#xff0c;而该寄存器的值尚未被前面的指令写入时&#xff0c;就会发生这种竞争。 指令2&#xff08;dadd r1, r1, r3&#xff09;依赖于指令1&#…