- 实现容器的依赖注入
- 为什么需要依赖注入
- IOC的实现流程
- 测试用例
如果一个类中, 依赖了另外一个类, 如下MainPageController 依赖了HeadLineShopCategoryCombineService . 那么就要保证HeadLineShopCategoryCombineService 这个类是初始化好的, 否则调用headLineShopCategoryCombineService.getMainPageInfo()
方法的时候, 就会报空指针.
@Controller
@Getter
public class MainPageController {
private HeadLineShopCategoryCombineService headLineShopCategoryCombineService;
public Result getMainPageInfo(HttpServletRequest req, HttpServletResponse resp){
return headLineShopCategoryCombineService.getMainPageInfo();
}
}
在上一篇文章 https://javaweixin6.blog.csdn.net/article/details/114493380中, 写的测试用例如下, 根据类, 获取其实例. 根据MainPageController类, 获取MainPageController的实例
@DisplayName("根据类获取其实例")
@Order(2)
@Test
public void getBeanTest() {
MainPageController controller = (MainPageController) beanContainer.getBean(MainPageController.class);
Assertions.assertEquals(true, controller instanceof MainPageController);
HelloServlet helloServlet = (HelloServlet) beanContainer.getBean(HelloServlet.class);
Assertions.assertEquals(null, helloServlet);
}
运行后, 可以看到headLineShopCategoryCombineService
是null , 那么就需要依赖注入功能, 把所依赖的对象, 进行实例初始化. 需要通过标记, 来告诉容器需要将哪些实例注入到Bean的成员变量中.
实现思路:
- 定义相关的注解标签
- 实现 创建被注解标记的成员变量实例, 并将其注入到成员变量中
- 依赖注入的使用 (暂时只做根据成员变量的注入 . )
整体的IOC流程如下图 实现依赖注入的类如下
package org.simpleframework.inject;
import lombok.extern.slf4j.Slf4j;
import org.simpleframework.core.BeanContainer;
import org.simpleframework.inject.annotation.Autowired;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;
import java.lang.reflect.Field;
import java.util.Set;
/**
* 类名称:DependencyInjector
* 类描述: 实现依赖注入
*
* @author: https://javaweixin6.blog.csdn.net/
* 创建时间:2021/3/7 19:22
* Version 1.0
*/
@Slf4j
public class DependencyInjector {
private BeanContainer beanContainer;
public DependencyInjector() {
// 从构造方法中, 实例化Bean容器
beanContainer = BeanContainer.getInstance();
}
/**
* 实现依赖注入
*/
public void doIoc() {
// 校验容器是否为空
if (ValidationUtil.isEmpty(beanContainer.getClasses())) {
log.warn(" 容器中实例为空! ");
return;
}
// 1. 遍历bean容器中所有的Class对象
for (Class clazz : beanContainer.getClasses()) {
// 2. 遍历Class 对象的所有的成员变量
Field[] fields = clazz.getDeclaredFields();
if (ValidationUtil.isEmpty(fields)) {
// 判断成员变量是否为空, 如果是空, 则遍历下一个对象
continue;
}
for (Field field : fields) {
// 3. 找出被Autowired标记的成员变量
if (field.isAnnotationPresent(Autowired.class)) {
// 获取注解的实例
Autowired autowired = field.getAnnotation(Autowired.class);
// 获取Autowired注解的属性值
String autowiredValue = autowired.value();
// 4. 获取成员变量的类型
Class fieldClass = field.getType();
// 5. 获取这些成员变量的类型, 在容器里对应的实例
Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
if (fieldValue == null) {
// 如果从容器中获取该实例失败, 那么抛出异常
throw new RuntimeException(" 无法从容器中获取实例, 目标类型为: " + fieldClass.getName() + " autowiredValue 值为: " + autowiredValue);
} else {
// 6. 通过反射将对应的成员变量实例注入到成员变量所在类的实例中
Object targetBean = beanContainer.getBean(clazz);
ClassUtil.setField(field, targetBean, fieldValue, true);
}
}
}
}
}
/**
* 根据Class 在BeanContainer里获取其实例或实现类
* 根据接口获取其实现类
*
* @param fieldClass
* @param autowiredValue
* @return
*/
private Object getFieldInstance(Class fieldClass, String autowiredValue) {
Object fieldValue = beanContainer.getBean(fieldClass);
if (fieldValue != null) {
// 不为空, 代表成员变量不是接口, 而是实现类
return fieldValue;
} else {
// 获取接口的实现类
Class implementedClass = getImplementedClass(fieldClass, autowiredValue);
if (implementedClass != null) {
// 从bean容器中获取其成员变量的实例并返回
return beanContainer.getBean(implementedClass);
} else {
// 获取的接口实现类为空, 那么直接返回null
return null;
}
}
}
/**
* 获取接口的实现类
*
* @param fieldClass
* @param autowiredValue
* @return
*/
private Class getImplementedClass(Class fieldClass, String autowiredValue) {
// 获取接口的所有的实现类的集合
Set classSet = beanContainer.getClassesBySuper(fieldClass);
if (!ValidationUtil.isEmpty(classSet)) {
if (ValidationUtil.isEmpty(autowiredValue)) {
// autowiredValue为默认值, 即用户未指定注入哪个对象
if (classSet.size() == 1) {
// 如果其实现类只有一个, 那么就直接返回就行
return classSet.iterator().next();
} else {
// 如果实现类多于一个 , 那么抛出异常
throw new RuntimeException("该接口有多个实现 " + fieldClass.getName() + " 请指定需要哪个实现类");
}
} else {
// autowiredValue值不为空, 那么遍历其所有的实现类, 找到和指定类名相同的进行返回
for (Class clazz : classSet) {
if (autowiredValue.equals(clazz.getSimpleName())) {
return clazz;
}
}
}
}
// 如果实现类为空, 那么直接返回null
return null;
}
}
ClassUtil 的工具类, 实现设置类的属性值
/**
* 设置类的属性值
*
* @param field 成员变量
* @param target 类实例
* @param value 成员变量的值
* @param accessible 是否允许设置私有属性
*/
public static void setField(Field field, Object target, Object value, boolean accessible) {
field.setAccessible(accessible);
try {
field.set(target, value);
} catch (IllegalAccessException e) {
log.error("设置属性值异常 ", e);
throw new RuntimeException(e);
}
}
测试用例
测试用例如下,
package org.simpleframework.inject;
import com.springstudy.controller.frontend.MainPageController;
import com.springstudy.service.combine.impl.HeadLineShopCategoryCombineServiceImpl;
import com.springstudy.service.combine.impl.HeadLineShopCategoryCombineServiceImpl2;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.simpleframework.core.BeanContainer;
/**
* @author: https://javaweixin6.blog.csdn.net/
* 创建时间:2021/3/7 22:22
* Version 1.0
*/
public class DependencyInjectorTest {
@Test
public void doIocTest() {
// 获取容器实例
BeanContainer beanContainer = BeanContainer.getInstance();
// 指定包名, 加载类到容器中
beanContainer.loadBeans("com.springstudy");
// 判断容器是否加载完毕
Assertions.assertEquals(true, beanContainer.isLoaded());
// 从容器中获取指定的bean
MainPageController mainPageController = (MainPageController) beanContainer.getBean(MainPageController.class);
// 判断获取的对象是否为MainPageController
Assertions.assertEquals(true, mainPageController instanceof MainPageController);
// 在没有进行调用IOC之前, 即没有依赖注入之前, Controller中的Service对象 为null
Assertions.assertNull(mainPageController.getHeadLineShopCategoryCombineService());
// 进行依赖注入
new DependencyInjector().doIoc();
// 在没有进行调用IOC之后, 即依赖注入之后, Controller中的Service对象 不为null
Assertions.assertNotNull(mainPageController.getHeadLineShopCategoryCombineService());
// 验证注入的是HeadLineShopCategoryCombineServiceImpl
Assertions.assertTrue(mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl);
// 验证注入的不是 HeadLineShopCategoryCombineServiceImpl2
Assertions.assertFalse(mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl2);
}
}
在项目中使用自定义的@Autowired注解进行注入 . 并且也有测试同一个接口, 两个实现类. 如下 一个Service接口, 两个实现类.
在Controller中注入时, 需指定其注入的类名称.
在测试用例中, 验证的代码如下
// 验证注入的是HeadLineShopCategoryCombineServiceImpl
Assertions.assertTrue(mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl);
// 验证注入的不是 HeadLineShopCategoryCombineServiceImpl2
Assertions.assertFalse(mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl2);
测试结果如下