实现简易版 Spring IOC 容器
实现简易版 Spring IOC 容器
核心目标
通过模拟 Spring 6.x 的核心功能,构建一个简易的 IOC 容器。主要包含以下功能:
- 使用自定义注解进行类扫描(如
@CustomComponent和@CustomConfiguration)。 - 封装 Bean 的元信息(类似于 Spring 的
BeanDefinition)。 - 单例 Bean 的实例化。
- 通过
@CustomAutowired实现依赖注入。
第一步:定义核心注解
以下是用于标识组件和依赖注入的注解:
import java.lang.annotation.*;
// 自定义 @Component 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomComponent {
String value() default "";
}
// 自定义 @Autowired 注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAutowired {
}
// 自定义 @Configuration 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomConfiguration {
String scanPackage();
}
第二步:封装 Bean 元信息
创建一个类来存储 Bean 的相关信息,例如对应的类、作用域等。
public class CustomBeanDefinition {
private Class<?> beanClass;
private String scope = "singleton";
private boolean lazyInit = false;
public CustomBeanDefinition(Class<?> beanClass) {
this.beanClass = beanClass;
}
// Getter 和 Setter 方法
public Class<?> getBeanClass() {
return beanClass;
}
public void setBeanClass(Class<?> beanClass) {
this.beanClass = beanClass;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public boolean isLazyInit() {
return lazyInit;
}
public void setLazyInit(boolean lazyInit) {
this.lazyInit = lazyInit;
}
}
第三步:实现核心 IOC 容器
构建一个简易的容器类,完成扫描、注册、实例化和依赖注入等功能。
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class CustomApplicationContext {
private Map<String, CustomBeanDefinition> beanDefinitionMap = new HashMap<>();
private Map<String, Object> singletonObjects = new HashMap<>();
private Class<?> configClass;
public CustomApplicationContext(Class<?> configClass) {
this.configClass = configClass;
scanAndRegisterBeans();
instantiateSingletonBeans();
}
private void scanAndRegisterBeans() {
CustomConfiguration configAnnotation = configClass.getAnnotation(CustomConfiguration.class);
String scanPackage = configAnnotation.scanPackage();
String packagePath = scanPackage.replace(".", "/");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource(packagePath);
if (resource == null) {
throw new RuntimeException("未找到指定包:" + scanPackage);
}
File packageDir = new File(resource.getFile());
recursiveScan(packageDir, scanPackage);
}
private void recursiveScan(File dir, String basePackage) {
if (!dir.isDirectory()) {
return;
}
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
String subPackage = basePackage + "." + file.getName();
recursiveScan(file, subPackage);
} else if (file.getName().endsWith(".class")) {
processClassFile(file, basePackage);
}
}
}
private void processClassFile(File classFile, String basePackage) {
try {
String className = basePackage + "." + classFile.getName().replace(".class", "");
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(CustomComponent.class)) {
CustomComponent component = clazz.getAnnotation(CustomComponent.class);
String beanName = component.value().isEmpty()
? toLowerFirstChar(clazz.getSimpleName())
: component.value();
CustomBeanDefinition beanDefinition = new CustomBeanDefinition(clazz);
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException("加载类失败:" + e.getMessage(), e);
}
}
private void instantiateSingletonBeans() {
for (String beanName : beanDefinitionMap.keySet()) {
CustomBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if ("singleton".equals(beanDefinition.getScope()) && !beanDefinition.isLazyInit()) {
Object beanInstance = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, beanInstance);
}
}
}
private Object createBean(String beanName, CustomBeanDefinition beanDefinition) {
try {
Class<?> beanClass = beanDefinition.getBeanClass();
Object beanInstance = beanClass.getDeclaredConstructor().newInstance();
injectDependencies(beanInstance);
return beanInstance;
} catch (Exception e) {
throw new RuntimeException("创建 Bean 失败:" + beanName, e);
}
}
private void injectDependencies(Object beanInstance) {
Field[] fields = beanInstance.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(CustomAutowired.class)) {
field.setAccessible(true);
Class<?> fieldType = field.getType();
Object dependency = findDependencyByType(fieldType);
if (dependency == null) {
throw new RuntimeException("未找到依赖 Bean:" + fieldType.getName());
}
try {
field.set(beanInstance, dependency);
} catch (IllegalAccessException e) {
throw new RuntimeException("依赖注入失败:" + field.getName(), e);
}
}
}
}
private Object findDependencyByType(Class<?> type) {
for (Object bean : singletonObjects.values()) {
if (type.isInstance(bean)) {
return bean;
}
}
return null;
}
public <T> T getBean(Class<T> clazz) {
for (Object bean : singletonObjects.values()) {
if (clazz.isInstance(bean)) {
return (T) bean;
}
}
throw new RuntimeException("未找到 Bean:" + clazz.getName());
}
private String toLowerFirstChar(String str) {
if (str == null || str.isEmpty()) {
return str;
}
char[] chars = str.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
}
第四步:测试代码
1. 编写业务类
@CustomComponent
public class UserRepository {
public void queryUser() {
System.out.println("查询用户数据...");
}
}
@CustomComponent
public class UserService {
@CustomAutowired
private UserRepository userRepository;
public void getUser() {
userRepository.queryUser();
System.out.println("处理用户请求...");
}
}
2. 配置类
@CustomConfiguration(scanPackage = "com.example.demo")
public class AppConfig {
}
3. 测试类
public class TestApp {
public static void main(String[] args) {
CustomApplicationContext context = new CustomApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.getUser();
}
}
输出结果
查询用户数据...
处理用户请求...
