手写Spring-IOC

🎉🎉本文是基于尚硅谷的Spring6教程的笔记,希望对正在学习的你有一定帮助。

03spring6-ioc-demo

开发环境

  • Java17
  • Maven3.8.5

前言:

Spring框架的IOC是基于Java反射机制实现,所以要自己实现一个Spring-IOC容器,需要了解反射。此文篇幅有限,不再讨论反射。

目录结构:

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─coding
│  │  │          ├─annotation
│  │  │          ├─bean
│  │  │          ├─dao
│  │  │          ├─service
│  │  │          └─test
│  │  └─resources
│  └─test
│      └─java

Task

  • 自己定义@Bean注解
  • 自己定义@DI注解

准备

dao
package com.coding.dao;

/**
 * @time 2023-02-21-20:38
 */
public interface UserDao {
     void add();
}

package com.coding.dao;


import com.coding.annotation.Bean;

/**
 * @time 2023-02-21-20:39
 */
@Bean
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("自定义bean dao.......");
    }
}

service
package com.coding.service;

/**
 * @time 2023-02-21-20:39
 */
public interface UserService {
    void add();
}

package com.coding.service;


import com.coding.annotation.Bean;
import com.coding.annotation.DI;
import com.coding.dao.UserDao;


/**
 * @time 2023-02-21-20:39
 */
@Bean
public class UserServiceImpl implements UserService {

    @DI
    private UserDao userDao;

    @Override
    public void add() {
        System.out.println("自定义bean service........");
        userDao.add();
    }
}

自定义注解

annotation
package com.coding.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @time 2023-02-22-15:20
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}

package com.coding.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @time 2023-02-22-15:20
 */

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

定义bean容器接口

package com.coding.bean;

/**
 * @time 2023-02-22-15:22
 */

public interface ApplicationContext {

    Object getBean(Class clazz);
}

public class AnnotationApplicationContext implements ApplicationContext {

    private Map<Class, Object> beanFactoryMap = new HashMap<>();

    @Override
    public Object getBean(Class clazz) {
        return beanFactoryMap.get(clazz);
    }
}
构造器基本逻辑
//扫描规则
    public AnnotationApplicationContext(String basePackagePath) {
        //包扫描
        try {
            //把.替换成斜杠\
            String packagePath = basePackagePath.replaceAll("\\.", "\\\\");
            //获取包的绝对路径
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().
                    getResources(packagePath);
            //获取包的绝对路径
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                //将转义字符转回来
                String filepath = URLDecoder.decode(url.getFile(), "utf-8");
                rootPath = filepath.substring(0, filepath.length() - basePackagePath.length());
                //扫描包
                loadBean(new File(filepath));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //属性注入
        loadDI();
    }
主要是实现loadBean方法和loadDI方法
思路:
 private void loadBean(File file) {
        //1判断是否是文件夹
        //2获取文件夹中所有内容
        //3判断文件夹里面为空,直接返回
        //4如果文件夹里面不为空,遍历文件夹所有内容
            //4.1 遍历得到每个FiLe对象,继续判断,如果还是文件夹,递归
            //4.2遍历得到FiLe对象不是文件夹,是文件,
            //4.3得到包路径+类名称部分-字符串截取
            //4.4判断当前文件类型是否.cLass
            //4.5如果是.cLass类型,把路径\替换成. 把 .class去掉
            //4.6判断类上是否有@Bean注解,有就实例化
            	   //4.6.1获取类的Class
                   //4.6.2判断是不是接口
                     //4.6.3判断类上是否有@Bean注解
                     //4.6.4实例化
            //4.7实例化后放到map集合beanFactoryMap里面
                //如果有接口把接口的class当成key,实例对象当成value
                //如果没有接口把自己的class当成key,实例对象当成value

    }
    
    
//属性注入
private void loadDI(){
    //实例化对象在 beanFactoryMap 的map集合里面
    //1 遍历 beanFactoryMap map集合
    //2获取map集合每个对象(value) ,每个对象属性获取到
    //3 遍历得到每个对象属性数组,得到每个属性
    //4判断属性上面是否有@DI注解
     	//私有属性  ,设置可以访问
    //5如果有@DI注解,把对象进行设置(注入)

}
实现:
//包扫描
    private void loadBean(File file) throws Exception {
        //1判断是否是文件夹
        if (file.isDirectory()) {
            //2获取文件夹中所有内容
            File[] childrenFiles = file.listFiles();
            //3判断文件夹里面为空,直接返回
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            //4如果文件夹里面不为空,遍历文件夹所有内容
            for (File childFile : childrenFiles) {
                //4.1 遍历得到每个FiLe对象,继续判断,如果还是文件夹,递归
                if (childFile.isDirectory()) {
                    loadBean(childFile);
                } else {
                    //4.2遍历得到FiLe对象不是文件夹,是文件,
                    //4.3得到包路径+类名称部分-字符串截取
                    String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
                    //4.4判断当前文件类型是否.class
                    if (pathWithClass.endsWith(".class")) {
                        //4.5如果是.cLass类型,把路径\替换成. 把 .class去掉
                        String allName = pathWithClass.substring(0, pathWithClass.length() - 6).replaceAll("\\\\", ".");
                        //4.6判断类上是否有@Bean注解,有就实例化
                        //4.6.1获取类的Class
                        Class<?> clazz = Class.forName(allName);
                        //4.6.2判断是不是接口
                        if (!clazz.isInterface()) {
                            //4.6.3判断类上是否有@Bean注解
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if (annotation != null) {
                                //4.6.4实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7实例化后放到map集合beanFactoryMap里面
                                if (clazz.getInterfaces().length > 0) {
                                    //如果有接口把接口的class当成key,实例对象当成value
                                    beanFactoryMap.put(clazz.getInterfaces()[0], instance);
                                } else {
                                    //如果没有接口把自己的class当成key,实例对象当成value
                                    beanFactoryMap.put(clazz, instance);
                                }
                            }

                        }

                    }
                }

            }

        }
    }
//属性注入
    private void loadDI() {
        //实例化对象在 beanFactoryMap 的map集合里面

        //1 遍历 beanFactoryMap map集合
        Set<Map.Entry<Class, Object>> entries = beanFactoryMap.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            //2获取map集合每个对象(value) ,每个对象属性获取到
            Object obj = entry.getValue();
            Class<?> clazz = obj.getClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            //3 遍历得到每个对象属性数组,得到每个属性
            for (Field field : declaredFields) {
                //4判断属性上面是否有@DI注解
                DI annotation = field.getAnnotation(DI.class);
                if (annotation != null) {
                    //私有属性  ,设置可以访问
                    field.setAccessible(true);
                    //5如果有@DI注解,把对象进行设置(注入)
                    try {
                        field.set(obj, beanFactoryMap.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        }


    }
测试:
package com.coding.test;

import com.coding.bean.AnnotationApplicationContext;
import com.coding.bean.ApplicationContext;
import com.coding.service.UserService;

/**
 * @time 2023-02-22-16:23
 */
public class TestBean {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationApplicationContext("com.coding");
        UserService userServiceClass = (UserService) context.getBean(UserService.class);
        System.out.println(userServiceClass);
        userServiceClass.add();
    }
}
结果:
com.coding.service.UserServiceImpl@421faab1
自定义bean service........
自定义bean dao.......
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值