一文带你了解——Spring IoC

目录

一、IoC介绍

二、Bean存储

2.1 @Controller(控制器存储)

2.1.1 获取bean对象的其他方式

2.1.2 Bean的命名约定

2.2 @Service(服务存储)

2.3 @Repository(仓库存储)

2.4 @Component(组件存储)

2.5 @Configuration(配置存储)

2.6 方法注解@Bean

2.6.1 方法注解的使用

2.6.2 定义多个对象

2.6.3 重命名Bean

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 的代码如下所示:
@Controller // 将对象存储到 Spring 中
public class UserController {
 public void sayHi(){
 System.out.println("hi,UserController...");
 }
}

从Spring容器中获取对象:

@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提供的功能。
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:

@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 的代码如下所示:

@Service
public class UserService {
   public void sayHi(String name) {
       System.out.println("hi,UserService...");
     }
 }
读取 bean 的代码:
@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的代码如下:

@Repository
public class UserRepository {
   public void sayHi() {
       System.out.println("Hi,UserRepository...");
     }
 }

读取bean的代码:

@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的代码如下:

@Component
public class UserComponent {
   public void sayHi() {
       System.out.println("Hi,UserComponent...");
     }
 }

读取bean的代码:

@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 的代码如下所示:
@Configuration
public class UserConfiguration {
 public void sayHi() {
 System.out.println("Hi,UserConfig...");
  }
}

读取bean的代码:

@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 容器中, 如下代码所示:
@Component
public class BeanConfig {
 @Bean
 public User user(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
 }
}

获取bean对象中的user:

@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的使用:
@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对象:
@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 对象进行重命名操作,如下代码所示:
@Bean(name = {"u1","user1"})
public User user1(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
}
此时我们使用 u1 就可以获取到 User 对象了,如下所示:
@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启动类所在包及其子包。
@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就都可以被扫描到。
  • 21
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值