• 手写一个Spring IOC框架


    目录

    一,Spring IOC

     二,流程图设计

    三,设计思路解析

    三,开始写代码

    1.准备工作:

    2.扫描并加载类信息

    3.初始化bean

    4.测试一下


    一,Spring IOC

    Spring IoC容器是Spring框架的核心,它通过读取配置信息来自动创建对象(bean)、配置对象属性以及管理对象的生命周期。IoC容器利用依赖注入(DI)自动将对象的依赖关系注入到需要它们的其他对象中,从而减少代码间的耦合度。

    容器支持多种bean作用域和生命周期管理,提供了事件处理、国际化消息和资源访问等高级功能。此外,Spring IoC与AOP(面向切面编程)紧密结合,支持通过切面来实现如日志、事务等横切关注点的处理。

    上面是IOC的介绍,接下来我将去完成一个简单的IOC容器,剖析底层。

     二,流程图设计

    三,设计思路解析

    1. 扫描类路径下所有以Java结尾的文件: 在Spring框架中,通过使用ClassPathScanningCandidateComponentProvider类,可以扫描指定的类路径下所有的.class文件。这个类通常与AnnotationConfigApplicationContext一起使用,后者是Spring中用于处理注解配置的上下文。

    2. 获得需要被IoC管理的类: 通过类路径扫描,Spring容器会筛选出那些需要被IoC容器管理的类。这些类通常带有特定的注解,如@Component@Service@Repository@Controller等,这些注解表明了一个类将作为Spring容器中的一个bean。

    3. 全类名加入到beanNames集合中: 将筛选出的类全限定名(即包名+类名)加入到一个名为beanNames的集合中。这个集合是Spring容器内部用于跟踪所有候选bean的一个列表。

    4. 反射为实例属性赋值: 对于每个候选的bean类,Spring容器会通过反射创建其实例,并通过反射机制为其属性赋值。这一过程涉及到处理@Autowired注解,以及根据类型或名称自动装配依赖关系。

    5. 完成依赖注入的过程: 依赖注入是Spring IoC容器的核心功能之一。Spring会根据bean的定义,解析和注入所有需要的依赖。这可能涉及到查找其他bean、处理复杂的依赖关系(如循环依赖)以及使用BeanFactory来获取和注入依赖。

    6. 反射获取类上的注解: 在处理每个bean时,Spring容器会通过反射获取类上的注解信息。这些注解可能会影响bean的创建、作用域、生命周期等。例如,@Scope注解定义了bean的作用域,@PostConstruct注解指定了在构造之后执行的方法等。

    7. 容器启动: 在所有bean都被创建并注入依赖之后,Spring容器会启动。这通常涉及到调用ApplicationContextrefresh()方法,该方法会触发容器的启动过程,包括初始化所有的singleton beans、处理@PostConstruct注解的方法、以及发送ContextRefreshedEvent事件。

    8. 启动结束: 一旦容器启动完成,Spring会发送一个ContextClosedEvent事件,表明容器已经准备就绪,可以开始处理请求和执行业务逻辑。

    三,开始写代码

    我们将一步步按照流程图中所述的将IOC底层实现出来。

    1.准备工作

    编写注解和测试类(包含@Autowired和@Component)

    1. import java.lang.annotation.ElementType;
    2. import java.lang.annotation.Retention;
    3. import java.lang.annotation.RetentionPolicy;
    4. import java.lang.annotation.Target;
    5. @Target(ElementType.FIELD)
    6. @Retention(RetentionPolicy.RUNTIME)
    7. public @interface Autowired {
    8. }
    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. public @interface Component {
    4. }
    1. @Component
    2. public class UserService {
    3. public void addUser(String name, int age) {
    4. System.out.println(name);
    5. System.out.println(age);
    6. }
    7. }
    1. @Component
    2. public class TestController {
    3. @Autowired
    4. private UserService userService;
    5. public void test(){
    6. userService.addUser("zhangsan",18);
    7. }
    8. }

    2.扫描并加载类信息

    (1)扫描类路径下的所有文件将其加入到集合中。

    (2)将.java结尾的文件筛选出来

    1. //构造函数
    2. public SpringIOC() {
    3. initPath();
    4. try {
    5. scan();
    6. } catch (FileNotFoundException e) {
    7. e.printStackTrace();
    8. }
    9. beanNames= new ArrayList<>();
    10. initBeanNames();
    11. }
    12. private void initPath(){
    13. basePath="类路径自己指定";
    14. basePackage="自己指定扫描包";
    15. }
    16. /**
    17. * 将类路径下所有文件提取出来放到集合中
    18. */
    19. private void scan() throws FileNotFoundException {
    20. File file = new File(basePath);
    21. filePaths = new ArrayList<>();
    22. if(file.exists()){
    23. Queue queue = new LinkedList<>();
    24. queue.add(file);
    25. while(!queue.isEmpty()){
    26. File poll = queue.poll();
    27. if(poll == null){
    28. continue;
    29. }
    30. if(poll.isDirectory()){//目录下面的所有文件夹
    31. File[] files = poll.listFiles();
    32. for (File f : files) {
    33. queue.add(f);
    34. }
    35. }else {
    36. filePaths.add(poll.getPath());//将单个文件放到filePaths当中
    37. }
    38. }
    39. }else {
    40. throw new FileNotFoundException(basePath+" not found");
    41. }
    42. }
    43. /**
    44. * 将所有的.java结尾的 全限定名放到 beanNames
    45. */
    46. public void initBeanNames(){
    47. for (String s : filePaths) {//遍历刚才文件路径
    48. String replace = s.replace(basePath, "");
    49. if(replace.endsWith(".java")) {
    50. replace = replace.substring(0, replace.length()-5);
    51. }
    52. char[] chars = replace.toCharArray();
    53. for (int i = 0; i < chars.length; i++) {
    54. if(chars[i]=='\\'){
    55. chars[i] = '.';
    56. }
    57. }
    58. beanNames.add(basePackage+"."+new String(chars));
    59. }
    60. }

    3.初始化bean

    在2中我们已经得到了所有java类的类名,这一步可以根据全类名来获取类的注解等各种信息。

    (1)反射获取类上注解,筛选出被IOC管理的类并创建实例,封装到ioc容器中。

    (2)为ioc容器中的bean反射设置属性(依赖注入)

    1. //非懒加载
    2. public void initBeans(){
    3. for (String beanName : beanNames) {
    4. System.out.println(beanName);
    5. try {
    6. Class aClass = Class.forName(beanName);
    7. Annotation[] declaredAnnotations = aClass.getDeclaredAnnotations();//获取类上的所有注解
    8. for (Annotation declaredAnnotation : declaredAnnotations) {
    9. if(declaredAnnotation instanceof Component){//反射获取带@Component的类
    10. Object o = aClass.newInstance();//创建实例
    11. beans.put(aClass.getName(),o);//将类名-实例放到beans容器当中
    12. }
    13. }
    14. } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
    15. e.printStackTrace();
    16. }
    17. }
    18. System.out.println(beans);
    19. for (Map.Entry entry : beans.entrySet()) {//将实例从map中拿出来
    20. Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();//获取实例的所有字段
    21. for (Field field : declaredFields) {
    22. Annotation[] declaredAnnotations = field.getDeclaredAnnotations();//字段上所有的注解
    23. for (Annotation annotation : declaredAnnotations) {
    24. if (annotation instanceof Autowired) {//带@Autowired的注解
    25. String name = field.getType().getName();//com.heaboy.springioc.entity.UserService
    26. // 从beans 中获得对应的对象
    27. Object o = beans.get(name);//beans是个map 只要里面有这样的类型 就能拿到
    28. field.setAccessible(true);//设置可访问的权限
    29. try {
    30. field.set(entry.getValue(), o);//反射将o设置给字段
    31. } catch (IllegalAccessException e) {
    32. e.printStackTrace();
    33. }
    34. }
    35. }
    36. }
    37. }
    38. }

    4.测试一下

    3完成之后,此时加@Autowired和@Component注解的类已经在集合中了,属性也已经注入。我们来编写测试代码debug一下。

    1. public class SpringIOCTest {
    2. @Test
    3. public void testScan() throws FileNotFoundException {
    4. SpringIOC springIOC = new SpringIOC();//获取java类的全类名
    5. springIOC.initBeans();
    6. TestController instance = (TestController)springIOC.getInstance(TestController.class.getName());
    7. instance.test();
    8. }
    9. }

    可以看到两个bean实例以及他们之间的依赖关系。

    完整代码获取可以去我的git: https://gitee.com/code0321/ioc

    拜托可以给它点点赞哦~

  • 相关阅读:
    BP神经网络中的BP是指,bp神经网络属于什么
    学生网页设计作业源码(HTML+CSS)——海贼王6页代码质量好
    从一坨代码说起
    【Python百日进阶-数据分析】Day123 - Plotly Figure参数:饼图(一)
    Hive之数据类型和视图
    体验下,大厂在使用功能的API网关!
    Java实验报告(二)
    时间轴、流程类时间轴绘制
    信息安全技术实验:网络嗅探与欺骗
    SRTT-110VDC-4H-C时间继电器
  • 原文地址:https://blog.csdn.net/m0_59925573/article/details/138022169