尚硅谷-Spring6.0.2-手写IoC篇

这套课程也有源码讲解,但个人觉得Spring5那套视频讲解思路更清晰,用什么依赖就导入什么依赖,这样才能知道,哪部分实际用到的依赖,而不是一股脑把需要用到的依赖都导进去;当然,这是学习期间的学习方法,实际工作中,肯定是要求依赖完整,最好一次性导入的,而且最好是使用Maven或Gradle那种依赖管理工具。

IoC容器对于Bean管理主要是①Bean的创建②属性注入

diy自己的IoC容器之Bean创建

先准备一个接口和接口实现类

public interface UserService {
    void show();
}

这里已经加上了我们自定义的帽子(注解)了~~~ 

@MyBean
public class UserServiceImpl implements UserService {

    @Override
    public void show() {
        System.out.println("UserServiceImpl...");

    }
}

注解方式实现,于是先自定义注解吧

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBean {
}

仿BeanFactory接口的子接口ApplicationContext,自定义Bean工厂接口及实现类

public interface MyApplicationContext {
    Object getBean(Class clazz);
}

简单介绍以下这部分的功能吧~主要是!包路径的解析,从包路径获取所有类的全路径名,据此创建自定义的BeanDefinitionMap。 

public class MyAnnotationApplicationContext implements MyApplicationContext{

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

    private static String rootPath;//根路径
    @Override
    public Object getBean(Class clazz) {
        return MyBeanDefinition.get(clazz);
    }

    //设置包扫描规则
    //带参构造,参数为包路径,eg.com.coffeeship 扫描包及子包下的所有类
    public MyAnnotationApplicationContext(String basePackage) {
        //包路径转换
        String packagePath = basePackage.replaceAll("\\.", "\\\\");
        try {
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
//                System.out.println(filePath);
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                loadBean(new File(filePath));
            }
        } catch (IOException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
                 InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private static void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        if (file.isDirectory()) {
            File[] childFiles = file.listFiles();
            //若文件夹路径下没有任何文件夹或文件,则直接返回
            if (childFiles == null || childFiles.length == 0) {
                return;
            }
            for (File child : childFiles) {
                //若是子文件夹,则递归
                if (child.isDirectory()) {
                    loadBean(child);
                } else {
                    String classPath = child.getAbsolutePath().substring(rootPath.length() - 1);
                    if (classPath.contains(".class")) {
                        String fullPath = classPath.replaceAll("\\\\", "\\.").replaceAll(".class", "");
                        Class<?> clazz = Class.forName(fullPath);
                        //判断当前引用数据类型是类还是接口
                        if (!clazz.isInterface()) {
                            MyBean myBean = clazz.getAnnotation(MyBean.class);
                            if (myBean != null) {
                                Object newInstance = clazz.getConstructor().newInstance();
                                //判断当前类是否有接口,若有接口,则接口是key,否则该类是key
                                if (clazz.getInterfaces().length > 0) {
                                    MyBeanDefinition.put(clazz.getInterfaces()[0], newInstance);
                                } else {
                                    MyBeanDefinition.put(clazz, newInstance);
                                }
                            }
                        }
                    }
                }
            }
         }
    }
}

测试

public class Main {
    public static void main(String[] args) {
        MyApplicationContext applicationContext = new MyAnnotationApplicationContext("com.coffeeship");
        //通过接口获取Bean
        UserService bean = (UserService) applicationContext.getBean(UserService.class);
        bean.show();
    }
}

diy自己的IoC容器之属性注入

再准备一个接口和接口实现类

public interface UserDao {
    void show();
}
@MyBean
public class UserDaoImpl implements UserDao {
    @Override
    public void show() {
        System.out.println("UserDaoImpl...");
    }
}

 在Service层注入Dao层属性,并调用Dao层的方法

@MyBean
public class UserServiceImpl implements UserService {

    @MyDI
    private UserDao userDao;
    
    @Override
    public void show() {
        System.out.println("UserServiceImpl...");
        userDao.show();
    }
}

补充属性注入方法

private static void loadDi() {
    //遍历BeanDefinitionMap,对每个Bean获取其所有属性,判断属性上是否有注解,若有则从Map中根据属性类型进行赋值
    Set<Map.Entry<Class, Object>> entrySet = MyBeanDefinition.entrySet();
    for (Map.Entry<Class, Object> entry : entrySet) {
        Object obj = entry.getValue();
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            MyDI annotation = field.getAnnotation(MyDI.class);
            if (annotation != null) {
                field.setAccessible(true);
                try {
                    field.set(obj, MyBeanDefinition.get(field.getType()));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

再次测试,成功输出Service层和Dao层的方法体

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值