【Spring之手写一个依赖注入容器】

本文详细介绍了如何在Spring框架中自定义Component和DI注解,以及如何使用这些注解来定义DAO和Service层,实现依赖注入。作者还展示了如何在测试类中使用自定义的bean容器并处理异常情况。
摘要由CSDN通过智能技术生成

1. 创建两个自定义注解

1.1 Component注解

@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

	String value() default "";
}

1.2 DI注解

@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DI {

	String value() default "";

}

2. ApplicationContext接口与实现类

2.1 ApplicationContext 接口

public interface ApplicationContext {
	// 获取bean
	Object getBean(String name);
	// 获取bean
	<T> T getBean(String name, Class<T> clazz);
	// 判断bean是否存在
	boolean exists(String name);

	boolean exists(String name, Class<?> clazz);
}

2.2 实现类:DefaultListableApplicationContext

// 实现类
public class DefaultListableApplicationContext implements ApplicationContext {

	/**
	 * 存放bean对象容器
	 */
	private Map<String, Object> objectsMap = new ConcurrentHashMap<>(128);
	private Map<Class, Object> objectsTypeMap = new ConcurrentHashMap<>(128);

	private Logger log = LoggerFactory.getLogger(DefaultListableApplicationContext.class);

	// 扫描的基础路径
	private String basePath;

	public DefaultListableApplicationContext(String basePackage) {
		//public static void pathDemo1(String basePackageName){
		String packagePath = basePackage.replaceAll("\\.", "\\\\");
		try {
			URL url = Thread.currentThread().getContextClassLoader().getResource(packagePath);
			if (StringUtils.hasText(url.getPath())) {
				String filePath = URLDecoder.decode(url.getFile(), "UTF8");
				// 扫描的基础路径
				basePath = filePath.substring(0, filePath.length() - packagePath.length());
				//包扫描
				loadBeans(new File(filePath));
				// 依赖注入
				loadDI();
			}

		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	@Nullable
	public Object getBean(String name) {
		return objectsMap.get(name);
	}

	@Override
	@Nullable
	public <T> T getBean(String name, Class<T> clazz) {
		Object obj = objectsMap.get(name);
		if (obj == null) {
			Object bean;
			if ((bean = objectsTypeMap.get(clazz)) == null) {
				for (Class<?> interfaceClazz : clazz.getInterfaces()) {
					if ((bean = objectsTypeMap.get(interfaceClazz)) != null)
						return (T) bean;
					getBean(name, interfaceClazz);
				}
				return null;
			}
			return (T) bean;
		}
		return clazz.isInstance(obj) ? clazz.cast(obj) : null;
	}

	@Override
	public boolean exists(String name) {
		return objectsMap.get(name) == null ? false : true;
	}

	@Override
	public boolean exists(String name, Class<?> clazz) {
		if (objectsMap.get(name) == null) {
			if (objectsTypeMap.get(clazz) == null) {
				for (Class<?> interfaceClazz : clazz.getInterfaces()) {
					if (objectsTypeMap.get(interfaceClazz) != null)
						return true;
					exists(name, interfaceClazz);
				}
				return false;
			}
			return true;
		}
		return true;
	}

	/**
	 * 包扫描
	 *
	 * @param file
	 */
	private void loadBeans(File file) {
		// 1 判断当前文件是否文件夹
		if (file.isDirectory()) {
			File[] childrenFiles = file.listFiles();
			// 2. 获取文件夹里面的所有内容

			// 3. 判断文件夹为空,直接返回
			if (childrenFiles == null || childrenFiles.length == 0) {
				return;
			}
			// 4. 如果文件夹不为空,遍历文件夹内容
			for (File childFile : childrenFiles) {
				// 4.1  遍历得到每个file对象,继续判断。如果是文件夹,递归
				if (childFile.isDirectory()) {
					loadBeans(childFile);
				} else {
					// 4.2 遍历得到的file不是文件夹,是文件
					// 4.3 得到包路径 + 类名称部分
					// C:/desktop/IdeaProjects/spring-mytest/src/main/java/com/ypy/context
					String pathWithClass = childFile.getAbsolutePath().substring(basePath.length() - 1);
					// 4.4 判断当前文件类型是否为.class
					if (pathWithClass.endsWith(".class")) {
						// 4.5 如果是.class类型,把路径\替换成., 把.class去掉
						// com.ypy.context.beans.UserServiceImpl
						String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");

						// 4.6 使用反射实例化
						try {
							Class<?> clazz = Class.forName(allName);
							if (!clazz.isInterface()) {
								// 4.6.1 判断是否有@Component注解,有
								if (clazz.getAnnotation(Component.class) != null) {

									Object instance = clazz.getConstructor().newInstance();

									// 优先使用用户自定义的bean名称
									if (StringUtils.hasText(clazz.getAnnotation(Component.class).value())) {
										String beanName = clazz.getAnnotation(Component.class).value();
										objectsMap.put(beanName, instance);
										log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");
										if (clazz.getInterfaces().length == 1) {
											objectsTypeMap.put(clazz.getInterfaces()[0], instance);
											log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");
										} else if (clazz.getInterfaces().length == 0) {
											objectsTypeMap.put(clazz, instance);
											log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");
										} else {
											throw new NotSupportMoreSuperInterface("Not support the bean that has more than two super interfaces.");
										}
									}

									// 其次使用父接口的类名作为bean名称
									else if (clazz.getInterfaces().length > 0) {
										String interfaceName = clazz.getInterfaces()[0].getSimpleName();
										String beanName = lowercaseFirstLetter(interfaceName);
										// 放入容器中
										objectsMap.put(beanName, instance);
										log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");
										// 可能出现bug
										objectsTypeMap.put(clazz.getInterfaces()[0], instance);
										log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");
									}

									// 如果说没有父接口,就使用类名作为bean名称
									else {
										String beanName = lowercaseFirstLetter(clazz.getSimpleName());
										// 放入容器中
										objectsMap.put(beanName, instance);
										log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");
										objectsTypeMap.put(clazz, instance);
										log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");
									}
								}
							}
						} catch (Exception e) {
							throw new RuntimeException(e);
						}
					}

				}
			}

		}


	}

	private void loadDI() {
		for (Map.Entry<String, Object> entry : objectsMap.entrySet()) {
			Object obj = entry.getValue();
			Class<?> clazz = obj.getClass();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {

				try {
					field.setAccessible(true);
					if (field.getAnnotation(DI.class) != null) {
						Class<?>[] interfaces = field.getType().getInterfaces();
						String needWiredBeanName;
						Object autoWiredBean;

						// 优先使用注解DI的value注入
						if (StringUtils.hasText(needWiredBeanName = field.getAnnotation(DI.class).value())) {
							autoWiredBean = objectsMap.get(needWiredBeanName);
							if (autoWiredBean != null) {
								field.set(obj, autoWiredBean);
								log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + " from " + "objectsMap, by value of annotation @DI");
								continue;
							} /*else {
								throw new NotExistsBean("Not exists the bean named <" + needWiredBeanName + "> to inject as property");
							}*/
						}

						// 没有父接口
						needWiredBeanName = lowercaseFirstLetter(field.getType().getSimpleName());
						if ((autoWiredBean = objectsMap.get(needWiredBeanName)) != null || (autoWiredBean = objectsTypeMap.get(field.getType())) != null) {
							field.set(obj, autoWiredBean);
							log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + " , by value or type of property itself ");
							continue;
						}

						// 使用父接口的名字从bean容器中查找,注入
						if (interfaces.length > 0) {
							for (Class<?> interfaceClazz : interfaces) {
								String interfaceClazzName = interfaceClazz.getSimpleName();
								if (interfaceClazz.isAssignableFrom(field.getType())) {
									needWiredBeanName = lowercaseFirstLetter(interfaceClazzName);
									if ((autoWiredBean = objectsMap.get(needWiredBeanName)) != null || (autoWiredBean = objectsTypeMap.get(interfaceClazz)) != null) {
										field.set(obj, autoWiredBean);
										log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + ", by value or type of super interface");
									}

								}
							}
							continue;
						}
						throw new InjectBeanException("There occurs an Exception while injecting property filed [" + field.getName() + "] of Class <" + clazz.getSimpleName() + "> , because bean factory doesn't exist the bean named " + needWiredBeanName);
					}
				} catch (IllegalAccessException e) {
					throw new RuntimeException(e);
				}
			}

		}
	}

	private String lowercaseFirstLetter(String name) {
		return name.substring(0, 1).toLowerCase() + name.substring(1);
	}

}


3. 定义DAO层和Service层及其实现

// 3.1 dao层
public interface UserDao {
	void addUser(User user);
}

/** 
*  userDao实现
*/
@Component("userDao")
public class UserDaoImpl implements UserDao {
	@Override
	public void addUser(User user) {
		System.out.println();
		System.out.println("------------------>>>执行UserDaoImpl中的addUser方法开始<<<<------------------");
		System.out.println("\t\t\t\t\t\t名字\t\t\t\t年龄");
		System.out.println("\t\t\t\t\t\t"+user.getName()+"\t\t\t\t"+user.getAge());
		System.out.println("------------------>>>执行UserDaoImpl中的addUser方法完毕<<<<------------------");

	}
}

// 3.2 service层

public interface UserService {
	void add();
}

// service实现
@Component("userService")
public class UserServiceImpl implements UserService {

	@DI("userDao")
	private UserDaoImpl userDao;

	@Override
	public void add() {
		System.out.println("》》》 user service impl execute add method...");
		User user = new User("张三", 25);
		userDao.addUser(user);
	}
}

4. 定义异常信息类

4.1 InjectBeanException

public class InjectBeanException extends RuntimeException{
	public InjectBeanException() {
		super();
	}

	public InjectBeanException(String message) {
		super(message);
	}
}

4.2 NotExistsBean

public class NotExistsBean extends RuntimeException{
	public NotExistsBean() {
	}

	public NotExistsBean(String message) {
		super(message);
	}
}

4.3 NotSupportMoreSuperInterface

public class NotSupportMoreSuperInterface extends RuntimeException{

	public NotSupportMoreSuperInterface() {
		super();
	}

	public NotSupportMoreSuperInterface(String message) {
		super(message);
	}
}

5. 测试自定义bean容器(带依赖注入)

5.1 新建测试类TestUser

public class TestUser {
	public static void main(String[] args) {
		DefaultListableApplicationContext context = new DefaultListableApplicationContext("com.ypy.context");
		UserService userService = context.getBean("aaa", UserServiceImpl.class);
		UserDao userDao = context.getBean("userDaoImpl", UserDao.class);
		boolean existFlag = context.exists("aaa"/*, UserService.class*/);
		System.out.println("UserService exists ? " + existFlag);
		System.out.println("从Bean容器中获取aaa对象地址: " + userService);
		System.out.println("从Bean容器中获取userDao对象地址: " + userDao);
		userService.add();
	}
}

5.2 输出结果

一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsMap store bean(name,obj) ===> (userDao,com.ypy.context.dao.impl.UserDaoImpl@2b193f2d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsTypeMap store bean(class,obj) ===> (UserDao,com.ypy.context.dao.impl.UserDaoImpl@2b193f2d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsMap store bean(name,obj) ===> (userService,com.ypy.context.service.impl.UserServiceImpl@7a81197d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsTypeMap store bean(class,obj) ===> (UserService,com.ypy.context.service.impl.UserServiceImpl@7a81197d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: <<< DI: Class UserServiceImpl of field named userDao is injected with value of com.ypy.context.dao.impl.UserDaoImpl@2b193f2d from objectsMap, by value of annotation @DI
UserService exists ? false
从Bean容器中获取aaa对象地址: com.ypy.context.service.impl.UserServiceImpl@7a81197d
从Bean容器中获取userDao对象地址: com.ypy.context.dao.impl.UserDaoImpl@2b193f2d
》》》 user service impl execute add method...

------------------>>>执行UserDaoImpl中的addUser方法开始<<<<------------------
						名字				年龄
						张三				25
------------------>>>执行UserDaoImpl中的addUser方法完毕<<<<------------------

Process finished with exit code 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值