【Spring注解驱动开发】如何实现方法、构造器位置的自动装配?我这样回答让面试官很满意!

简介: 在 冰河技术 微信公众号前面的文章中,我们介绍了如何使用注解来自动装配Spring组件。之前将的都是在来的字段上添加注解,那有没有什么方法可以实现方法、构造器位置的自动装配吗?今天我们就一起来探讨下如何实现方法、构造器位置的自动装配。

再谈@Autowired注解

在我发表在 冰河技术信公众号的《【Spring注解驱动开发】使用@Autowired@Qualifier@Primary三大注解自动装配组件,你会了吗?》一文中简单介绍了下@Autowired注解注解的使用方法。下面,我们再来看下@Autowired注解的源码。

package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
 boolean required() default true;
}

我们通过@Autowired注解的源码可以看出,在@Autowired注解上标注有如下的注解信息。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

可以看出@Autowired注解不仅可以标注在字段上。也可以标注在构造方法上,实例方法上,参数上。

项目案例

案例准备

接下来,我们在项目中新建一个Dog类,在Doc类中有一个Cat类的引用,并且我们使用@Component注解将Dog类加载到IOC容器中,如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 测试实体类
 */
@Component
public class Dog {
    private Cat cat;
    public Cat getCat() {
        return cat;
    }
    public void setCat(Cat cat) {
        this.cat = cat;
    }
    @Override
    public String toString() {
        return "Dog{" +  "cat=" + cat + '}';
    }
}

配置好之后,我们还需要在AutowiredConfig类的@ComponentScan注解中进行配置,使其能够扫描io.mykit.spring.plugins.register.controller包下的类,如下所示。

@Configuration
@ComponentScan(value = {
        "io.mykit.spring.plugins.register.dao",
        "io.mykit.spring.plugins.register.service",
        "io.mykit.spring.plugins.register.controller",
        "io.mykit.spring.plugins.register.bean"})
public class AutowiredConfig {
}

此时,我们可以直接在Dog类中的cat字段上添加@Autowired注解,使其自动装配。这是我们在《【Spring注解驱动开发】使用@Autowired@Qualifier@Primary三大注解自动装配组件,你会了吗?》一文中得出的结论。那今天我们就使用其他的方式来实现cat的自动装配。

标注在实例方法上

我们也可以将@Autowired注解标注在setter方法上,如下所示。

@Autowired
public void setCat(Cat cat) {
    this.cat = cat;
}

当@Autowired注解标注在方法上时,Spring容器在创建对象的时候,就会调用相应的方法为对象赋值。如果标注的方法存在参数时,则方法使用的参数和自定义类型的值,需要从IOC容器中获取。

接下来,我们将AutowiredTest类的testAutowired01()方法中有关获取和打印PersonService信息的代码注释,新增获取和打印Dog信息的代码,如下所示。

@Test
public void testAutowired01(){
    //创建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
    //PersonService personService = context.getBean(PersonService.class);
    //System.out.println(personService);
    Dog dog = context.getBean(Dog.class);
    System.out.println(dog.toString());
    context.close();
}

运行AutowiredTest类的testAutowired01()方法进行测试,可以看到,结果信息中输出了如下一行信息。

Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}

说明已经获取到cat的信息,可以将@Autowired注解标注在方法上

为了验证最终的输出结果是否是从IOC容器中获取的,我们可以在AutowiredTest类的testAutowired01()方法中直接获取Cat的信息,如下所示。

@Test
public void testAutowired01(){
    //创建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
    //PersonService personService = context.getBean(PersonService.class);
    //System.out.println(personService);
    Dog dog = context.getBean(Dog.class);
    System.out.println(dog.toString());
    Cat cat = context.getBean(Cat.class);
    System.out.println(cat);
    context.close();
}

我们再次运行AutowiredTest类的testAutowired01()方法进行测试,可以在输出的结果信息看到如下两行代码。

Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}
io.mykit.spring.plugins.register.bean.Cat@6a400542

可以看出在Dog类中通过@Autowired注解获取到的Cat对象和直接从IOC容器中获取到Cat对象是同一个对象。

标注在构造方法上

在前面的案例中,我们在Dog类上使用了@Component注解,如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 测试实体类
 */
@Component
public class Dog {
    private Cat cat;
    public Cat getCat() {
        return cat;
    }
    @Autowired
    public void setCat(Cat cat) {
        this.cat = cat;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "cat=" + cat +
                '}';
    }
}

此时,Spring默认加载IOC容器中的组件,IOC容器启动的时候默认会调用bean的无参构造器创建对象,然后再进行初始化赋值等操作。

接下来,我们为Dog类添加一个有参构造方法,然后去除setCat()方法上的@Autowired注解,将@Autowired注解标注在有参构造方法上,并在构造方法中打印信息,如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 测试实体类
 */
@Component
public class Dog {
    private Cat cat;
    @Autowired
    public Dog(Cat cat){
        this.cat = cat;
        System.out.println("调用了Dog的有参构造方法");
    }
    public Cat getCat() {
        return cat;
    }
    public void setCat(Cat cat) {
        this.cat = cat;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "cat=" + cat +
                '}';
    }
}

接下来,我们运行AutowiredTest类的testAutowired01()方法进行测试,可以看到输出结果信息中存在如下一行信息。

调用了Dog的有参构造方法

说明IOC容器在启动的时候调用了Dog类的有参构造方法。并且可以从输出的如下两行信息可以看出:通过Dog类的toString()方法打印出的Cat对象和直接从IOC容器中获取的Cat对象是同一个对象。

Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}
io.mykit.spring.plugins.register.bean.Cat@6a400542

这里,需要大家注意的是:使用@Autowired注解标注在构造方法上时,构造方法中的参数对象也都是从IOC容器中获取的。

标注在参数上

我们也可以将@Autowired注解标注在参数上,例如,在Dog类中我们将构造方法上的@Autowired注解标注在构造方法的参数上,如下所示。

public Dog(@Autowired Cat cat){
    this.cat = cat;
    System.out.println("调用了Dog的有参构造方法");
}

也可以将@Autowired注解标注在setter方法的参数上,如下所示。

public void setCat(@Autowired  Cat cat) {
    this.cat = cat;
}

这些效果与标注在字段、实例方法和构造方法上的效果都是一样的。

例如,我们将@Autowired注解标注在构造方法的参数上,运行AutowiredTest类的testAutowired01()方法进行测试,可以看到,输出结果中,同样包含如下三行信息。

调用了Dog的有参构造方法
Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}
io.mykit.spring.plugins.register.bean.Cat@6a400542

结论:无论Autowired注解标注在字段上、实例方法上、构造方法上还是参数上,都是从IOC容器中获取参数组件的值。

如果Spring的bean只有一个有参构造方法,并且这个有参构造方法只有一个参数,并且这个参数是IOC容器中的对象,当@Autowired注解标注在这个构造方法的参数上时,我们可以将@Autowired注解省略,如下所示。

public Dog(Cat cat){
    this.cat = cat;
    System.out.println("调用了Dog的有参构造方法");
}

接下来,我们运行AutowiredTest类的testAutowired01()方法进行测试,从输出的结果信息中,可以看出,同样输出了下面的三行信息。

调用了Dog的有参构造方法
Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}
io.mykit.spring.plugins.register.bean.Cat@6a400542

说明:如果Spring的bean只有一个有参构造方法,并且这个有参构造方法只有一个参数,并且这个参数是IOC容器中的对象,当@Autowired注解标注在这个构造方法的参数上时,我们可以将@Autowired注解省略。

标注在方法位置

@Autowired注解可以标注在某个方法的位置上。这里,为了更好的演示效果,我们新建一个Fish类,在Fish类中有一个Cat类型的成员变量,如下所示。

package io.mykit.spring.plugins.register.bean;
/**
 * @author binghe
 * @version 1.0.0
 * @description 测试类
 */
public class Fish {
    private Cat cat;
    public void setCat(Cat cat) {
        this.cat = cat;
    }
    @Override
    public String toString() {
        return "Fish{" + "cat=" + cat + '}';
    }
}

接下来,我们在AutowiredConfig类中实例化Fish类,如下所示。

@Bean
public Fish fish(){
    return new Fish();
}

接下来,我们在AutowiredTest类中创建testAutowired02()方法,如下所示。

@Test
public void testAutowired02(){
    //创建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
    Fish fish = context.getBean(Fish.class);
    System.out.println(fish);
    context.close();
}

运行testAutowired02()方法,在输出的结果信息中存在如下一行信息。

Fish{cat=null}

说明此时的Fish类中的Cat对象为空。此时,我们可以将Cat对象作为一个参数传递到AutowiredConfig类的fish()方法中,并且将Cat对象设置到Fish中,如下所示。

@Bean
public Fish fish(Cat cat){
    Fish fish = new Fish();
    fish.setCat(cat);
    return fish;
}

当然,我们也可以使用@Autowired注解来标注fish()方法中的cat参数,如下所示。

@Bean
public Fish fish(@Autowired  Cat cat){
    Fish fish = new Fish();
    fish.setCat(cat);
    return fish;
}

接下来,我们再次运行testAutowired02()方法,在输出的结果信息中存在如下一行信息。

Fish{cat=io.mykit.spring.plugins.register.bean.Cat@21de60b4}

说明Cat对象被成功创建并设置到了Fish类中。

相关文章
|
9月前
|
安全 Java 决策智能
Spring Boot自动装配
Spring Boot自动装配基于“约定优于配置”理念,通过条件化配置与Starters机制,智能推断并加载所需组件,大幅简化开发流程。它实现配置自动化,提升效率,降低维护成本,支持自定义扩展,推动微服务快速构建,是Java生态中开发范式的革新之作。(238字)
|
9月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
771 2
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
566 2
|
前端开发 安全 Java
2025春招,Spring 面试题汇总
本文详细整理了2025年春招必备的Spring面试题,分为基础和高级两大部分,帮助求职者全面掌握Spring相关知识点,结合实际项目经验,提升面试成功率。内容涉及Spring框架、AOP、事务管理、数据库集成、Spring Boot、Spring Security、微服务架构等,助力你在春招中脱颖而出。
3435 0
|
设计模式 算法 架构师
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
1371 2
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
Java 程序员 Spring
Spring事务的1道面试题
每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。
Spring事务的1道面试题
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
426 2
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?