[Spring] @Bean 修饰方法时如何注入参数

目录

[一、@Bean 的简单使用](#一、@Bean 的简单使用)

1、正常情况

2、问题提出

二、解决方案

1、@Qualifier

2、直接写方法名

三、特殊情况

1、DataSource


一、@Bean 的简单使用

在开发中,基于 XML 文件配置 Bean 对象的做法非常繁琐且不好维护,因此绝大部分情况下都是使用"完全注解开发"。

对于 Spring 而言,IOC 容器中的 Bean 对象的创建和使用是一大重点,Spring 也为我们提供了注解方式创建 bean 对象:使用 @Bean。

在举例之前,先熟悉以下两个需要用到的类:

(1)User 类

java 复制代码
package com.demo.pojo;

import org.springframework.stereotype.Component;

@Component
public class User {
    private String name;

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

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

(2)Book 类

java 复制代码
package com.demo.pojo;

import org.springframework.stereotype.Component;

@Component
public class Book {
    private User user;

    public void setUser(User user) {
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

1、正常情况

(1)下面是一个简单的 bean 对象创建:

java 复制代码
@Bean
public User getUser() {
    return new User();
}

经此操作,IOC 中就会多出一个与 <bean id = "getUser" class = "类路径.User"/> 同义的 bean 对象。即:方法名就是 id,返回类型就是 class

(2)含有普通类型参数的 bean 对象创建:

java 复制代码
@Bean
public User getUser(@Value("wyt") String name) {
    User user = new User();
    user.setName(name);
    return user;
}

需要注意的是,方法中的参数并不是 bean 对象的成员属性,而是代表着 bean 对象的创建依赖于这几个参数,或许用来 setParameter,或许只是中间变量。

而在 xml 文件中的 <property> 则必须要求用 set 方法。

(3)含有对象类型参数的 bean 对象创建:

java 复制代码
package com.demo.config;

import com.demo.pojo.Book;
import com.demo.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

@Configuration
public class Config {
    @Bean
    public User getUser(@Value("wyt") String name) {
        User user = new User();
        user.setName(name);
        return user;
    }

    @Bean
    public Book createBook(User user) {
        Book book = new Book();
        book.setUser(user);
        return book;
    }
}

对比在 xml 中是如何实现参数是对象类型时的属性注入:

(4)正常情况下,@Bean 使用总结

(4-1)@Bean 注解的方法,其返回值就是一个 <bean> 对象,并且注册到了 IOC 容器中。

  • id 为方法的名字;
  • class 为方法的返回类型;

(4-2)@Bean 对象的实例化依赖于方法内的参数,参数可以是普通类型,也可以是对象类型。

  • 若为普通类型,用 @Value("xxx") 来注入这个参数;
  • 若为对象类型,则默认情况下,Spring 会到 IOC 容器中寻找与参数类型相同的 bean 对象来注入这个参数;

2、问题提出

根据上述内容,我们会想到:如果参数是对象类型,可是 IOC 中拥有不止一个相同类型的 bean 对象,这该怎么办呢?

比如下面的情况:

可以看到,Book 对象的参数 user 已经报错了,无法自动装配,因为存在多个 User 类型的 Bean,Spring 不知道应该将哪个 bean 注入到 user 中

二、解决方案

首先我们要知道,默认情况下,参数的注入使用的是 @Autowired,不需要显式写出。

1、@Qualifier

我们可以想到,@Autowired 是根据类型自动装配,当一个类型有多个 bean 对象时失效。

而 @Qualifier 是根据名称进行装配,这不就意味着我们可以用 @Qualifier 来明确需要的 bean 对象嘛。

这有两种写法,都是有效的:

  • 直接在参数前加 @Qualifier("function_name");
  • 在方法前加 @Qualifier("function_别名"),在参数前加 @Qualifier("function_别名");

(1)配置类

java 复制代码
package com.demo.config;

import com.demo.pojo.Book;
import com.demo.pojo.User;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

@Configuration
public class Config {
    @Bean
    public User getUser(@Value("wyt") String name) {
        User user = new User();
        user.setName(name);
        return user;
    }
    @Bean
    public User createUser(@Value("wyt") String name) {
        User user = new User();
        user.setName(name);
        return user;
    }
    @Bean
    public Book createBook(@Qualifier("createUser") User user) {
        Book book = new Book();
        book.setUser(user);
        return book;
    }
}

(2)测试代码

java 复制代码
@Test
public void SameObjectTest() {
    ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
    Book book = context.getBean("createBook", Book.class);
    System.out.println(book.getUser());
}

(3)输出结果

2、直接写方法名

因为一般情况下,方法名不能相同,因此也可以通过将参数名写成对应的方法名来进行注入

java 复制代码
package com.demo.config;

import com.demo.pojo.Book;
import com.demo.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

@Configuration
public class Config {
    @Bean
    public User getUser(@Value("wyt") String name) {
        User user = new User();
        user.setName(name);
        return user;
    }
    @Bean
    public User createUser(@Value("wyt") String name) {
        User user = new User();
        user.setName(name);
        return user;
    }
    @Bean
    public Book createBook(User createUser) {
        Book book = new Book();
        book.setUser(createUser);
        return book;
    }
}

三、特殊情况

在这里要说明一些特别的对象类型,比如:DataSource。

1、DataSource

(1)问题描述

java 复制代码
@Bean
public DruidDataSource createDruidDataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    return dataSource;
}

@Bean
public DruidDataSource getDruidDataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    return dataSource;
}

当我们像上面的代码一样,写了两个数据源的 bean 对象之后,运行就会出现如下报错:

No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 2

简单来说,就是因为 Spring 有其自身的配置类,导致程序不知道选择哪一个数据源。

(2)解决方法

给其中任意一个加上 @Primary,代表当出现多个同类型 bean 时,优先使用哪一个。写上之后,就可以使用前文所述的方法,如:@Qualifier 或直接写方法名。

java 复制代码
@Bean
@Primary
public DruidDataSource createDruidDataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    return dataSource;
}
相关推荐
青云交2 分钟前
Java 大视界 -- Java 大数据实战:分布式架构重构气象预警平台(2 小时→2 分钟)
java·java 大数据 气象预警平台·flink 实时数据清洗·spark 区域定制模型·气象灾害预警系统
柏林以东_2 分钟前
异常的分类与用法
java·开发语言
智绘前端7 分钟前
React 组件开发速查卡
前端·react.js·前端框架
专注API从业者8 分钟前
淘宝商品 API 接口架构解析:从请求到详情数据返回的完整链路
java·大数据·开发语言·数据库·架构
学嵌入式的小杨同学8 分钟前
【嵌入式 C 语言实战】栈、队列、二叉树核心解析:存储原理 + 应用场景 + 实现思路
linux·c语言·网络·数据结构·数据库·后端·spring
独自破碎E9 分钟前
解释一下为什么要有虚拟内存
java
哪里不会点哪里.13 分钟前
Spring 的装配顺序详解(配置 → 扫描 → 注入 → 初始化)
java·sql·spring
xiaolyuh12319 分钟前
Spring MVC 深度解析
java·spring·mvc
箫笙默20 分钟前
前端相关技术简介
前端
-凌凌漆-21 分钟前
【java】java中函数加与不加abstract 的区别
java·开发语言