@Configuration、@Bean、@Import 装配组件 与 @Resource 与 @Autowired 与 @Qualifier 、@Primary 获取组件

本文详细介绍了Spring框架中@Configuration配置类、@Bean、@Import、@Resource、@Autowired与@Qualifier等注解的使用方法,以及如何通过这些注解进行依赖注入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

@Configuration 配置类

@Bean 添加组件

@Import 添加组件

导入 ImportSelector 实现类

导入 ImportBeanDefinitionRegistrar 实现类

 @ImportResource 导入Spring 配置文件

@Resource 与 @Autowired 与 @Qualifier

@Qualifier 标记在方法参数前

@Primary 指定主实现类

注入同一类型全部实现类的实例


1、已经知道 @ImportResource 导入Spring 配置文件 如以前经常在 beans.xml 配置的各种定时器、自己写的类以及各种第三方如 MyBatis 与 Hibernate 的类实例等,但 Spring 官方并不建议这么做,因为现在主流是面向全注解编程

2、所以现在就用 @Configuration(配置类)来替代以前的 Spring 配置文件,用 @Bean 来替代 Spring 配置文件中 <bean> 标签,来给容器中添加组件

@Configuration 配置类

1)@Configuration 指明当前类是一个配置类,相当于把该类作为 spring 的 xml 配置文件中的 。

2)被 @Configuration 注解的类内部包含有一个或多个被 @Bean 注解的方法,这些方法将会被 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类进行扫描,并用于构建bean定义,初始化Spring容器。

3) @Configuation 等价于 <beans> ,@Bean 等价于  <bean>。

4)@Configuration 配置类同样必须是在 SpringBoot能扫描到的位置。

@Bean 添加组件

1)@Bean 是一个方法级别上的注解,配合 @Configuration 注解使用,也可以用在 @Component 注解的类里。

2)默认情况下 bean 的名称和方法名称相同,也可以使用 name 属性来指定,值是一个字符串数组,可以指定多个值。

3)autowire 属性有 3 个值,Autowire.NO:默认装配方式,Autowire.BY_NAME:根据名称装配,,Autowire.BY_TYPE:根据类型装配

4)@Bean 标注的方法在应用启动时就会自动执行,可以 System.out.println() 打印查看。

使用举例

1、提供一个接口与其两个实现类:

//兵器接口
public interface Weapon {
    String showInfo();
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/service/Weapon.java

import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wmx.web_app.service.Weapon;

//刀。业务层实现类此时并没有加 @org.springframework.stereotype.Service 注解
public class Knife implements Weapon {

    @Override
    public String showInfo() {
        JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
        ObjectNode objectNode = nodeFactory.objectNode();
        objectNode.put("code", 10011);
        objectNode.put("name", "血饮狂刀");
        return objectNode.toString();
    }
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/service/impl/Knife.java

//枪、矛
public class Spear implements Weapon {

    @Override
    public String showInfo() {
        JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
        ObjectNode objectNode = nodeFactory.objectNode();
        objectNode.put("code", 10010);
        objectNode.put("name", "火尖枪");
        return objectNode.toString();
    }
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/service/impl/Spear.java

2、提供配置类 @Configuration 与 @Bean 装配自己需要的对象:

/**
 * @Configuration 指明当前类是一个配置类;相当于把该类作为 spring 的 xml 配置文件中的 <beans>
 * @Configuration 配置类可以有多个
 */
@Configuration
public class SystemConfig {
    /**
     * "@Bean" 相当于在 beans.xml 中的配置:<bean id="knife" class="com.wmx.web_app.service.impl.Knife"/>
     * 将方法的返回值添加到容器中,容器中这个组件默认的 id 就是方法名
     */
    @Bean
    public Knife knife() {
        return new Knife();
    }
    @Bean
    public Spear spear() {
        return new Spear();
    }
    /**
     * 同一个实例可以装配多个到容器中,可以根据改变方法名来区分,或者直接使用 name 属性指定组件名称
     * 这在某些时候是非常有用的,比如装配2个 org.springframework.web.client.RestTemplate,一个使用负载均衡,一个不使用负载均衡
     */
    @Bean(name = {"spearGold"})
    public Spear spearGold() {
        return new Spear();
    }
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/config/SystemConfig.java

3、提供一个控制层方便从浏览器发请请求:

//兵器控制层
@RestController
@RequestMapping("weapon")
public class WeaponController {

    @Autowired
    private Knife knife;
    @Resource
    private Spear spear;
    @Resource(name = "spearGold") //指定组件名称注入
    private Spear spearGold;

    /**
     * http://192.168.2.77:8080/weapon/info?type=1
     * http://192.168.2.77:8080/weapon/info?type=2
     * http://192.168.2.77:8080/weapon/info?type=3
     *
     * @param type :1 表示枪,2表示刀
     * @return
     */
    @GetMapping("info")
    public String info(@RequestParam long type) {
        String message = null;
        if (type == 1) {
            message = spear.showInfo();
        } else if (type == 2) {
            message = knife.showInfo();
        } else {
            message = spearGold.showInfo();
        }
        System.out.println(message);
        return message;
    }
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/controller/WeaponController.java

@Import 添加组件

1、使用 @Import 注解,可以将以下内容导入到Spring容器,@Import等同于之前的<import/>标签。:

配置类(Configuration Class)

1、通过导入其他的配置类,可以将其内部定义的Bean注册到容器中。

2、比如某个配置类(@Configuration)无法被扫描到,此时可以进行导入。

普通类(Non-configuration Class)1、将普通的类作为Bean导入到Spring容器中。

2、@Import 注解可以标注在类上,可以结合 @Configuration 注解、ImportSelector、ImportBeanDefinitionRegistrar 一起使用,并且要在 Springboot 扫描范围内@Import 也可以直接标记在启动类上。如果结合 @Conditional 注解,还可以实现条件导入。

3、@Import 引入方式也很简单,被引入的类会被实例化 bean 对象,交于 Spring 容器管理,在需要使用的地方直接引入即可。

// 第一步
public class A {
}

// 第二步
@Import({A.class})
@Configuration
public class TestConfiguration {
}

// 第三步
@Autowired
private A a;

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/config/SystemConfig.java

。 

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/component/SystemTimer.java

导入 ImportSelector 实现类

1、ImportSelector 是一个接口,实现这个接口需要重写 selectImports 方法。方法会返回一个String数组,它包含的元素是需要被导入到容器中的类的全限定名。

public class HelloImportSelector implements ImportSelector {
    /**
     * 返回需要导入的类名的数组,可以是任何普通类,配置类(@Configuration、@Bean、@CompontentScan等标注的类)
     * @importingClassMetadata:用来获取被@Import标注的类上面所有的注解信息
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List list = new ArrayList<>();
        list.add("intl.skw.test.HelloImport");
        return StringUtils.toStringArray(list);
    }
}
 
@Import(HelloImportSelector.class)
public class SpringTestApplication {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringTestApplication.class);
        HelloImport helloImport = applicationContext.getBean(HelloImport.class);
        helloImport.test();
    }
}

导入 ImportBeanDefinitionRegistrar 实现类

1、当类实现了 ImportBeanDefinitionRegistrar 接口,就可以拿到注册器,可以向容器中注册任何的Bean,并且可以对Bean添加属性。

public class HelloImportDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(HelloImport.class)
                .getBeanDefinition();
        registry.registerBeanDefinition("HelloImport", beanDefinition);
    }
}
 
@Import(HelloImportDefinitionRegistrar.class)
public class SpringTestApplication {
 
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringTestApplication.class);
        HelloImport helloImport = applicationContext.getBean(HelloImport.class);
        helloImport.test();
    }
}

 @ImportResource 导入Spring 配置文件

https://blog.csdn.net/wangmx1993328/article/details/81005170#@ImportResource 导入Spring 配置文件。 

@Resource 与 @Autowired 与 @Qualifier

1、@Resource、@Autowired 都可以标注在成员属性上,或属性的 setter 方法上。

2、@Autowired 注解按类型转配 bean,有一个 required 属性,表示必须找到匹配的 Bean,否则抛异常,默认值为 true,@Autowired(required=true) ,如果允许 null 值,可以设置它 required 属性为 false

3、@Autowired 可以与 @Qualifier 组合使用,添加限定符,解决按类型匹配找到多个 Bean 问题:

@Autowired
@Qualifier("circular") // circular 为 bean 的名称。表示如果按 Shape 类型匹配了多个时,则使用名称为 circular 的对象
private Shape shape; // 通常如果 Shape 接口有多个实现类被交由容器管理了,则此时需要使用 @Qualifier 进行限定

4、@Resource 先按名称装配 bean,当找不到名称匹配的 bean 时,再按类型装配。常用属性为 name,还有其它几个不常用的属性 lookup、type、mappedName、description 等。

@Resource   //未指定 name 属性时, 默认使用成员属性的变量名,当找不到名称匹配的 bean 时,再按类型装配
private Circular circular;

@Resource(name = "rectangle") //指定被注入的 bean 的名称,当找不到名称匹配的 bean 时,不再按类型装配
private Rectangle rectangle;

使用举例

1、提供一个接口 Shape(图形),两个实现类 Circular(圆形)、Rectangle(矩形)代码如下:

//图形接口
public interface Shape {
    //画图,绘图,绘制
    void draw();
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/service/Shape.java

import com.wmx.web_app.service.Shape;
import org.springframework.stereotype.Service;
//矩形
@Service
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制矩形.");
    }
}
//圆形
@Service
public class Circular implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形.");
    }
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/service/impl/Circular.java

2、提供一个控制层从浏览器访问测试:

import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wmx.web_app.service.Shape;
import com.wmx.web_app.service.impl.Circular;
import com.wmx.web_app.service.impl.Rectangle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("shape")
public class ShapeController {

    @Autowired //按 Rectangle 类型注入
    private Rectangle rectangle;

    @Resource //先按名称(circular)注入,找不到时按 Circular 类型注入
    private Circular circular;

    @Autowired
    @Qualifier("circular") // circular 为 bean 的名称。表示如果按 Shape 匹配了多个类型时,则使用名称为 circular 的对象
    private Shape shape;

    @Resource(name = "rectangle") // 因为 Shape 接口有 2 个实现,所以必须指定特定名称
    private Shape shape2;

    //绘制图形。flag=1为矩形,flag=2为圆形。http://192.168.2.77:8080/shape/draw?flag=1
    @GetMapping("draw")
    public String draw(@RequestParam int flag) {
        JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
        ObjectNode objectNode = nodeFactory.objectNode();
        objectNode.put("code", 200);
        objectNode.put("flag", flag);
        if (flag == 1) {
            rectangle.draw();
            objectNode.put("message", "矩形绘制");
        } else if (flag == 2) {
            circular.draw();
            objectNode.put("message", "圆形绘制");
        } else {
            objectNode.put("code", 500);
            objectNode.put("message", "参数值错误");
        }
        shape.draw();//输出"绘制圆形."
        shape2.draw();//输出"绘制矩形."
        return objectNode.toString();
    }
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/controller/ShapeController.java

@Qualifier 标记在方法参数前

/**
 * Springboot + JdbcTemplate 多数据源配置
 * 1、自定义 {@link DataSource}
 * 2、自定义 {@link JdbcTemplate}
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2022/2/23 17:06
 */
@Configuration
public class DataSourceConfig {
    /**
     * 默认数据源
     * bean:name 属性用于指定实例的名称,同时添加到 Spring 容器中。不指定名称时,默认是与方法名称相同。
     * Primary:当使用 @Resource、@Autowired 注入时,如果出现多个相同类型,则选择 primary 标记的类型优先注入。
     * ConfigurationProperties:为实例注入全局配置文件中指定前缀下的配置信息
     */
    @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.default")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }
    //workflow 数据源
    @Bean(name = "workflowDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.workflow")
    public DataSource workflowDataSource() {
        return DataSourceBuilder.create().build();
    }
    //element 数据源
    @Bean(name = "elementDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.element")
    public DataSource elementDataSource() {
        return DataSourceBuilder.create().build();
    }
    //使用自定义的数据源重新创建 JdbcTemplate 实例,参数 DataSource 此时默认取的是上面 Primary 标记的实例
    @Bean(name = "jdbcTemplate")
    public JdbcTemplate defaultJdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
    //使用自定义的数据源重新创建 JdbcTemplate 实例,使用 Qualifier 获取指定名称的 DataSource 实例
    @Bean(name = "workflowJdbcTemplate")
    public JdbcTemplate workflowJdbcTemplate(@Qualifier("workflowDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
    //使用自定义的数据源重新创建 JdbcTemplate 实例,使用 Qualifier 获取指定名称的 DataSource 实例
    @Bean(name = "elementJdbcTemplate")
    public JdbcTemplate elementJdbcTemplate(@Qualifier("elementDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

https://gitee.com/wangmx1993/jdbc-multiple-data-sources/blob/master/src/main/java/com/wmx/jmds/config/DataSourceConfig.java

@Primary 指定主实现类

1、@Primary 注解同样可以用来处理一个接口多个实现类的值注入情况,它直接写在实现类上,标识如果出现多个类型时,选择此类型优先注入。

/**
 * 圆形
 * @Primary 注解标识此类为 Shape 接口的主实现,当根据类型注入 Shape 有多个子类型时,选择本类进行注入
 */
@Service
@Primary
public class Circular implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形.");
    }
}

https://gitee.com/wangmx1993/web_app/blob/master/src/main/java/com/wmx/web_app/service/impl/Circular.java

2、现在注入 Shape 时,@Resource 可以不再需要 name 属性指定 bean 的名称,@Autowired 也不需要借助 @Qualifier 注解指定 bean 的名称,因为它默认会使用 Circular 进行注入。

    @Resource
    private Shape shape3;

    @Autowired
    private Shape shape4;

注入同一类型全部实现类的实例

1、@Resource 与 @Autowired 通常用于获取单个实例,而有时候却希望获取某个接口所有实现类的实例。

2、使用方式也非常简单,就行文件上传一样,只需要修改一下类型就行,使用 Map<T>、List<T> 、数组注入即可。

    // 将 DataSource 接口所有实现类的实例注入到属性当中
    // 如果是 List 或者数组,则自动把实例对象全部放进去
    // 如果是 Map,则把实例的名称赋值作为 key,实例对象作为 value。
    // Resource 与 Autowired 接收方式相同
    @Resource
    private Map<String, DataSource> dataSourceMap;
    @Autowired
    private List<DataSource> dataSourceList;
    @Autowired
    private DataSource[] dataSources;

    // 当 Bean 初始化时执行 PostConstruct 标记的方法
    @PostConstruct
    public void init() {
        int j = 1;
        for (Map.Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
            // 1:dataSource=HikariDataSource (null)
            // 2:workflowDataSource=HikariDataSource (null)
            // 3:elementDataSource=HikariDataSource (null)
            System.out.println(j++ + ":" + entry.getKey() + "=" + entry.getValue());
        }
    }

https://gitee.com/wangmx1993/jdbc-multiple-data-sources/blob/master/src/main/java/com/wmx/jmds/conntroller/JmdController.java

https://gitee.com/wangmx1993/jdbc-multiple-data-sources/blob/master/src/main/java/com/wmx/jmds/config/DataSourceConfig.java

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蚩尤后裔-汪茂雄

芝兰生于深林,不以无人而不芳。

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

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

打赏作者

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

抵扣说明:

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

余额充值