第四章:依赖注入——从 @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 为什么分为两个阶段?
这种设计有重要意义:
- 性能优化:
init()只执行一次,而inject()可能执行多次(原型 Bean) - 避免循环依赖:
init()时只获取BeanRegisterInfo,不获取实际实例 - 提前发现错误:注入问题在容器启动时就能发现,而非运行时
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;
}
查找顺序:
- 如果
@Resource(name = "xxx")指定了名称,按名称查找 - 否则,按字段类型的全限定名查找
- 如果按名称找不到,再按类型查找
- 都找不到,检查是否有
@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:集合类型注入
当字段是 List 或 Set 类型时,注入所有匹配的 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 的依赖注入机制:
-
InjectHandler 接口
init():初始化阶段,分析字段并准备注入信息inject():运行时阶段,执行实际注入
-
五种依赖注入策略
InstacenInject:具体类型注入AbstractInject:接口/抽象类注入,支持 @PrimaryCollectionInject:List/Set 集合注入MapInject:Map 类型注入,支持多种 Key 生成方式BeanHolderInject:延迟注入,获取自身代理对象
-
配置属性注入
@PropertyRead注解- 支持 12+ 种类型的自动转换
- 系统属性优先于配置文件
-
辅助注解
@CanBeNull:允许空值@Primary:首选实现@MapKeyBySimpleClassName:类名作为 Map Key@MapKeyMethodName:方法返回值作为 Map Key
-
循环依赖解决
- 持有
BeanRegisterInfo而非实例 - 运行时逐步创建
- 持有
下一章预告:我们将进入 AOP 模块,学习 Jfire 的五种增强方式(@Before、@After、@Around、@AfterReturning、@AfterThrowable),理解增强管理器的设计和使用。
思考题
-
为什么
InstacenInject先按名称查找,再按类型查找?这样设计有什么好处? -
如果一个接口有多个实现类,但都没有标注
@Primary,会发生什么?如何解决? -
BeanHolder的泛型为什么必须是当前类本身?如果允许任意类型会有什么问题? -
如何实现一个自定义的
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 框架 Jfire 为蓝本,带你从零手写一个完整的 IOC 容器。专栏共 10 章,涵盖 IOC 容器核心原理:Bean 定义与生命周期、Bean 工厂设计、五种依赖注入策略、循环依赖解决方案;深入 AOP 实现:五种增强方式、字节码动态生成技术;以及企业级特性:声明式事务管理(四种传播级别)、声明式缓存框架、条件注解与自动配置机制。