目前我们业务代码里有一种常见的模式,定义一个接口,接着通过多个handler类实现这个接口, 在面对不同的情况时通过调用不同的handler类来处理业务代码,通常这些handler类都会继承一个公用的父类,父类中会有一些公共的方法。

本文不讨论这种模式的合理性,单纯从技术角度分析实现的方案。因为在Java生态下,都是使用spring框架。所以之前的业务代码中都会把这些handler做成bean,而如何使用这些bean,实现方案千奇百怪。有交给spring ioc容器管理后,自行从context中根据bean name获取的。也有手动注入到一个大map中的。

其实官方文档里指出里Autowired注解是可以自动注入map和list的,这句话有点奇怪,我还是直接show code吧。

我使用的java版本是

openjdk 21.0.2 2024-01-16
OpenJDK Runtime Environment (build 21.0.2+13-58)
OpenJDK 64-Bit Server VM (build 21.0.2+13-58, mixed mode, sharing)

对于使用spring的ioc容器和bean而言,只需要spring-context依赖即可。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.1.4</version>
</dependency>

首先我们定义一个接口IMan和它的两个字类Teacher和Student, 为了方便我们不加入额外的逻辑和属性

public interface IMan {}
public class Teacher implements IMan{}
public class Student implements IMan{}

我们在实现一个ClassRoom类,该类使用@AutoWried注解注入一个元素类型为IMan的list和一个key为String, Value类型为IMan的map

public class ClassRoom {
    @Autowired
    IMan[] people;
    @Autowired
    Map<String, IMan> categories;
}

同时,使用xml配置自动装配和相关的bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">


    <!-- 启用自动装配 -->
    <context:annotation-config/>

    <bean name="teacher" class="org.example.Teacher"/>
    <bean name="student" class="org.example.Student"/>
    <bean name="classRoom" class="org.example.ClassRoom"/>
</beans>

在主类中,通过ClassPathXmlApplicationContext读取xml配置后,通过IOC容器获取classRoom

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-root.xml");
        ClassRoom classRoom = applicationContext.getBean("classRoom", ClassRoom.class);
        Teacher teacher = applicationContext.getBean("teacher", Teacher.class);
        Student student = applicationContext.getBean("student", Student.class);
        System.out.println("Teacher Bean: "+teacher);
        System.out.println("Student Bean: "+student);
        for (IMan man : classRoom.people) {
            System.out.println(man);
        }
        System.out.println(classRoom.categories);
    }
}

查看运行结果, 可以看到被AutoWired注解标记的list和map会自动注入相关接口的实现bean,并且对于map,它的主键会是bean的name, 值则是对应的bean

Teacher Bean: org.example.Teacher@6af93788
Student Bean: org.example.Student@ef9296d
org.example.Teacher@6af93788
org.example.Student@ef9296d
{teacher=org.example.Teacher@6af93788, student=org.example.Student@ef9296d}