「JavaEE」Spring IoC 1:Bean 的存储

🎇个人主页

🎇所属专栏:Spring

🎇欢迎点赞收藏加关注哦!
 


IoC 简介

IoC 全称 Inversion of Control,即控制反转

控制反转是指控制权反转:获得依赖对象的过程被反转了

传统开发模式中,需要某个对象时,我们要自己通过 new 来创建对象, 现在无需自行创建, 而是把创建对象的任务交给容器, 程序中只需依赖注入 (Dependency Injection, 简称 DI) 就可以了。这个容器称为 IoC 容器

Spring 是一个 IoC 容器, 所以 Spring 有时也被称为 Spring 容器。作为容器, 它具备两个最基础的功能:存和取

Spring 容器管理的对象我们称为 Bean 。我们把 Bean 交给 Spring 管理, 由它负责对象的创建和销毁。我们的程序只需告诉 Spring,哪些需要存, 以及如何从 Spring 中取出对象


Bean 的存储

有两类注解类型可以实现存储

  1. 类注解:@Controller@Service@Repository@Component@Configuration

  2. 方法注解:@Bean

@Controller (控制器存储)

@Controller // 将对象存储到 Spring 中
public class UserController { 
    public void Hello(){
        System.out.println("Hello UserController"); 
    }
}  

要确认对象是否已经存到 Spring 容器中,我们可以试试能否从 Spring 中取出 Bean,那就得先得到 Spring 的上下文,即 ApplicationContext

@SpringBootApplication
public class SpringIoCDemoApplication {
    public static void main(String[] args) {
        //获取 Spring 上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCDemoApplication.class, args);
        //从上下文中获取 Bean
        UserController userController = context.getBean(UserController.class);
        //使用 Bean
        userController.Hello();
    }
}

观察运行结果:

如果把 @Controller 去掉的话,运行时会抛异常:

这个异常意思是说没有找到你要的 Bean,说明去掉注解后就没有 UserController 类的实例,说明没存进 Spring

上面代码是根据类型查找对象

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

public interface BeanFactory {
    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    
    //其他成员方法已省略
}

常用的是第 1、2、4 种,这三种方式获取到的bean是一样的。其中第一、二种根据名称获取对象。Spring 会给每个对象起一个名字, 根据 Bean 的名称(BeanId)就可以获取到对应的对象

命名规则

默认情况下是类名的小驼峰表示。如果类名的前两位均为大写,那么 Bean 的名称就是类名本身

举个例子

类名:UserController Bean 名称为:userController

类名: UController Bean的名称为: UController

@SpringBootApplication
public class SpringIoCDemoApplication {
    public static void main(String[] args) {
        //获取 Spring 上下文对象
        ApplicationContext context = SpringApplication.run(SpringIoCDemoApplication.class, args);
        UserController userController1 = context.getBean(UserController.class);
        UserController userController2 = (UserController) context.getBean("userController");
        UserController userController3 = context.getBean(UserController.class,"userController");
        System.out.println(userController1);
        System.out.println(userController2);
        System.out.println(userController3);
    }
}

运行后发现地址都是一样的,说明是同一个对象


@Service(服务存储)

直接把上述代码的 @Controller 改为 @Service

@Service
public class UserController {
    public void Hello() {
        System.out.println("Hello UserController");
    }
}

观察运行结果,发现成功从 Spring 中获取到 UserService 对象, 并执行 UserService 的 Hello 方法

同理,换成 @Repository(仓库存储)、@Component(组件存储)、@Configuration(配置存储)还是可以得到一样的结果。这样看来的话,这些注解的作用是差不多的,那还为什么要弄这么多类注解?


类注解的分类

这个也是和我们前面讲的应用分层相呼应。目的是让程序员看到类注解之后,就能直接了解当前类的用途,类注解的用途如下:

  • @Controller:控制层, 接收请求, 处理请求并响应

  • @Servie:业务逻辑层, 处理具体的业务逻辑

  • @Repository:数据访问层,也称为持久层,负责数据访问操作

  • @Configuration:配置层. 处理项目中的一些配置信息

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

观察 @Controller / @Service / @Repository / @Configuration 等注解的源码可以发现这些注解里面都有 @Component

@Component 是一个元注解,可以注解其他类注解。如 @Controller@Service@Repository 等. 这些注解称为 @Component衍生注解

@Controller , @Service 和 @Repository 用于更具体的用例,这三个分别在控制层, 业务逻辑层, 持久化层


方法注解 @Bean

方法注解 @Bean 要配合类注解才能将对象存储到 Spring 容器中

@Componen
public class BeanConfig { 
     @Bean 
     public User user(){ 
         User user = new User(); 
         user.setName("zhangsan"); 
         user.setAge(18); 
         return user; 
     } 
}

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

运行结果如下:

@Bean 注解的 Bean 名称就是它的方法名,如果同个类有多个对象,我们通过 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; 
     } 
}

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

运行结果:


扫描路径

Bean 要想生效,还需要被 Spring 扫描

通过修改目录结构来测试 Bean 对象是否生效:

@SpringBootApplication
public class ApplicationControllerApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(ApplicationControllerApplication.class, args);
        Student s1 = (Student) context.getBean("student1");
        Student s2 = (Student) context.getBean("student2");
        System.out.println(s1);
        System.out.println(s2);
    }
}

运行时抛出异常:没有找到 Bean 对象

使用五大注解声明的 bean,要想生效, 还需要配置扫描路径, 让 Spring 扫描到这些注解,通过 @ComponentScan 进行配置

@SpringBootApplication
@ComponentScan({"com.example.demo"})
public class ApplicationControllerApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(ApplicationControllerApplication.class, args);
        Student s1 = (Student) context.getBean("student1");
        Student s2 = (Student) context.getBean("student2");
        System.out.println(s1);
        System.out.println(s2);
    }
}

配置之后再次运行,就可以正常跑起来了:

加了 @ComponentScan 注解的类,当它需要用到某个依赖时,它就会去 @ComponentScan 所指的路径中找

一开始没配置扫描路径也可以运行是因为:虽然没有显式配置 @ComponentScan,但它实际上已经包含在了启动类声明注解 @SpringBootApplication 中了,默认扫描的范围是 SpringBoot 启动类所在包及其子包

建议把启动类放在我们希望扫描的包的路径下, 这样我们定义的 bean 就都可以被扫描到

  • 32
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值