一文带你了解——Spring IoC

目录

一、IoC介绍

二、Bean存储

[2.1 @Controller(控制器存储)](#2.1 @Controller(控制器存储))

[2.1.1 获取bean对象的其他方式](#2.1.1 获取bean对象的其他方式)

[2.1.2 Bean的命名约定](#2.1.2 Bean的命名约定)

[2.2 @Service(服务存储)](#2.2 @Service(服务存储))

[2.3 @Repository(仓库存储)](#2.3 @Repository(仓库存储))

[2.4 @Component(组件存储)](#2.4 @Component(组件存储))

[2.5 @Configuration(配置存储)](#2.5 @Configuration(配置存储))

[2.6 方法注解@Bean](#2.6 方法注解@Bean)

[2.6.1 方法注解的使用](#2.6.1 方法注解的使用)

[2.6.2 定义多个对象](#2.6.2 定义多个对象)

[2.6.3 重命名Bean](#2.6.3 重命名Bean)

[2.7 扫描路径](#2.7 扫描路径)


一、IoC介绍

IoC: Inversion of Control (控制反转),也就是说 Spring 是一个"控制反转"的容器。就是将对象的
控制权交给Spring的IOC容器,由IOC容器创建及管理对象,也就是bean的存储。

二、Bean存储

Spring存储对象的注解有:

  1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration
  2. 方法注解:@Bean

2.1 @Controller(控制器存储)

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

java 复制代码
@Controller // 将对象存储到 Spring 中
public class UserController {
 public void sayHi(){
 System.out.println("hi,UserController...");
 }
}

从Spring容器中获取对象:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 UserController bean = context.getBean(UserController.class);
 //使⽤对象
 bean.sayHi();
 }

ApplicationContext,也就是Spring上下文,Spring的运行环境。

运行结果如下:

2.1.1 获取bean对象的其他方式

ApplicationContext 也提供了其他获取bean的方式, ApplicationContext 获取bean对象的功能, 是父类BeanFactory提供的功能。

java 复制代码
public interface BeanFactory {
 
 //以上省略...
 
 // 1. 根据bean名称获取bean
 Object getBean(String var1) throws BeansException;
 // 2. 根据bean名称和类型获取bean
 <T> T getBean(String var1, Class<T> var2) throws BeansException;
 // 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean
 Object 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种都涉及到根据名称来获取对象。那么bean的名称是什么呢?

2.1.2 Bean的命名约定

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

类名: UserController, Bean的名称为: userController
类名: AccountManager, Bean的名称为: accountManager
类名: AccountService, Bean的名称为: accountService
也有一些特殊情况,当有多个字符并且第一个和第二个字符都是大写时,将保留原始的大写。 这些规则与java.beans.Introspector.decapitalize (Spring在这里使用的)定义的规则相同。比如:
类名: UController, Bean的名称为: UController
类名: AManager, Bean的名称为: AManager

根据这个命名规则,以下方法可以获取Bean:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 //根据bean类型, 从Spring上下⽂中获取对象
 UserController userController1 = context.getBean(UserController.class);
 //根据bean名称, 从Spring上下⽂中获取对象
 UserController userController2 = (UserController)context.getBean("userController");
 //根据bean类型+名称, 从Spring上下⽂中获取对象
 UserController userController3 = context.getBean("userController",UserController.class);
 
 System.out.println(userController1);
 System.out.println(userController2);
 System.out.println(userController3);
 }
}

获取bean对象, 是父类BeanFactory提供的功能。

常见面试题:
ApplicationContext VS BeanFactory

  • 继承关系和功能方面来说:Spring 容器有两个顶级的接口:BeanFactory 和 ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能力,而 ApplicationContext 属于 BeanFactory 的子类,它除了继承了 BeanFactory 的所有功能之外, 它还拥有独特的特性,还添加了对国际化支持、资源访问支持、以及事件传播等方面的支持。
  • 从性能方面来说:ApplicationContext 是一次性加载并初始化所有的 Bean 对象,而BeanFactory 是需要哪个才去加载哪个,因此更加轻量。(空间换时间)

2.2 @Service(服务存储)

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

java 复制代码
@Service
public class UserService {
   public void sayHi(String name) {
       System.out.println("hi,UserService...");
     }
 }

读取 bean 的代码:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = 
SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring中获取UserService对象
 UserService userService = context.getBean(UserService.class);
 //使⽤对象
 userService.sayHi();
 }
}

运行结果如下:

2.3 @Repository(仓库存储)

使用@Repository存储bean的代码如下:

java 复制代码
@Repository
public class UserRepository {
   public void sayHi() {
       System.out.println("Hi,UserRepository...");
     }
 }

读取bean的代码:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 UserRepository userRepository = context.getBean(UserRepository.class);
 //使⽤对象
 userRepository.sayHi();
 }
}

运行结果如下:

2.4 @Component(组件存储)

使用@Component存储bean的代码如下:

java 复制代码
@Component
public class UserComponent {
   public void sayHi() {
       System.out.println("Hi,UserComponent...");
     }
 }

读取bean的代码:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 UserComponent userComponent = context.getBean(UserComponent.class);
 //使⽤对象
 userComponent.sayHi();
 }
}

运行结果如下:

2.5 @Configuration(配置存储)

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

java 复制代码
@Configuration
public class UserConfiguration {
 public void sayHi() {
 System.out.println("Hi,UserConfig...");
  }
}

读取bean的代码:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
 //使⽤对象
 userConfiguration.sayHi();
 }
}

运行结果如下:

NOTE: 为什么要这么多类注解?
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:其实这些注解里面都有一个注解 @Component ,说明它们本身就是属于 @Component 的"子类"。
@Component 是一个元注解,也就是说可以注解其他类注解,如 @Controller , @Service ,
@Repository 等。 这些注解被称为 @Component 的衍生注解。
@Controller , @Service 和 @Repository 用于 更具体的用例(分别在控制层, 业务逻辑层, 持
久化层), 在开发过程中, 如果你要在业务逻辑层使用 @Component 或@Service,显然@Service是更好的选择。

2.6 方法注解@Bean

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

  • 使用外部包里的类,没办法添加类注解;
  • ⼀个类,需要多个对象,比如多个数据源;

这种场景,我们就需要使用方法注解 @Bean。

2.6.1 方法注解的使用

在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中, 如下代码所示:

java 复制代码
@Component
public class BeanConfig {
 @Bean
 public User user(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
 }
}

获取bean对象中的user:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 User user = context.getBean(User.class);
 //使⽤对象
 System.out.println(user);
 }
}

运行结果如下:

2.6.2 定义多个对象

@Bean的使用:

java 复制代码
@Component
public class BeanConfig {
 @Bean
 public User user1(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
 }
 @Bean
 public User user2(){
 User user = new User();
 user.setName("lisi");
 user.setAge(19);
 return user;
 }
}

根据名称来获取bean对象:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = 
SpringApplication.run(SpringIocDemoApplication.class, args);
 //根据bean名称, 从Spring上下⽂中获取对象
 User user1 = (User) context.getBean("user1");
 User user2 = (User) context.getBean("user2");
 System.out.println(user1);
 System.out.println(user2);
 }
}

运行结果如下:


@Bean注解的bean,bean的名称就是它的方法名。

2.6.3 重命名Bean

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

java 复制代码
@Bean(name = {"u1","user1"})
public User user1(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
}

此时我们使用 u1 就可以获取到 User 对象了,如下所示:

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 User u1 = (User) context.getBean("u1");
 //使⽤对象
 System.out.println(u1);
 }
}

2.7 扫描路径

使用五大注解声明的bean,要想生效,还需要配置扫描路径,让Spring扫描到这些注解。也就是通过在启动类中添加 @ComponentScan 来配置扫描路径。默认扫描的范围是SpringBoot启动类所在包及其子包。

java 复制代码
@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 User u1 = (User) context.getBean("u1");
 //使⽤对象
 System.out.println(u1);
 }
}

但是此做法不做推荐,那么推荐做法为:
把启动类放在希望扫描的包的路径下, 这样定义的bean就都可以被扫描到。

相关推荐
suweijie7683 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿3 小时前
List深拷贝后,数据还是被串改
java
xlsw_7 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹7 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭8 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫8 小时前
泛型(2)
java
超爱吃士力架8 小时前
邀请逻辑
java·linux·后端
南宫生8 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石8 小时前
12/21java基础
java
李小白669 小时前
Spring MVC(上)
java·spring·mvc