设计思路
- 定义注解:通过自定义注解来标记需要进行依赖注入的字段或方法。例如,定义一个
@Inject
注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Inject {
}
- 扫描组件:利用类加载器和反射,扫描指定包路径下所有的类,找到带有特定注解(如
@Component
)的类,将这些类标记为需要进行依赖注入的组件。
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class ClassScanner {
public static List<Class<?>> scanPackage(String packageName) {
List<Class<?>> classes = new ArrayList<>();
String path = packageName.replace('.', '/');
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource(path);
if (url == null) {
return classes;
}
File directory = new File(url.getFile());
for (File file : directory.listFiles()) {
if (file.isDirectory()) {
classes.addAll(scanPackage(packageName + "." + file.getName()));
} else if (file.getName().endsWith(".class")) {
try {
String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
classes.add(Class.forName(className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return classes;
}
}
- 依赖查找与注入:对于扫描到的组件类,通过反射获取其字段和方法,检查是否有
@Inject
注解。如果有,查找对应的依赖实例并注入。对于字段注入,直接通过Field.set
方法设置值;对于方法注入,通过Method.invoke
方法调用相应的方法。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Injector {
private Map<Class<?>, Object> instanceMap = new HashMap<>();
public void injectDependencies() {
List<Class<?>> componentClasses = ClassScanner.scanPackage("your.package.name");
for (Class<?> componentClass : componentClasses) {
try {
Object instance = componentClass.newInstance();
instanceMap.put(componentClass, instance);
for (Field field : componentClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
field.set(instance, findDependency(field.getType()));
}
}
for (Method method : componentClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(Inject.class)) {
method.setAccessible(true);
Object[] args = new Object[method.getParameterTypes().length];
for (int i = 0; i < args.length; i++) {
args[i] = findDependency(method.getParameterTypes()[i]);
}
method.invoke(instance, args);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Object findDependency(Class<?> type) {
return instanceMap.get(type);
}
}
- 性能和资源优化:
- 缓存实例:在依赖查找过程中,使用一个
Map
来缓存已经创建的实例,避免重复创建相同的对象,提高性能。例如instanceMap
。
- 减少反射调用频率:对于频繁使用的组件,在初始化时就完成依赖注入,而不是每次使用时都通过反射进行注入。
- 延迟加载:对于一些不常用的依赖,可以采用延迟加载的策略,只有在真正需要时才进行实例化和注入。
关键代码实现要点
- 自定义注解:确保注解的
RetentionPolicy
设置为RUNTIME
,这样在运行时才能通过反射获取到注解信息。
- 类扫描:合理利用类加载器和文件系统操作,高效地扫描指定包路径下的所有类文件。注意处理嵌套包的情况。
- 反射操作:在使用反射进行字段设置和方法调用时,要注意设置访问权限,确保可以访问私有字段和方法。同时,要处理可能出现的异常,如
IllegalAccessException
、InvocationTargetException
等。
- 依赖管理:设计一个有效的依赖查找机制,能够根据类型准确找到对应的依赖实例。并且通过缓存等方式优化查找性能。