第四章:依赖注入——从 @Resource 到五种注入策略

第四章:依赖注入——从 @Resource 到五种注入策略

本章将深入剖析 Jfire 的依赖注入机制,理解 InjectHandler 的设计思想,掌握五种注入策略的实现原理和适用场景,以及配置属性注入的完整实现。

4.1 依赖注入概述

依赖注入(Dependency Injection,DI)是 IOC 容器的核心功能之一。它让对象不再主动创建或查找依赖,而是由容器自动注入所需的依赖。

4.1.1 Jfire 的注入方式

Jfire 采用 字段注入(Field Injection)方式:

@Resource
public class UserService {

    @Resource  // 字段注入
    private UserDao userDao;

    @Resource
    private OrderDao orderDao;
}

与构造器注入和 Setter 注入相比,字段注入的优缺点:

方式 优点 缺点
字段注入 代码简洁、使用方便 无法注入 final 字段、不便于测试
构造器注入 依赖明确、支持不可变对象 参数多时构造器臃肿
Setter 注入 灵活、可选依赖 代码冗余

4.1.2 注入处理器架构

Jfire 使用 策略模式 处理不同类型字段的注入:

                    InjectHandler (接口)
                          │
           ┌──────────────┴──────────────┐
           │                             │
DefaultDependencyInjectHandler    DefaultPropertyInjectHandler
    (Bean 依赖注入)                  (配置属性注入)
           │
     ┌─────┼─────┬─────────┬──────────┐
     │     │     │         │          │
     ▼     ▼     ▼         ▼          ▼
  Instance  Abstract  Collection   Map    BeanHolder
  Inject    Inject    Inject      Inject   Inject

4.2 InjectHandler 接口设计

让我们先看核心接口的定义:

// 源码:cc/jfire/jfire/core/inject/InjectHandler.java

/**
 * 注入处理器。可能注入的是参数,也可能是依赖
 */
public interface InjectHandler {
    /**
     * 初始化注入处理器
     * @param field 需要注入的字段
     * @param context 应用上下文
     */
    void init(Field field, ApplicationContext context);

    /**
     * 执行注入操作
     * @param instance 目标对象实例
     */
    void inject(Object instance);

    /**
     * 自定义注入处理器注解
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface CustomInjectHanlder {
        Class<InjectHandler> value();
    }
}

4.2.1 接口方法说明

方法 调用时机 职责
init() Bean 注册时 分析字段类型,准备注入所需的信息
inject() Bean 创建时 执行实际的字段赋值操作

4.2.2 为什么分为两个阶段?

这种设计有重要意义:

  1. 性能优化init() 只执行一次,而 inject() 可能执行多次(原型 Bean)
  2. 避免循环依赖init() 时只获取 BeanRegisterInfo,不获取实际实例
  3. 提前发现错误:注入问题在容器启动时就能发现,而非运行时

4.3 DefaultDependencyInjectHandler:依赖注入处理器

这是最核心的注入处理器,负责处理 @Resource 注解标注的字段:

// 源码:cc/jfire/jfire/core/inject/impl/DefaultDependencyInjectHandler.java

public class DefaultDependencyInjectHandler implements InjectHandler {
    private ApplicationContext context;
    private Inject        inject;        // 具体的注入策略
    private ValueAccessor valueAccessor; // 字段访问器
    private Field         field;

    @Override
    public void init(Field field, ApplicationContext context) {
        this.field = field;

        // 基本类型不支持注入
        if (field.getType().isPrimitive()) {
            throw new UnsupportedOperationException("基础类型无法执行注入操作");
        }

        this.context = context;
        valueAccessor = ValueAccessor.standard(field);
        Class<?> fieldType = field.getType();

        // 根据字段类型选择注入策略
        if (Map.class.isAssignableFrom(fieldType)) {
            inject = new MapInject();
        } else if (Collection.class.isAssignableFrom(fieldType)) {
            inject = new CollectionInject();
        } else if (BeanHolder.class.isAssignableFrom(fieldType)) {
            inject = new BeanHolderInject();
        } else if (fieldType.isInterface() || Modifier.isAbstract(fieldType.getModifiers())) {
            inject = new AbstractInject();
        } else {
            inject = new InstacenInject();
        }
    }

    @Override
    public void inject(Object instance) {
        inject.inject(instance);
    }

    // 内部策略接口
    interface Inject {
        void inject(Object instance);
    }

    // ... 五种注入策略的实现
}

4.3.1 策略选择流程

                  字段类型判断
                       │
          ┌────────────┼────────────────────┐
          │            │                    │
          ▼            ▼                    │
    是 Map 类型?   是 Collection 类型?     │
     ┌──┴──┐         ┌──┴──┐               │
    YES    NO       YES    NO              │
     │      │        │      │              │
     ▼      │        ▼      │              │
  MapInject │   CollectionInject           │
            │               │              │
            └───────┬───────┘              │
                    │                      │
                    ▼                      │
           是 BeanHolder 类型?             │
              ┌──┴──┐                      │
             YES    NO                     │
              │      │                     │
              ▼      │                     │
       BeanHolderInject                    │
                     │                     │
                     ▼                     │
            是接口或抽象类?                 │
              ┌──┴──┐                      │
             YES    NO                     │
              │      │                     │
              ▼      ▼                     │
        AbstractInject  InstacenInject     │
                              │            │
                              └────────────┘

4.4 五种注入策略详解

4.4.1 InstacenInject:具体类型注入

这是最基本的注入策略,用于注入具体类型的 Bean:

// 源码:DefaultDependencyInjectHandler.java 内部类

class InstacenInject implements Inject {
    private BeanRegisterInfo beanRegisterInfo;

    InstacenInject() {
        AnnotationContext annotationContext = AnnotationContext.getInstanceOn(field);
        Resource resource = annotationContext.getAnnotation(Resource.class);

        // 确定 Bean 名称:优先用注解指定的名称,否则用字段类型的全限定名
        String beanName = StringUtil.isNotBlank(resource.name())
            ? resource.name()
            : field.getType().getName();

        // 先按名称查找
        beanRegisterInfo = context.getBeanRegisterInfo(beanName);

        // 名称找不到,再按类型查找
        if (beanRegisterInfo == null) {
            beanRegisterInfo = context.getBeanRegisterInfo(field.getType());
        }

        // 都找不到,检查是否允许为空
        if (beanRegisterInfo == null &&
            !annotationContext.isAnnotationPresent(CanBeNull.class)) {
            throw new InjectValueException(
                "无法找到属性:" + field.getDeclaringClass().getSimpleName()
                + "." + field.getName() + "可以注入的bean");
        }
    }

    public void inject(Object instance) {
        Object value = beanRegisterInfo.get().getBean();
        try {
            valueAccessor.setObject(instance, value);
        } catch (Exception e) {
            throw new InjectValueException(e);
        }
    }
}

使用场景

@Resource
public class OrderService {

    @Resource  // 具体类型,使用 InstacenInject
    private OrderDaoImpl orderDao;

    @Resource(name = "primaryDataSource")  // 指定名称
    private HikariDataSource dataSource;
}

查找顺序

  1. 如果 @Resource(name = "xxx") 指定了名称,按名称查找
  2. 否则,按字段类型的全限定名查找
  3. 如果按名称找不到,再按类型查找
  4. 都找不到,检查是否有 @CanBeNull 注解

4.4.2 AbstractInject:接口与抽象类注入

当字段类型是接口或抽象类时,需要查找其实现类:

// 源码:DefaultDependencyInjectHandler.java 内部类

class AbstractInject implements Inject {
    BeanRegisterInfo beanRegisterInfo;

    AbstractInject() {
        Class<?> fieldType = field.getType();
        AnnotationContext annotationContext = AnnotationContext.getInstanceOn(field);
        Resource resource = annotationContext.getAnnotation(Resource.class);

        // 如果定义了名称,就寻找特定名称的 Bean
        if (StringUtil.isNotBlank(resource.name())) {
            beanRegisterInfo = context.getBeanRegisterInfo(resource.name());
            if (beanRegisterInfo == null &&
                !annotationContext.isAnnotationPresent(CanBeNull.class)) {
                throw new BeanDefinitionCanNotFindException(resource.name());
            }
        } else {
            // 查找所有匹配的实现类
            Collection<BeanRegisterInfo> beanRegisterInfos =
                context.getBeanRegisterInfos(fieldType);

            if (beanRegisterInfos.size() > 1) {
                // 多个实现类,查找 @Primary 标注的
                List<BeanRegisterInfo> primary = beanRegisterInfos.stream()
                    .filter(info -> AnnotationContext.isAnnotationPresent(
                        Primary.class, info.getType()))
                    .toList();

                if (primary.size() != 1) {
                    throw new BeanDefinitionCanNotFindException(
                        beanRegisterInfos, fieldType);
                } else {
                    beanRegisterInfo = primary.get(0);
                }
            } else if (beanRegisterInfos.size() == 1) {
                // 只有一个实现类,直接使用
                beanRegisterInfo = beanRegisterInfos.iterator().next();
            } else if (annotationContext.isAnnotationPresent(CanBeNull.class)) {
                // 没有实现类,但允许为空
            } else {
                throw new BeanDefinitionCanNotFindException(
                    beanRegisterInfos, fieldType);
            }
        }
    }

    @Override
    public void inject(Object instance) {
        if (beanRegisterInfo != null) {
            Object value = beanRegisterInfo.get().getBean();
            try {
                valueAccessor.setObject(instance, value);
            } catch (Exception e) {
                throw new InjectValueException(e);
            }
        }
    }
}

使用场景

// 定义接口
public interface PaymentService {
    void pay(Order order);
}

// 多个实现类
@Resource
public class AlipayServiceImpl implements PaymentService { ... }

@Resource
@Primary  // 标记为首选实现
public class WechatPayServiceImpl implements PaymentService { ... }

// 注入接口类型
@Resource
public class OrderService {

    @Resource  // 接口类型,使用 AbstractInject
    private PaymentService paymentService;  // 注入 WechatPayServiceImpl
}

@Primary 注解

// 源码:cc/jfire/jfire/core/prepare/annotation/configuration/Primary.java

/**
 * 当一个接口有多个实现的时候,如果进行依赖注入,则选择有该注解的实现
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Primary {}

4.4.3 CollectionInject:集合类型注入

当字段是 ListSet 类型时,注入所有匹配的 Bean:

// 源码:DefaultDependencyInjectHandler.java 内部类

class CollectionInject implements Inject {
    private static final int LIST = 1;
    private static final int SET  = 2;
    private final BeanRegisterInfo[] beanRegisterInfos;
    private int listOrSet = 0;

    CollectionInject() {
        Type genericType = field.getGenericType();
        if (!(genericType instanceof ParameterizedType)) {
            throw new InjectTypeException(
                field.toGenericString() + "不是泛型定义,无法找到需要注入的Bean类型");
        }

        // 获取泛型参数类型
        Class<?> rawType = (Class<?>) ((ParameterizedType) genericType)
            .getActualTypeArguments()[0];

        // 查找所有匹配的 Bean
        beanRegisterInfos = context.getBeanRegisterInfos(rawType)
            .stream()
            .toArray(BeanRegisterInfo[]::new);

        // 判断是 List 还是 Set
        if (List.class.isAssignableFrom(field.getType())) {
            listOrSet = LIST;
        } else if (Set.class.isAssignableFrom(field.getType())) {
            listOrSet = SET;
        }
    }

    @Override
    public void inject(Object instance) {
        try {
            // 获取字段当前值
            Collection<Object> value = (Collection<Object>) valueAccessor.get(instance);

            // 如果为空,创建默认集合
            if (value == null) {
                if (listOrSet == LIST) {
                    value = new LinkedList<Object>();
                    valueAccessor.setObject(instance, value);
                } else if (listOrSet == SET) {
                    value = new HashSet<Object>();
                    valueAccessor.setObject(instance, value);
                } else {
                    throw new InjectValueException(
                        "无法识别类型:" + field.getType().getName());
                }
            }

            // 添加所有匹配的 Bean
            for (BeanRegisterInfo each : beanRegisterInfos) {
                value.add(each.get().getBean());
            }
        } catch (Exception e) {
            throw new InjectValueException(e);
        }
    }
}

使用场景

// 多个处理器实现
@Resource
public class LogHandler implements MessageHandler { ... }

@Resource
public class EmailHandler implements MessageHandler { ... }

@Resource
public class SmsHandler implements MessageHandler { ... }

// 注入所有处理器
@Resource
public class MessageDispatcher {

    @Resource  // 集合注入,使用 CollectionInject
    private List<MessageHandler> handlers;  // 包含所有 3 个处理器

    public void dispatch(Message message) {
        for (MessageHandler handler : handlers) {
            handler.handle(message);
        }
    }
}

4.4.4 MapInject:Map 类型注入

当字段是 Map 类型时,将匹配的 Bean 以键值对形式注入:

// 源码:DefaultDependencyInjectHandler.java 内部类

class MapInject implements Inject {
    MapKeyType         mapKeyType;
    BeanRegisterInfo[] beanRegisterInfos;
    Method             method;

    MapInject() {
        Type genericType = field.getGenericType();
        if (!(genericType instanceof ParameterizedType)) {
            throw new InjectTypeException(
                field.toGenericString() + "不是泛型定义,无法找到需要注入的Bean类型");
        }

        // 获取 Map 的 Value 类型(第二个泛型参数)
        Class<?> rawType = (Class<?>) ((ParameterizedType) genericType)
            .getActualTypeArguments()[1];

        // 查找所有匹配的 Bean
        beanRegisterInfos = context.getBeanRegisterInfos(rawType)
            .stream()
            .toArray(BeanRegisterInfo[]::new);

        AnnotationContext annotationContext = AnnotationContext.getInstanceOn(field);

        // 确定 Map Key 的生成方式
        if (annotationContext.isAnnotationPresent(MapKeyMethodName.class)) {
            mapKeyType = MapKeyType.METHOD;
            String methodName = annotationContext
                .getAnnotation(MapKeyMethodName.class).value();
            try {
                method = rawType.getMethod(methodName);
            } catch (Exception e) {
                throw new MapKeyMethodCanNotFindException(methodName, rawType, e);
            }
        } else if (annotationContext.isAnnotationPresent(MapKeyBySimpleClassName.class)) {
            mapKeyType = MapKeyType.SIMPLE_CLASSNAME;
        } else {
            mapKeyType = MapKeyType.BEAN_NAME;
        }
    }

    @Override
    public void inject(Object instance) {
        try {
            Map<Object, Object> value = (Map<Object, Object>) valueAccessor.get(instance);
            if (value == null) {
                value = new HashMap<>();
                valueAccessor.setObject(instance, value);
            }

            switch (mapKeyType) {
                case SIMPLE_CLASSNAME -> {
                    // Key 为类的简单名称
                    for (BeanRegisterInfo each : beanRegisterInfos) {
                        Object entryValue = each.get().getBean();
                        String entryKey = each.getType().getSimpleName();
                        value.put(entryKey, entryValue);
                    }
                }
                case METHOD -> {
                    // Key 为指定方法的返回值
                    for (BeanRegisterInfo each : beanRegisterInfos) {
                        Object entryValue = each.get().getBean();
                        Object entryKey = method.invoke(entryValue);
                        value.put(entryKey, entryValue);
                    }
                }
                case BEAN_NAME -> {
                    // Key 为 Bean 名称(默认)
                    for (BeanRegisterInfo each : beanRegisterInfos) {
                        Object entryValue = each.get().getBean();
                        String entryKey = each.getBeanName();
                        value.put(entryKey, entryValue);
                    }
                }
            }
        } catch (Exception e) {
            throw new InjectValueException(e);
        }
    }
}

// Map Key 类型枚举
enum MapKeyType {
    BEAN_NAME,          // Bean 名称作为 Key
    SIMPLE_CLASSNAME,   // 类简单名称作为 Key
    METHOD              // 方法返回值作为 Key
}

三种 Key 生成方式

注解 Key 来源 示例
(默认) Bean 名称 "com.example.AlipayServiceImpl"
@MapKeyBySimpleClassName 类简单名称 "AlipayServiceImpl"
@MapKeyMethodName("xxx") 方法返回值 自定义

使用场景

// 定义接口,包含 Key 方法
public interface PaymentService {
    String getPayType();  // 返回支付类型作为 Key
    void pay(Order order);
}

@Resource
public class AlipayServiceImpl implements PaymentService {
    @Override
    public String getPayType() { return "alipay"; }
    // ...
}

@Resource
public class WechatPayServiceImpl implements PaymentService {
    @Override
    public String getPayType() { return "wechat"; }
    // ...
}

// 按方法返回值作为 Key
@Resource
public class PaymentRouter {

    @Resource
    @MapKeyMethodName("getPayType")  // 使用 getPayType() 返回值作为 Key
    private Map<String, PaymentService> paymentServices;
    // 结果:{"alipay": AlipayServiceImpl, "wechat": WechatPayServiceImpl}

    public void route(String payType, Order order) {
        PaymentService service = paymentServices.get(payType);
        service.pay(order);
    }
}
// 按类简单名称作为 Key
@Resource
public class HandlerManager {

    @Resource
    @MapKeyBySimpleClassName  // 使用类简单名称作为 Key
    private Map<String, MessageHandler> handlers;
    // 结果:{"LogHandler": ..., "EmailHandler": ..., "SmsHandler": ...}
}

4.4.5 BeanHolderInject:延迟注入

BeanHolder 用于解决特殊场景:获取 Bean 自身的代理对象。

// 源码:cc/jfire/jfire/core/inject/BeanHolder.java

/**
 * Bean持有者接口,用于获取当前Bean的代理对象
 */
public interface BeanHolder<T> {
    /**
     * 返回当前Bean对象
     */
    T getSelf();
}

注入实现

// 源码:DefaultDependencyInjectHandler.java 内部类

class BeanHolderInject implements Inject {
    private BeanRegisterInfo beanRegisterInfo;

    public BeanHolderInject() {
        Class<?> declaringClass = field.getDeclaringClass();
        // 获取泛型参数类型
        Type actualTypeArgument = ((ParameterizedType) field.getGenericType())
            .getActualTypeArguments()[0];

        // 泛型必须是当前类本身
        if (declaringClass.equals(actualTypeArgument) == false) {
            throw new IllegalArgumentException(
                "BeanHolder字段的泛型必须要匹配该字段所在的类");
        }

        beanRegisterInfo = context.getBeanRegisterInfo(declaringClass);
    }

    @Override
    public void inject(Object instance) {
        Object bean = beanRegisterInfo.get().getBean();  // 获取代理对象
        BeanHolderImpl<Object> beanHolder = new BeanHolderImpl<>(bean);
        valueAccessor.setObject(instance, beanHolder);
    }
}

// BeanHolder 实现
class BeanHolderImpl<T> implements BeanHolder<T> {
    private final T instance;

    BeanHolderImpl(T instance) {
        this.instance = instance;
    }

    @Override
    public T getSelf() {
        return instance;
    }
}

使用场景

为什么需要 BeanHolder?考虑这个场景:

@Resource
public class UserService {

    @Resource
    private BeanHolder<UserService> self;  // 持有自己的代理对象

    @Transactional
    public void saveUser(User user) {
        // ... 保存用户
    }

    public void batchSave(List<User> users) {
        for (User user : users) {
            // 直接调用 this.saveUser() 不会触发事务代理
            // this.saveUser(user);  // 错误!

            // 通过 self 调用,才能触发事务
            self.getSelf().saveUser(user);  // 正确!
        }
    }
}

原理

  • this 是原始对象,调用方法不经过 AOP 代理
  • self.getSelf() 返回的是 AOP 增强后的代理对象
  • 通过代理对象调用才能触发事务、日志等增强逻辑

4.5 配置属性注入:DefaultPropertyInjectHandler

除了依赖注入,Jfire 还支持从配置文件注入属性值:

// 源码:cc/jfire/jfire/core/inject/impl/DefaultPropertyInjectHandler.java

public class DefaultPropertyInjectHandler implements InjectHandler {
    private ValueAccessor valueAccessor;
    private Object        propertyValue;   // 属性值
    private String        propertyName;    // 属性名
    private Inject        inject;          // 注入策略

    @Override
    public void init(Field field, ApplicationContext applicationContext) {
        valueAccessor = ValueAccessor.standard(field);
        AnnotationContext annotationContext = AnnotationContext.getInstanceOn(field);
        PropertyRead propertyRead = annotationContext.getAnnotation(PropertyRead.class);

        // 确定属性名:优先用注解指定的,否则用字段名
        propertyName = StringUtil.isNotBlank(propertyRead.value())
            ? propertyRead.value()
            : field.getName();

        // 获取配置
        Map<String, Object> config = applicationContext.getConfig().fullPathConfig();

        // 优先读取系统属性(-D 参数)
        if (StringUtil.isNotBlank(System.getProperty(propertyName))) {
            propertyValue = System.getProperty(propertyName);
        }
        // 再读取配置文件
        else if (config.containsKey(propertyName)) {
            propertyValue = config.get(propertyName);
        }

        // 根据字段类型选择转换策略
        if (propertyValue != null) {
            Class<?> type = field.getType();
            if (type == int.class || type == Integer.class) {
                inject = new IntInject();
            } else if (type == String.class) {
                inject = new StringInject();
            } else if (type == boolean.class || type == Boolean.class) {
                inject = new BooleanInject();
            }
            // ... 更多类型
        }
    }

    @Override
    public void inject(Object instance) {
        if (inject == null) {
            return;  // 没有配置值,不注入
        }
        inject.inject(instance);
    }
}

4.5.1 支持的属性类型

类型 转换策略 示例
int / Integer IntInject "8080"8080
long / Long LongInject "1000000"1000000L
boolean / Boolean BooleanInject "true"true
String StringInject 直接使用
String[] StringArrayInject "a,b,c"["a","b","c"]
int[] IntArrayInject "1,2,3"[1,2,3]
Set<String> SetStringInject "a,b,c"Set.of("a","b","c")
Map MapInject YAML Map 直接使用
File FileInject "/path/to/file"new File(...)
Class ClassInject "com.example.MyClass"Class
byte[] ByteArrayInject Base64 或 16 进制字符串
枚举类型 EnumInject "ENABLED"Status.ENABLED

4.5.2 使用示例

配置文件(application.yml)

server:
  port: 8080
  host: localhost

app:
  name: MyApplication
  debug: true
  allowedOrigins: http://localhost,http://example.com

注入配置

@Resource
public class ServerConfig {

    @PropertyRead("server.port")
    private int port;  // 8080

    @PropertyRead("server.host")
    private String host;  // "localhost"

    @PropertyRead("app.debug")
    private boolean debug;  // true

    @PropertyRead("app.allowedOrigins")
    private String[] allowedOrigins;  // ["http://localhost", "http://example.com"]

    @PropertyRead  // 默认使用字段名
    private String name;  // 需要配置中有 "name" 键
}

4.5.3 属性优先级

系统属性(-Dxxx=yyy)  >  配置文件  >  默认值
// 可以通过 JVM 参数覆盖配置
// java -Dserver.port=9090 -jar app.jar
// 此时 port 为 9090,而非配置文件中的 8080

4.6 @CanBeNull:允许空值注入

当依赖可能不存在时,使用 @CanBeNull 避免启动失败:

// 源码:cc/jfire/jfire/core/inject/notated/CanBeNull.java

/**
 * 使用该注解表明该属性的注入可以允许为空
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Documented
@Inherited
public @interface CanBeNull {}

使用场景

@Resource
public class NotificationService {

    @Resource
    @CanBeNull  // 如果没有 EmailSender 的 Bean,不会报错
    private EmailSender emailSender;

    public void notify(String message) {
        if (emailSender != null) {
            emailSender.send(message);
        } else {
            // 降级处理
            System.out.println("Email service not available: " + message);
        }
    }
}

4.7 循环依赖与注入处理器

在第二章中,我们提到了循环依赖问题。注入处理器的设计在解决循环依赖中起到关键作用。

4.7.1 关键设计:持有 BeanRegisterInfo 而非实例

源码注释清楚地说明了这一点:

// 源码:DefaultDependencyInjectHandler.java init() 方法

/**
 * 需要注意,在生成注入处理器的过程中,会发现类之间存在循环依赖。
 * Inject的实例如果在初始化的时候就获取对应类的BeanDefinition实例,会导致死循环。
 * 因为下一个类依赖于本类,所以下一个类的注入属性也需要持有本类的BeanDefinition,
 * 但是此时本类的BeanDefinition还在生成中。
 *
 * 解决的办法很简单,就是Inject的实例初始化时持有所需要注入属性对应的
 * BeanRegisterInfo实例即可。在运行期在逐步创建对应的实例。
 */

4.7.2 运行时逐步创建

init() 阶段:
┌─────────────────┐      ┌─────────────────┐
│   ServiceA      │      │   ServiceB      │
│                 │      │                 │
│ inject.init()   │      │ inject.init()   │
│ 持有 B 的       │      │ 持有 A 的       │
│ RegisterInfo    │      │ RegisterInfo    │
└─────────────────┘      └─────────────────┘
    不需要 B 的实例         不需要 A 的实例

inject() 阶段(运行时):
创建 A 实例
    │
    ▼
inject A 的依赖 → 需要 B 实例 → 创建 B 实例
                                    │
                                    ▼
                    inject B 的依赖 → 需要 A 实例
                                    → 从临时缓存获取

4.8 注入处理器的生成

DefaultBeanRegisterInfo 中生成注入处理器:

// 源码:DefaultBeanRegisterInfo.java

private InjectHandler[] generateInjectHandlers() {
    if (type.isInterface()) {
        return new InjectHandler[0];  // 接口没有字段
    }

    // 获取所有字段(包括父类)
    Collection<Field> allFields = getAllFields(type);
    List<InjectHandler> list = new LinkedList<>();

    // 1. 自定义注入处理器
    buildCustomInjectHandlers(allFields, list);

    // 2. @Resource 依赖注入处理器
    buildResourceInjectHandlers(allFields, list);

    // 3. @PropertyRead 属性注入处理器
    buildPropertyReadInjectHandlers(allFields, list);

    return list.toArray(new InjectHandler[list.size()]);
}

// 构建 @Resource 注入处理器
private void buildResourceInjectHandlers(Collection<Field> allFields,
                                         List<InjectHandler> list) {
    list.addAll(getInjectHandlers(
        allFields,
        field -> field.isAnnotationPresent(Resource.class),  // 过滤条件
        field -> {  // 创建处理器
            try {
                DefaultDependencyInjectHandler injectHandler =
                    new DefaultDependencyInjectHandler();
                injectHandler.init(field, context);
                return injectHandler;
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
    ));
}

4.9 本章小结

本章我们深入学习了 Jfire 的依赖注入机制:

  1. InjectHandler 接口

    • init():初始化阶段,分析字段并准备注入信息
    • inject():运行时阶段,执行实际注入
  2. 五种依赖注入策略

    • InstacenInject:具体类型注入
    • AbstractInject:接口/抽象类注入,支持 @Primary
    • CollectionInject:List/Set 集合注入
    • MapInject:Map 类型注入,支持多种 Key 生成方式
    • BeanHolderInject:延迟注入,获取自身代理对象
  3. 配置属性注入

    • @PropertyRead 注解
    • 支持 12+ 种类型的自动转换
    • 系统属性优先于配置文件
  4. 辅助注解

    • @CanBeNull:允许空值
    • @Primary:首选实现
    • @MapKeyBySimpleClassName:类名作为 Map Key
    • @MapKeyMethodName:方法返回值作为 Map Key
  5. 循环依赖解决

    • 持有 BeanRegisterInfo 而非实例
    • 运行时逐步创建

下一章预告:我们将进入 AOP 模块,学习 Jfire 的五种增强方式(@Before、@After、@Around、@AfterReturning、@AfterThrowable),理解增强管理器的设计和使用。

思考题

  1. 为什么 InstacenInject 先按名称查找,再按类型查找?这样设计有什么好处?

  2. 如果一个接口有多个实现类,但都没有标注 @Primary,会发生什么?如何解决?

  3. BeanHolder 的泛型为什么必须是当前类本身?如果允许任意类型会有什么问题?

  4. 如何实现一个自定义的 InjectHandler,实现从 Redis 获取配置的功能?

核心源码清单

文件 路径 核心内容
InjectHandler.java core/inject/ 注入处理器接口
DefaultDependencyInjectHandler.java core/inject/impl/ 依赖注入处理器(5 种策略)
DefaultPropertyInjectHandler.java core/inject/impl/ 配置属性注入处理器
BeanHolder.java core/inject/ Bean 持有者接口
CanBeNull.java core/inject/notated/ 允许空值注解
PropertyRead.java core/inject/notated/ 配置属性读取注解
MapKeyBySimpleClassName.java core/inject/notated/ 类名作为 Key 注解
MapKeyMethodName.java core/inject/notated/ 方法返回值作为 Key 注解
Primary.java core/prepare/annotation/configuration/ 首选实现注解
Java 进阶提升,手写系列 文章被收录于专栏

专栏以轻量级 Java 框架 Jfire 为蓝本,带你从零手写一个完整的 IOC 容器。专栏共 10 章,涵盖 IOC 容器核心原理:Bean 定义与生命周期、Bean 工厂设计、五种依赖注入策略、循环依赖解决方案;深入 AOP 实现:五种增强方式、字节码动态生成技术;以及企业级特性:声明式事务管理(四种传播级别)、声明式缓存框架、条件注解与自动配置机制。

全部评论
这是把书搬到这里了?
点赞 回复 分享
发布于 12-17 21:23 陕西

相关推荐

头像
12-08 09:58
复旦大学 Java
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务