面试官问我咋实现Spring框架IOC和DI好吧打趴下,深度解析手动实现Spring框架的IOC与DI功能2

简介: 面试官问我咋实现Spring框架IOC和DI好吧打趴下,深度解析手动实现Spring框架的IOC与DI功能2

定义相关属性扫描所有 Bean



修改 BeanContainer.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/08 008 17:24
 * @description
 **/
@Slf4j
@SuppressWarnings("unused")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }
    /**
     * 容器枚举
     *
     * @author yby6
     * @date 2023/10/08
     */
    private enum ContainerHolder {
        /**
         * 持有人
         */
        HOLDER;
        /**
         * 实例
         */
        private final BeanContainer instance;
        /**
         * 容器
         */
        ContainerHolder() {
            instance = new BeanContainer();
        }
    }
    /**
     * bean注释
     */
    private final List<Class<? extends Annotation>> beanAnnotation =
            Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);
    /**
     * bean映射
     */
    private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>();
    /**
     * 容器实例的个数
     *
     * @return int
     */
    public int size() {
        return beanMap.size();
    }
    /**
     * 加载状态
     */
    private boolean loaded = false;
    /**
     * 容器是否被加载过
     *
     * @return boolean
     */
    public boolean isLoaded() {
        return loaded;
    }
    /**
     * 加载bean
     *
     * @param packageName 包名
     */
    public synchronized void loadBeans(String packageName) {
        // 判断容器是否被加载过
        if (isLoaded()) {
            log.warn("容器已经被加载!");
            return;
        }
        // 1.获取该包下所有的字节码
        Set<Class<?>> packageClass = ClassUtil.getPackageClass(packageName);
        if (null == packageClass || packageClass.isEmpty()) {
            log.warn("get null from packageName:{}", packageName);
            return;
        }
        // 2.遍历所有的字节码对象
        packageClass.forEach(clazz -> beanAnnotation.forEach(annotation -> {
            // 2.1.判断当前字节码上是否有指定的注解
            if (clazz.isAnnotationPresent(annotation)) {
                // 创建对象存放到集合当中
                beanMap.put(clazz, BeanContainer.newInstance(clazz, false));
            }
        }));
        loaded = true;
    }
    private static <T> T newInstance(Class<?> clazz, boolean accessible) {
        Constructor<?> declaredConstructor;
        try {
            declaredConstructor = clazz.getDeclaredConstructor();
            declaredConstructor.setAccessible(accessible);
            return (T) declaredConstructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("创建实例失败!");
        }
    }
}


改造一下我们之前的测试类代码移动目录位置,移动到 test 包当中如下,顺便改一下我们自定义的 IOC 与 DI 功能代码存放包的位置:


/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/9/30 030 14:36
 * @description
 **/
public class MyTest {
    private static BeanContainer beanContainer;
    @BeforeAll
    public static void init() {
        beanContainer = BeanContainer.getInstance();
    }
    @Test
    @SuppressWarnings("all")
    void testClassUtil() {
        ClassUtil.getPackageClass("top.it6666").forEach(System.out::println);
    }
    @Test
    void testBeanContainer() {
        Assertions.assertFalse(beanContainer.isLoaded());
        beanContainer.loadBeans("top.it6666");
        Assertions.assertEquals(1, beanContainer.size());
        Assertions.assertTrue(beanContainer.isLoaded());
    }
}

创建一个 MyController.java 然后在该类上添加 @Controller 注解导包是我们自己定义的那个注解:


然后运行测试类即可查看结果:




容器相关操作


增加删除操作,可以通过 class 获取对应的实例。获取所有的 key 和所有的 value。通过注解来获取被注解标注的 class。通过传入接口,来获取接口实现类的 class 对象。


/**
 * 添加bean
 *
 * @param clazz clazz
 * @param obj   obj
 * @return {@link Object}
 */
public Object addBean(Class<?> clazz, Object obj) {
    return beanMap.put(clazz, obj);
}
/**
 * 删除bean
 *
 * @param clazz clazz
 * @return {@link Object}
 */
public Object removeBean(Class<?> clazz) {
    return beanMap.remove(clazz);
}
/**
 * 获取Bean
 *
 * @param clazz clazz
 * @return {@link Object}
 */
public Object getBean(Class<?> clazz) {
    return beanMap.get(clazz);
}
/**
 * 获取容器所有的classes
 *
 * @return {@link Set}<{@link Class}<{@link ?}>>
 */
public Set<Class<?>> getClasses() {
    return beanMap.keySet();
}
/**
 * 获取容器所有的Bean实例
 *
 * @return {@link Set}<{@link Object}>
 */
public Set<Object> getBeans() {
    return new HashSet<>(beanMap.values());
}


添加根据指定注解获取该注解标记的 Bean 的 Class 集合:

/**
 * 根据指定注解获取该注解标记的 Bean 的 Class 集合
 *
 * @param annotation 注解
 * @return {@link Set}<{@link Class}<{@link ?}>>
 */
public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
    // 1.获取容器当中所有的class
    Set<Class<?>> classes = this.getClasses();
    if (null == classes || classes.isEmpty()) {
        log.info("没有任何信息!");
        return null;
    }
    Set<Class<?>> classSet = new HashSet<>();
    classes.forEach(clazz -> {
        if (clazz.isAnnotationPresent(annotation)) {
            // 保存到集合当中
            classSet.add(clazz);
        }
    });
    return classSet.size() > 0 ? classSet : null;
}


添加根据传入的接口,到容器当中找对应的实现类或子类:

/**
 * 根据传入的接口,到容器当中找对应的实现类或子类
 *
 * @param interfaceOrClass 接口或类
 * @return {@link Set}<{@link Class}<{@link ?}>>
 */
public Set<Class<?>> getClassBySuper(Class<?> interfaceOrClass) {
    // 1.获取容器当中所有的class
    Set<Class<?>> classes = getClasses();
    if (null == classes || classes.isEmpty()) {
        log.info("没有任何信息!");
        return null;
    }
    Set<Class<?>> classSet = new HashSet<>();
    classes.forEach(clazz -> {
        // 判断当前字节码是否为指定字节码的子类或接口并且指定的字节码不等于本身字节码
        if (interfaceOrClass.isAssignableFrom(clazz) && !Objects.equals(clazz, interfaceOrClass)) {
            classSet.add(clazz);
        }
    });
    return classSet.size() > 0 ? classSet : null;
}





依赖注入


定义相关的注解标签,实现创建被注解标记的成员变量实例,并将其注入到成员变量里,依赖注入使用:

创建 Autowired.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/25 025 9:01
 * @description
 **/
/*
及作用在成员变量上
* */
@Target(ElementType.FIELD)
/*
保留在运行时
* */
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    String value() default "";
}


依赖注入实现思路:





依赖注入整体实现


DependencyInjector.java

/**
 * 依赖注入
 *
 * @author yby6
 * @date 2023/10/25
 */
@Slf4j
public class DependencyInjector {
    /**
     * Bean容器
     */
    private final BeanContainer beanContainer;
    /**
     * 依赖注入,构造器
     */
    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }
    /**
     * 执行Ioc操作
     */
    public void doIoc() {
        // 1.遍历Bean容器中所有的Class对象
        beanContainer.getClasses().forEach(clazz -> {
            // 2.遍历Class对象的所有成员变量
            Field[] declaredFields = clazz.getDeclaredFields();
            if (declaredFields.length == 0) {
                // 当前没有成员变量,继续查找下一个
                return;
            }
            // 有成员变量
            Arrays.stream(declaredFields).forEach(field -> {
                // 3.找出被Autowired标记的成员变量
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 获取注解实例
                    // 被 Autowired注解标注
                    // 4.获取这些成员变量的类型
                    Class<?> fieldType = field.getType();
                    // 5.获取成员变量的类型在容器里对应的实例
                    Object instanceObj = this.getFieldInstance(fieldType, field.getAnnotation(Autowired.class).value());
                    // 6.通过反射将对象的成员变量实例进行赋值操作
                    if (null == instanceObj) {
                        throw new RuntimeException("成员属性注入失败:" + fieldType.getName());
                    } else {
                        // 获取当前类实例
                        // 给当前类实例成员变量赋值
                        this.setField(field, beanContainer.getBean(clazz), instanceObj, true);
                    }
                }
            });
        });
    }
    /**
     * 根据Class在BeanContainer里获取其实例或者实现类
     *
     * @param fieldClass     成员变量类型
     * @param autowiredValue autowired注解的内容
     * @return {@link Object}
     */
    private Object getFieldInstance(Class<?> fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        // 字段是实现类,直接返回
        if (null != fieldValue) {
            return fieldValue;
        } else {
            // 字段是接口
            Class<?> implClass = this.getImplClass(fieldClass, autowiredValue);
            if (implClass != null) {
                return beanContainer.getBean(implClass);
            } else {
                return null;
            }
        }
    }
    /**
     * 得到impl类
     *
     * @param fieldClass     接口
     * @param autowiredValue autowired的价值
     * @return {@link Class}<{@link ?}>
     */
    private Class<?> getImplClass(Class<?> fieldClass, String autowiredValue) {
        //从容器当中获取该接口的实现类
        Set<Class<?>> classSet = beanContainer.getClassBySuper(fieldClass);
        if (null != classSet && !classSet.isEmpty()) {
            if (Objects.equals(autowiredValue, "")) {
                if (classSet.size() == 1) {
                    return classSet.iterator().next();
                } else {
                    // 如果有多个实现类
                    throw new RuntimeException("有多个实现类,请指定具体的实现类名称:" + fieldClass.getName());
                }
            } else {
                // 注解当中有设置值
                for (Class<?> clazz : classSet) {
                    if (autowiredValue.equals(clazz.getSimpleName())) {
                        return clazz;
                    }
                }
            }
        }
        return null;
    }
    /**
     * 设置字段
     *
     * @param field        字段
     * @param targetObject 目标对象
     * @param obj          obj
     * @param accessible   是否可访问
     */
    private void setField(Field field, Object targetObject, Object obj, boolean accessible) {
        field.setAccessible(accessible);
        try {
            field.set(targetObject, obj);
        } catch (IllegalAccessException e) {
            log.error("setField error:" + e);
            throw new RuntimeException(e);
        }
    }
}




测试依赖注入


创建 Service

IUserService.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/25 025 10:03
 * @description
 **/
public interface IUserService {
    /**
     * 显示
     */
    void show();
}


IUserServiceImpl.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/25 025 10:04
 * @description
 **/
@Service
public class IUserServiceImpl implements IUserService {
    @Override
    public void show() {
        System.out.println("my is IUserServiceImpl");
    }
}


修改 MyController.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/09 009 11:31
 * @description
 **/
@Controller
public class MyController {
    @Autowired
    private IUserService iUserService;
    public void testShow() {
        this.iUserService.show();
    }
}


修改 MyTest.java 当前 IUserService 只有一个实现类所以 @Autowired 是可以直接标记即可不用指定名称,在 MyTest 添加 testIoc 方法:

@Test
void testIoc() {
    beanContainer.loadBeans("top.it6666");
    MyController myController = (MyController) beanContainer.getBean(MyController.class);
    DependencyInjector dependencyInjector = new DependencyInjector();
    dependencyInjector.doIoc();
    myController.testShow();
}


运行结果如下:


如上是只有一个实现类,紧接着在来新建一个实现类,在 impl 包当中创建 ITaoBaoServiceImpl.java:

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/25 025 10:42
 * @description
 **/
@Service
public class ITaoBaoServiceImpl implements IUserService {
    @Override
    public void show() {
        System.out.println("my is ITaoBaoServiceImpl");
    }
}


再次运行测试类代码结果如下图:


修改 MyController.java:


再次运行测试类代码运行结果如下图所示:





最后


本期结束咱们下次再见👋~

🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗

相关文章
|
27天前
|
数据可视化 搜索推荐 BI
深度解析好用项目管理工具的功能优势
在选择项目管理工具时,重点在于全面的功能和高性价比。好工具应具备资源利用图(避免过度分配或闲置资源),团队协作功能(促进沟通与进度追踪),质量管理(如问题跟踪和自定义工作流),项目规划和跟踪(甘特图支持),任务管理(任务分解和依赖关系),以及费用跟踪。Zoho Projects、Microsoft Project、Jira等工具各有价格差异,例如,对于50个用户,Microsoft Project最贵,Zoho Projects最实惠,性价比高,适合中小企业。
31 2
|
30天前
|
XML 存储 Java
Spring重要类解析
Spring重要类解析
21 0
|
28天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
66 1
|
1天前
|
canal 缓存 关系型数据库
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战
|
1天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
4天前
|
JSON 前端开发 Java
Javaweb之SpringBootWeb案例之异常处理功能的详细解析
Javaweb之SpringBootWeb案例之异常处理功能的详细解析
16 0
|
4天前
|
存储 前端开发 Java
Javaweb之SpringBootWeb案例之登录校验功能的详细解析
Javaweb之SpringBootWeb案例之登录校验功能的详细解析
6 0
|
4天前
|
存储 Java 程序员
SpringBoot之分层解耦以及 IOC&DI的详细解析
SpringBoot之分层解耦以及 IOC&DI的详细解析
8 0
|
13天前
|
SQL API 数据库
Python中的SQLAlchemy框架:深度解析与实战应用
【4月更文挑战第13天】在Python的众多ORM(对象关系映射)框架中,SQLAlchemy以其功能强大、灵活性和易扩展性脱颖而出,成为许多开发者首选的数据库操作工具。本文将深入探讨SQLAlchemy的核心概念、功能特点以及实战应用,帮助读者更好地理解和使用这一框架。
|
14天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
20 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道

推荐镜像

更多