总结 Spring 中存储 Bean 的相关注解以及这些注解的用法.

目录

1 Bean的存储

1.1 @Controller(控制器存储)

1.2 @Service(服务存储)

1.3 @Repository(仓库存储) 

1.4 @Component(组件存储)

1.5 @Configuration(配置存储)

2 为什么要这么多类注解?

3 ⽅法注解@Bean

3.1 ⽅法注解要配合类注解使⽤

3.2 定义多个对象

3.3 Bean传递参数

3.4 重命名Bean

4 扫描路径

5 总结


在了解Bean的存储之前, 需要先知道IOC这个思想, IOC就是将控制权反转, 将创建依赖对象的权利交给Spring, 而不是原来的使用方对象来创建依赖对象了. 那么将创建对象的控制权交给Spring的IOC容器后, 就由IOC容器来创建并管理对象, 这也就是Bean的存储

1 Bean的存储

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

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

2. ⽅法注解:@Bean.

1.1 @Controller(控制器存储)

使⽤@Controller 存储bean的代码如下所⽰:

@Controller //将对象存储在Spring中
public class UserController {

    public void sayHi() {
        System.out.println("hi,UserController...");
    }
}

如何观察这个对象已经存在Spring容器当中了呢?

接下来我们学习如何从Spring容器中获取对象

@SpringBootApplication
public class IocDemoApplication {

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

观察运⾏结果,发现成功从Spring中获取到Controller对象,并执⾏了Controller的sayHi方法.

如果把@Controller删掉,再观察运⾏结果

也就是Spring找不到这个对象, 因为没有将这个bean存储在Spring中.

获取bean对象的其他⽅式

上述代码是根据类型来查找对象,如果Spring容器中, 同⼀个类型存在多个bean的话, 怎么来获取呢? ApplicationContext 也提供了其他获取bean的⽅式, ApplicationContext获取bean对象的功能, 是⽗ 类BeanFactory提供的功能

常用的获取bean的方式是通过bean的名称获取, bean的类型获取, bean的名称和类型一起获取.

这其中涉及到了bean的名称来获取对象, 那么bean的名称是什么呢?

Spring bean是Spring框架在运⾏时管理的对象, Spring会给管理的对象起⼀个名字.

⽐如学校管理学⽣, 会给每个学⽣分配⼀个学号, 根据学号, 就可以找到对应的学⽣. Spring也是如此, 给每个对象起⼀个名字, 根据bean的名称(beanId)就可以获取到对应的对象.

程序开发⼈员不需要为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.

@SpringBootApplication
public class IocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(IocDemoApplication.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);
    }
}

 地址⼀样, 说明对象是同一个.

1.2 @Service(服务存储)

使⽤@Service 存储bean的代码如下所⽰:

@Service
public class UserService {
    public void sayHi(String name) {
        System.out.println("Hi," + name);
    }
}

读取bean的代码:

@SpringBootApplication
public class IocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(IocDemoApplication.class, args);
        UserService userService = context.getBean(UserService.class);
        userService.sayHi();
    }
}

观察运⾏结果,发现成功从Spring中获取到UserService对象,并执⾏UserService的sayHi⽅法 

1.3 @Repository(仓库存储) 

使⽤ @Repository 存储bean的代码如下所⽰:

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

@SpringBootApplication
public class IocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(IocDemoApplication.class, args);
        UserRepository userRepository = context.getBean(UserRepository.class);
        userRepository.sayHi();
    }
}

读取bean的代码和上面的基本差不多.

1.4 @Component(组件存储)

使⽤@Component 存储bean的代码如下所⽰:

@Component
public class UserComponent {
    public void doUserComponent() {
        System.out.println("do UserComponent....");
    }
}

读取bean的代码和上面的基本差不多, 就不写了

1.5 @Configuration(配置存储)

使⽤@Configuration 存储bean的代码如下所⽰:

@Configuration
public class UserConfig {
    public void doUserConfig() {
        System.out.println("do UserConfig...");
    }
}

读取bean的代码和上面的基本差不多, 就不写了.

2 为什么要这么多类注解?

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

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

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

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

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

这和每个省/市都有⾃⼰的⻋牌号是⼀样的. ⻋牌号都是唯⼀的,标识⼀个⻋辆的.但是为什么还需要设置不同的⻋牌开头呢. ⽐如陕西的⻋牌号就是:陕X:XXXXXX,北京的⻋牌号:京X:XXXXXX,甚⾄⼀个省不同的县区也 是不同的,⽐如西安就是,陕A:XXXXX,咸阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样. 这样做的好处除了可以节约号码之外,更重要的作⽤是可以直观的标识⼀辆⻋的归属地.

类注解之间的关系 

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

@Controller , @Service 和 @Repository ⽤于更具体的⽤例(分别在控制层,业务逻辑层,持 久化层),在开发过程中,如果你要在业务逻辑层使⽤ @Component 或@Service,显然@Service是更 好的选择.

⽐如杯⼦有喝⽔杯,刷⽛杯等,但是我们更倾向于在⽇常喝⽔时使⽤⽔杯,洗漱时使⽤刷⽛杯. 

以上介绍的五大注解只能加在类上, 并且只能加在自己的代码上. 如果你引入了一个第三方的jar包, 并且也希望交给Spring管理, 是没有办法加五大注解的,

3 ⽅法注解@Bean

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

1. 使⽤外部包⾥的类, 没办法添加类注解

2. ⼀个类,需要多个对象,⽐如多个数据源

这种场景, 我们就需要使⽤⽅法注解@Bean

我们先来看看⽅法注解如何使⽤:

// 这个是@Data是lombok插件的注解
@Data
public class UserInfo {
    private Integer id;
    private String name;
    private Integer age;
}
public class BeanConfig {

    @Bean
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }
}

然⽽,当我们写完以上代码,尝试获取bean对象中的user时却发现,根本获取不到:

@SpringBootApplication
public class IocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(IocDemoApplication.class, args);
        
        //@Bean演示
        UserInfo userInfo = (UserInfo) context.getBean(UserInfo.class);
        System.out.println(userInfo);

      
    }

}

这是为什么呢?

3.1 ⽅法注解要配合类注解使⽤

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

@Configuration
public class BeanConfig {

    @Bean
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }
}

再次运行代码, 就没有报错了. 

3.2 定义多个对象

对于同⼀个类,如何定义多个对象呢?

比如多数据源的场景,类是同⼀个,但是配置不同,指向不同的数据源

@Configuration
public class BeanConfig {

    @Bean
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }

    @Bean
    public UserInfo userInfo2() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(11);
        return userInfo;
    }

}

定义了多个对象的话,我们根据类型获取对象,获取的是哪个对象呢?

运行结果:

报错信息显⽰:期望只有⼀个匹配,结果发现了两个,userInfo,userInfo2.

从报错信息中,可以看出来, @Bean注解的bean,bean的名称就是它的⽅法名.

接下来我们根据名称来获取bean对象

@SpringBootApplication
public class IocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(IocDemoApplication.class, args);
       

        //@Bean演示
        UserInfo userInfo = (UserInfo) context.getBean("userInfo");
        System.out.println(userInfo);

        UserInfo userInfo2 = (UserInfo) context.getBean("userInfo2");
        System.out.println(userInfo2);

    }

}

可以看到, @Bean可以针对同⼀个类,定义多个对象. 

3.3 Bean传递参数

3.4 重命名Bean

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

    @Bean(name = {"u1", "userInfo"})
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }

此时我们使⽤u1就可以获取到User对象了,如下代码所⽰:

@SpringBootApplication
public class IocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(IocDemoApplication.class, args);     
        //@Bean演示
        UserInfo userInfo = (UserInfo) context.getBean("u1");
        System.out.println(userInfo);       
    }

}

name={} 可以省略,如下代码所⽰:

    @Bean({"u1", "userInfo"})
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }

只有⼀个名称时,{}也可以省略,如:

    @Bean("u1")
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(18);
        return userInfo;
    }

4 扫描路径

bean想要生效, 就要能够被Spring扫描到.

下⾯我们通过修改项⽬⼯程的⽬录结构,来测试bean对象是否⽣效:

在运行代码:

@SpringBootApplication
public class IocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(IocDemoApplication.class, args);     
        //@Bean演示
        UserInfo userInfo = (UserInfo) context.getBean("u1");
        System.out.println(userInfo);       
    }

}

解释:没有bean的名称为u1.

为什么没有找到bean对象呢? 使⽤五⼤注解声明的bean,要想⽣效,还需要配置扫描路径,让Spring扫描到这些注解 也就是通过 @ComponentScan 来配置扫描路径.

@ComponentScan({"org.example"})
@SpringBootApplication
public class IocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(IocDemoApplication.class, args);     
        //@Bean演示
        UserInfo userInfo = (UserInfo) context.getBean("u1");
        System.out.println(userInfo);       
    }

}

运行结果 

{} ⾥可以配置多个包路径 这种做法仅做了解,不做推荐使⽤ 

那为什么前⾯没有配置@ComponentScan注解也可以呢? @ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication 中了

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

我们通常的做法是把启动类放在我们希望扫描的包的路径下,这样我们定义的bean就都可以被扫描到.

5 总结

Bean的存是通过五大注解和@Bean实现的, 使用五大注解和@Bean时, Spring会给一个默认的名称. 

五大注解: BeanName是类名的小驼峰表示法, 如果前两个字母是大写, BeanName为类名.

@Bean: BeanName是方法名.

那如何修改BeanName呢?

UserService userService = (UserService) context.getBean("uuu");
userService.doService();

UserInfo userInfo = (UserInfo) context.getBean("uu");
System.out.println(userInfo);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早点睡觉1.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值