Spring 中简单存取 Bean 的相关注解

前言

之前我们存储获取 Bean 的操作很繁琐,需要将 Bean 放入 xml 文件中,获取 Bean 还必须得获取上下文,就很麻烦。

其实这些都可以简化,在 Spring 中想要更简单的存储和读取对象,核⼼是使⽤注解。

想要将对象存储在 Spring 中,有两种注解类型可以实现:

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

获取对象的实现⽅法有以下 3 种:

  1. 属性注⼊
  2. 构造⽅法注⼊
  3. Setter 注⼊

存储 Bean 对象

五大类注解

五大类注解有:
@Controller(控制器)校验参数的合法性,相当于安检系统
@Service(服务)业务组装,相当于客服中心
@Repository(数据持久层)实际业务处理,就是实际办理的业务
@Component(组件)工具类层,基础工具
@Configuration(配置层)配置

首先,我们得配置一下 xml 文件:

在这里插入图片描述

注意:使用类注解存储 bean,和使用 xml 存储 bean 是可以混用的。

User 类:

//使用类注解
@Controller
public class User {
    public void sayHi(String name) {
        System.out.println("hello " + name);
    }
}
public class App {
    public static void main(String[] args) {
        //得到 spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean 对象
        User user = (User) context.getBean("user");
        user.sayHi("张三");
    }
}

在这里插入图片描述

关于 getBean 后面应该填什么?
对于不同的类名有不同的写法,如果首字母大写,第二个字母小写,那么 bean 的名称就是类名首字母小写;如果不满足首字母大写第二字母小写,则使用原类名。

当然了,我们也可以在类注解后面设置 id,通过 id 来得到 bean 对象。

//id 可以随便取, 两种方式都可
//@Controller(value = "666")
@Controller("666")
public class User {
    public void sayHi(String name) {
        System.out.println("hello " + name);
    }
}
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        User user = (User) context.getBean("666");
        user.sayHi("张三");
    }
}

来看看五大类注解间的关系:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”。
所以它们在使用上都差不多,那为什么还要分出 5 大类呢?
直接一个 @Component 就可以 存储 bean 对象了,还要分这么多有什么用呢?

其实是让程序员看到类注解之后,就能直接了解当前类的⽤途,比如我们看到一辆车的车牌号就知道它的归属地是哪,这个也一样。

程序的⼯程分层,调⽤流程如下:
在这里插入图片描述

方法注解(@Bean)

类注解是添加到某个类上的,⽽⽅法注解是放到某个⽅法上的:

Student 类:

//普通类
public class Student {
    private String name;
    private int id;
    private int score;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", score=" + score +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}

class 类:

//注意: 方法注解要配合五大类注解一起使用
//     该类也存储在 IoC 容器中
@Controller
public class Class {

    //使用方法注解, 将方法的返回值存储到 IoC 容器中
    @Bean
    public Student student() {
        Student student = new Student();
        student.setId(6);
        student.setName("张三");
        student.setScore(150);
        return student;
    }
}

获取 bean 对象:

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //注意:@Bean 默认命名是方法名
        // 也就是添加方法注解的那个方法名
        Student student = context.getBean("student", Student.class);
        System.out.println(student.toString());
    }
}

在这里插入图片描述
@Bean 还有几种重命名方式:

    @Bean("aaa")
    @Bean(value = "sss")
    @Bean(name = "nnn")

点入 @Bean :

在这里插入图片描述
发现 name 和 value 效果是一样的。

除了取单个名称外,还可以取多个名称:

    @Bean(value = {"sss","888"})
    @Bean(name = {"sss","888"})
    @Bean({"sss","888"})

注意:

  1. 如果重命名了,那么获取 bean 对象时,默认的方法获取 bean 对象的方式就不能用了。
  2. 添加了 @Bean 注解的方法无法传参(方法的调用是程序在控制),也就不能重载。
  3. 如果多个 Bean 使用相同的名称,那么程序执行不会报错,但是第一个 Bean 后的对象不会被放到容器中,也就是只有第一次创建 Bean 的时候会将对应的 Bean 名称关联起来,后续再有相同名称的 Bean 存储的时候,容器会自动忽略。

获取 Bean 对象 (@Autowired)

获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊。

属性注入

属性注⼊是使⽤ @Autowired 实现的:

普通类 User:

public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

添加类注解的 UserService:

@Service
public class UserService {
    public User getUser(int id, String name) {
        User user = new User();
        user.setId(id);
        user.setName(name);
        return user;
    }
}

添加类注解的 UserController:

@Controller
public class UserController {

	//使用属性注入,将IoC容器中的UserService类型的对象
	//注入变量 userService中
    @Autowired
    private UserService userService;

    public User getUser(int id, String name) {
        return userService.getUser(id,name);
    }
}

注意:下面我们不使用 @Autowired 来进行依赖注入,因为 main 函数是静态方法,@Autowired 又不能给局部变量注入,那就只能放在类里面方法外面了,再加上 static 修饰 才能在 main 里访问,但可惜的是静态类属于方法的一部分,static 成员是不能使用 @Autowired 来注入。

public class Test {
    public static void main(String[] args) {
    	//依赖查找
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        UserController userController = context.getBean("userController", UserController.class);
        //检验是否得到了 UserController 类型对象
        System.out.println(userController.getUser(3,"张三").toString());
    }
}

在这里插入图片描述

依赖查找 VS 依赖注入
依赖查找:依赖 Bean 的名称来获取 Bean 对象。
依赖注入:通过注解 @Autowired 根据对象类型进行依赖注入,首先根据 getType 从容器中获取对象,如果 IoC 容器中只有一个该类型对象,则直接注入到当前属性上;如果容器中有多个该类型对象,则会使用 getName (根据名称)进行匹配。(具体体现如下)

多个同类型 Bean 注入怎么办?

问题:如若有多个同类型的 Bean 对象存储进 IoC 容器中,那么我们该如何准确获取该类型对象?

问题场景:

Users 类:

@Service
public class Users {

	//通过方法注解,添加两个同类型 bean
    @Bean("user1")
    public User user1() {
        User user = new User();
        user.setName("李四");
        user.setId(22);
        return user;
    }

    @Bean("user2")
    public User user2() {
        User user = new User();
        user.setName("王五");
        user.setId(88);
        return user;
    }
}

UserService 类:

@Service
public class UserService {

	//对 user 进行属性注入
    @Autowired
    User user;

    public void add() {
    	//拿到 user 后打印内容,判断是哪个 user
        System.out.println(user.toString());
    }
}

Test 类:

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

看结果:
在这里插入图片描述
它是说期望有一个 bean 进行匹配,但出现了两个:user1、user2

解决问题:

  1. 将属性的名字和 Bean 的名字对应上。

就比如我要得到的是 王五 这个对象,那我只需要将下面两点的名称对应上即可:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. @Autowired 配合 @Qualifier 使用

这个就是在注入时,声明要注入的 bean 名称
在这里插入图片描述

属性注入分析:
优点: 使用简单
缺点:

  1. 无法注入 final 修饰的变量
  2. 只适用于 IoC 容器
  3. 容易违背单一设计原则

Setter 注入

我们写类属性时,可以对这些属性生成相应的 get 和 set 方法,Setter 注入就是针对 set 方法,进行的注入:

User 类:

@Service
public class Users {

        @Bean("user")
        public User getUser() {
            User user = new User();
            user.setName("老王");
            user.setId(26);
            return user;
        }
}

UserService 类:

@Service
public class UserService {

    private User user;

	//对类属性 user 进行 Setter 注入
    @Autowired
    public void setUser(User user) {
        this.user = user;
    }

    public void add() {
        System.out.println(user.toString());
    }
}
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

在这里插入图片描述

注意:@Autowired 不能省略

Setter 注入分析:
优点:通常 Setter 只是注入一个属性,所以 Setter 更符合单一设计原则。
缺点:

  1. 无法注入一个 final 修饰的变量
  2. Setter 注入的对象可以被修改。(setter 是一个方法,既然是方法就可能被多次调用和修改)

构造方法注入(官方推荐)

构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所示:

只对 UserService 进行修改:

@Service
public class UserService {

    private User user;
	
	//构造方法注入
    @Autowired
    public UserService(User user) {
        this.user = user;
    }

    public void add() {
        System.out.println(user.toString());
    }
}

注意:如果只有一个构造方法,不写 @Autowired 也可以,但若是有多个构造方法,就得加上 @Autowired,表明是哪个构造方法需要注入。

构造方法注入分析:
优点:

  1. 可以注入一个 final 修饰的变量
  2. 注入的变量不会被修改,因为构造方法只加载一次
  3. 构造方法注入可以保证注入对象完全初始化
  4. 构造方法注入通用性更好

缺点:

  1. 写法比属性注入更复杂
  2. 使用构造方法注入,无法解决循环依赖的问题

其实 @Resource 的功能和 @Autowired 差不多,那它俩有啥区别呢?

@Autowired 与 @Resource 的区别:

  1. 出生不同:@Resource 来自 JDK,@Autowired 来自 Spring 框架
  2. 支持参数不同:@Resource 支持很多参数设置,@Autowired 只支持一个参数设置
  3. 使用上的区别:@Resource 不支持构造方法注入,@Autowired 支持构造方法注入
  4. idea 兼容性支持不同:使用 @Autowired 在 idea 专业版下可能会误报,@Resource 不存在误报问题(@Resource 相当于是亲儿子了)

关于第四点:因为@Autowired 来自 Spring 框架,@Resource 来自 JDK,所以执行顺序有差异,Spring 框架的执行是在 java 程序执行之后的,当我们使用 @Autowired 时,它不能检测到 IoC 容器中是否有这个类型的 Bean,所以就报错(运行起来不影响结果);使用 @Resource 的话,执行顺序是靠前的,它知道这个 Bean 是否存在。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

随风的浪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值