帮我实现一个依赖注入的框架
时间: 2023-08-08 07:09:58 浏览: 146
PrismDIFullApp.zip
依赖注入(Dependency Injection,简称 DI)是一种设计模式,它的主要目的是将对象之间的依赖关系从代码中解耦,以提高代码的可维护性和可测试性。在 Java 中,常用的 DI 框架有 Spring、Guice 等。下面是一个简单的 DI 框架的示例,可以帮助你理解 DI 的实现原理。
```java
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class DIContainer {
private Map<Class<?>, Object> singletonMap = new ConcurrentHashMap<>();
public <T> T getBean(Class<T> clazz) {
T bean = (T) singletonMap.get(clazz);
if (bean == null) {
bean = createBean(clazz);
singletonMap.put(clazz, bean);
}
return bean;
}
private <T> T createBean(Class<T> clazz) {
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if (constructors.length != 1) {
throw new RuntimeException("There should be only one constructor for class " + clazz.getName());
}
Constructor<T> constructor = (Constructor<T>) constructors[0];
Object[] args = new Object[constructor.getParameterCount()];
for (int i = 0; i < args.length; i++) {
args[i] = getBean(constructor.getParameterTypes()[i]);
}
try {
T bean = constructor.newInstance(args);
injectFields(bean);
return bean;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Failed to create bean for class " + clazz.getName(), e);
}
}
private <T> void injectFields(T bean) throws IllegalAccessException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Inject.class)) {
Object value = getBean(field.getType());
field.setAccessible(true);
field.set(bean, value);
}
}
}
}
```
在上面的代码中,我们使用了反射来实现依赖注入。首先,我们定义了一个 `DIContainer` 类,它维护了一个单例对象的映射表。然后,我们通过 `getBean()` 方法获取一个类的实例。如果该类的实例已经存在于映射表中,则直接返回;否则,我们通过 `createBean()` 方法创建一个新的实例,并将其保存到映射表中。
在 `createBean()` 方法中,我们首先获取该类的构造函数,并通过反射创建一个新的实例。然后,我们遍历构造函数的参数列表,并通过递归调用 `getBean()` 方法来获取参数的实例。最后,我们调用 `injectFields()` 方法来注入该实例的成员变量。
在 `injectFields()` 方法中,我们首先获取该实例的所有成员变量,并遍历这些成员变量。如果该成员变量被 `@Inject` 注解标记,则我们通过递归调用 `getBean()` 方法来获取该成员变量的实例,并通过反射将其注入到实例中。
下面是一个示例,演示如何使用该 DI 框架:
```java
public interface MessageService {
void send(String message);
}
public class EmailService implements MessageService {
@Override
public void send(String message) {
System.out.println("Sending email message: " + message);
}
}
public class SMSService implements MessageService {
@Override
public void send(String message) {
System.out.println("Sending SMS message: " + message);
}
}
public class MyApplication {
@Inject
private MessageService messageService;
public void run() {
messageService.send("Hello, world!");
}
}
public class Main {
public static void main(String[] args) {
DIContainer container = new DIContainer();
container.getBean(MyApplication.class).run();
}
}
```
在上面的示例中,我们定义了一个 `MessageService` 接口和两个实现类 `EmailService` 和 `SMSService`。然后,我们定义了一个 `MyApplication` 类,它依赖于一个 `MessageService` 实例,并在 `run()` 方法中调用该实例的 `send()` 方法。
最后,在 `Main` 类中,我们创建了一个 `DIContainer` 实例,并通过调用 `getBean()` 方法来获取 `MyApplication` 类的实例。由于 `MyApplication` 类的成员变量被 `@Inject` 注解标记,我们的 DI 框架会自动注入一个 `MessageService` 实例到该成员变量中。最终,我们调用 `run()` 方法来执行应用程序。
阅读全文