【Spring】注解读取和存储Bean对象(下)

三、Spring 获取 bean 对象

获取 bean 对象也叫 “对象装配”,“对象注入”, 意思就是把对象取出来放到某个类中。

对象装配(对象注入)的实现有以下三种方法:

1. 属性注入  2. 构造方法注入 3. Setter 注入

接下来博主将围绕着3种方式展开来给大家讲述Spring 如何实现动态注入 bean 对象(运行时动态的将 bean 对象注入到某个类中)。

3.1 属性注入

属性注入需要使用到 @Autowired 注解实现。接下来给大家举一个例子:

有一个学生类(学生信息的组织),假设对应数据库中的学生表,有一个Service 层的 StuService类,这类可以从数据库中读取学生数据(增删查改),实例成一个完整的学生对象,有一个 StuController 类,他可以针对学生数据进行实际的使用(调用处理),所以 StuController 类在运行中需要获取到 StuService 类的实例对象来操作数据库中的学生表。当然这里不会给大家真的用数据库,写伪代码来模拟一下真实的环境。

@Service
public class StuService {
    /**
     * 根据学生id从数据库中获取学生信息
     * @param id
     * @return
     */
    public Student getStu(String id) {
        //伪代码,链接数据库,查找学生信息
        Student stu = new Student();
        stu.setId(id);
        stu.setName("张三");
        stu.setAge(18);
        stu.setSex("男");
        return stu;
    }
}
@Controller
public class StuController {
    //1. 属性注入,从Spring 中取出 StuService 类型的bean 对象并注入到类引用中
    @Autowired
    private StuService stuService;

    //2. 调用注入的 bean 对象执行查询操作
    public Student getStu(String id) {
        return stuService.getStu(id);
    }
}

获取 Controller 中的 getStu 的方法:正常情况下我们就将 Student 学生的信息转换为 json 格式由前端发请求,服务器做出响应,将数据(http协议)给到前端,前端利用数据构建页面。

package school.student2;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import school.student.Student;

public class App {
    public static void main(String[] args) {
        //1. 得到 Spring 的上下文对象
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-test.xml");
        //2. 调用 getBean() 方法获取指定的 Bean 对象
        StuController stuController = context.getBean("stuController", StuController.class);
        //3. 使用bean 对象
        Student student = stuController.getStu("1");
        System.out.println(student.toString());
    }
}

 属性注入的核心:


3.2 构造方法注入

构造方法注入是在类的构造方法中实现注入:同样使用 @Autowired 注解

 如果只有一个构造方法,那么 @Autowired 注解可以省略不写:

如果类中出现多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法,否则程序会报错。


3.3 Setter 注入

Setter 本质上就是使用自定义方法来代替构造方法注入,也是需要在 set 方法上添加 @Autowired 注解:

注意:使用 Setter 注入一定要添加@Autowired 注解


3.4 三种注入优缺点分析

3.4.1 属性注入

优点:实现起来比较简单,只需要一个@Autowired 注解

缺点:

1. 不能注入不可变的(final)的对象

final 修饰的引用,只能在创建的时候进行初始化赋值操作,或在构造方法中初始化。

 2. 只能适用于 loC 容器

属性注入通常与IoC(控制反转)容器紧密相关,并且在大多数情况下,它更常用于IoC容器中。IoC容器通过管理对象的生命周期、依赖关系和对象创建等任务,将控制权交给框架,以实现解耦和可扩展性。总体而言,属性注入与IoC容器密切相关,并且在通过IoC容器管理对象时,属性注入是一种常用的依赖注入方式,能够帮助实现代码的解耦和可配置性。然而,在非IoC场景下,属性注入的使用可能会受到一些限制,并且可能不如其他依赖注入方式适用。

3. 更容易违背单一设计原则(针对类)

属性注入在某些情况下可能会违背单一设计原则(Single Responsibility Principle,SRP)。SRP的目标是确保一个类对于变化的原因只有一个,以便提高代码的可维护性和灵活性。它强调职责的划分和分离,使每个类专注于单一的功能或关注点

以下是属性注入可能导致违背SRP的一些情况:

  1. 依赖过多:属性注入可能导致一个类具有过多的依赖关系。如果一个类依赖过多的其他类或组件,那么它可能承担过多的职责。这样的类可能会变得庞大而难以理解和维护。

  2. 责任扩散:属性注入可能导致一个类承担不应该由它承担的额外职责。当一个类注入了许多属性时,特别是来自不同领域的属性,它可能参与多个责任和业务逻辑,从而违反SRP。

  3. 耦合度增加:属性注入可能导致类之间的耦合度增加。如果一个类对其依赖项的实例化和配置有太多控制权,它与这些依赖项之间的耦合度会增加。这使得类的扩展和修改变得更加困难。

虽然属性注入在一些情况下可能会违背SRP,但这并不意味着属性注入本身是错误的或应该完全避免的。合理使用属性注入,可以通过分割类的职责、解耦依赖关系和保持代码的可维护性来避免违背SRP。在设计和实现过程中,应该谨慎选择注入的属性和依赖项,确保它们与类的职责和关注点保持一致,避免过度复杂化和耦合。


3.4.2 构造方法注入

优点:

1. 可以注入一个不可变(final修饰)对象,构造方法可以为其赋值。

 2. 注入的对象不会被修改

  • 可以被 final 的修饰(只有一个实例)
  • 构造方法伴随着类加载只执行一次

3. 通用性更好

通过构造方法注入,类的依赖关系可以明确地表达出来,构造方法注入允许依赖关系在类的实例化阶段就被传递进来。这意味着在类的实例化过程中,可以清楚地看到类所依赖的对象是哪些,而不是在方法调用时才进行依赖注入。

缺点:

没有属性注入实现简单。

需要注意的是,构造方法注入并不适用于所有情况,特别是当所依赖的对象较多时,会出现构造方法参数列表冗长的问题。在这种情况下,可以考虑使用其他形式的依赖注入方式,如属性注入或方法注入,来提供更灵活的注入方式。选择合适的依赖注入方式,取决于具体的情况和项目需求。

构造方法注入是 Spring 官方推荐的注入方法。


3.4.3 Setter 方式注入(普通方法注入)

Setter 方法是 Spring 早期版本推荐的注入方式,。

优点:比较符合单一设计原则(SRP),方法嘛,针对的是方法级别的,虽然在通用性上没有构造方法强,Setter方法注入也有其适用的场景,比如可选的依赖项或动态变化的依赖关系。在这些情况下,setter方法注入可能更为灵活和方便。选择合适的依赖注入方式,需要根据具体需求和设计原则进行权衡和判断。

缺点:

1. 不能注入不可变(final)的对象;

final 修饰的引用,只能在创建的时候进行初始化赋值操作,或在构造方法中初始化。

2. 注入对象可以被修改

set 方法是普通的方法,可以被重复调用,在调用时就存在被修改的风险。Spring 现版本已经推荐使用构造方法注入的方式来进行类注入。


3.5 @Resource 注入

在 Java 里面在进行类注入时,除了使用 @Autowired 注解,还可以使用 @Resource 注解:

 @Autowired 和 @Resource 都是在 Java 中用于进行依赖注入的注解,但它们有一些不同之处。


3.5.1 @Autowired 和 @Resource 的区别

  1. 出生不同:@Autowired 注解出自于Spring框架(非官方),  @Resource 注解出自于 JDK(官方)。
  2. 功能支持不同:@Autowired 可用于属性注入,构造方法注入,和 Setter(普通方法) 注入.而 @Resoure 只能用于 Setter 注入和属性注入, 不能用于构造方法注入(意味着不能注入不可变对象).
  3. 参数支持不同:  @Resource 支持更多的参数设置, 例如: 对Bean 取一个名字(name), 而 @Autowired 只能设置 required 参数。required 是 @Autowired 注解的一个可选属性,默认值为 true。它用于指定依赖注入是否是必需的。当 required 属性设置为 true 时,如果找不到匹配的依赖对象,则会抛出 NoSuchBeanDefinitionException 异常。

示例:

@Autowired(required = true)
private MyDependency myDependency;

当 required 属性设置为 false 时,如果找不到匹配的依赖对象,则会将依赖对象设置为 null,即允许依赖注入的可选性。

示例:

@Autowired(required = false)
private MyDependency myDependency;

尽管 required 默认值为 true,但是在某些情况下,我们可能使用 @Autowired(required = false) 来标记可选的依赖注入,以便在没有匹配的依赖对象时不抛出异常。这在需要可选依赖或动态注入的场景下非常有用。


3.6 @Bean 注入多个同类型的 bean 对象报错处理

当我们使用 @Bean 方法注解往 Spring 中注入多个同类型的 Bean 对象时,就会报错。

注意:以往的例子我们需要在 Spring 配置文件中将路径注释一下,不然启动的时候Spring 容器中会注册多个同类型的 bean对象(stu)。

@Component
public class StudentDemo {
    StudentDemo() {
        System.out.println("StudentDemo 执行");
    }
    @Bean
    public Student getStu() {
        //1. 创建对象
        Student student = new Student();
        //2. 初始化对象
        student.setId("1");
        student.setName("李小四");
        student.setSex("男");
        student.setAge(18);
        //3. 返回对象
        return student;
    }

    @Bean
    public Student getStu2() {
        //1. 创建对象
        Student student = new Student();
        //2. 初始化对象
        student.setId("2");
        student.setName("王小五");
        student.setSex("男");
        student.setAge(20);
        //3. 返回对象
        return student;
    }
}

在另一个类中获取 Student 对象:

public class StudentApp {
    public static void main(String[] args) {
        //1. 得到 Spring 的上下文对象
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-test.xml");
        //2. 调用 getBean() 方法获取指定的 Bean 对象
        StudentController stuController = context.getBean(StudentController.class);

        //3. 利用 stuController 对象的 getStudent()获取 bean 对象
        Student student = stuController.getStudent();
        System.out.println(student.toString());
    }
}

@Controller
class StudentController {

    //1. 注入 student 对象,在StudentDemo 类中我们使用 @Bean 注入了两个stu 对象
    @Autowired
    private Student student;

    //2. 返回
    public Student getStudent() {
        return student;
    }
}

以上程序执行结果:

同一类型多个 Bean 报错处理: 

  • 使用Resoure(name = "FunctionName") 进行类注入的时候只能指定获取@Bean注解修饰方法的名字,如此一来,问题迎刃而解~
  • 使用@Qualifier 注解定义名称,可以跟@Autowired 注解配合使用(Autowired 注解不可以指定name 注入)。

1. 使用 @Resource(name = "XXXXX") 定义

2. 使用 @Qualifier 

@Qualifier注解 用于帮助指定特定的依赖注入或自动装配。它通常与依赖注入框架(如Spring)一起使用,以解决多个候选对象的歧义性。在依赖注入中,当存在多个相同类型的bean时,可以使用@Qualifier注解来标识要使用的具体bean。通过在@Qualifier注解中指定一个唯一的标识符(name),可以告诉依赖注入框架,注入哪个具体的bean。


 四、总结

1. 将对象存储到 Spring 中:

  • 古老一点的做法是在 Spring 配置文件的 标签下直接添加注解。

不好的一点是比较麻烦,每个 Bean 对象都需要我们手动的添加,对此我们有了注解的方式。

  • 使用类注解:@Controller 、@Service、@Repository、@Configuration、@Component这些其他注解的内部都有一个 @Component注解, 说明这些注解可以认为是 @Component 的子类.

使用类注解:需要在Spring 配置文件中设置包扫描路径,当Spring 启动时,会根据包路径下的类注解将修饰的类实例到 Spring 当中,类直接需要配合扫描路径使用,不然无法成功注入(此处是单例模式,一个类只有一份实例)。

  • 使用方法注解 @Bean 【注意事项:必须配合类注解一起使用】

2. Bean 对象的命名规则

类首字母大写和类首字母小写及第二个字母小写,都是默认都是通过首字母小写作为name 来获取 Bean ;如果类首字母和第二个字母都是大写,那么直接使用原类名作为name 来获取 Bean 。

3. 从Spring 中获取bean 对象:

  • 属性注入 
  • 构造方法注入(官方推荐)
  • Setter 注入

 4. 注入的关键字:

@Autowired 和 @Resource

5. @Autowired 与 @Resource 的区别:

  • 出生不同:Autowired 出自Spring  ,Resource 出自 JDK
  • 参数不同:@Resource 支持更多的参数,例如可以在获取的时候给 Bean 对象取名字
  • 功能的不同:@Autowired 支持属性注入,构造方法注入和 Setter 方法注入,@Resource 只支持属性注入和 Setter 方法注入。

6. 解决@Bean 方法注解注入多个同类型 Bean 对象的报错:

  • 使用 @Resource(name= " 方法名")
  • 使用 @Qualifier (value = "方法名")

人生的旅途中,前面还有无数个日子需要别无选择地去面对。

你可能感兴趣的:(JAVA,spring,java,后端)