Spring18 - 手写 IoC - @Bean

创建注解类 @Bean

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

创建扫描包以及子包、解析注解的 ApplicationContext接口

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

在 ApplicationContext 接口的实现类 AnnotationApplicationContext 中完成有 @Bean 注解的类的实例化

AnnotationApplicationContext 的有参构造中,实现包扫描与实例化
public AnnotationApplicationContext(String basePackage){
    try {
        //传入参数格式:com.atguigu.xxx.xxx
        //1 将 . 替换为 / 改成文件路径
        String packagePath = basePackage.replaceAll("\\.", "\\/");
        //2 获取包绝对路径
        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() - packagePath.length());

            //包扫描
            loadBean(new File(filePath));
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
  • 因此,在 ApplicationContext 对象创建后(调用有参构造),可以直接通过 getBean 方法获取类的实例对象
具体的包扫描与注解解析操作封装在 loadBean 方法中,对传入的文件进行扫描,实现类的实例化
public void loadBean(File file) throws Exception {
    //1 判断当前是否为文件夹
    if (file.isDirectory()) {
        //2 获取文件夹里面的内容
        File[] childrenFiles = file.listFiles();

        //3 判断文件夹里面为空,直接返回
        if (childrenFiles == null || childrenFiles.length == 0) {
            return;
        } else {
            //4 如果文件夹不为空,遍历文件夹所有内容
            for (File childFile : childrenFiles) {
                if (childFile.isDirectory()) {
                    //4.1 遍历得到每个File对象,继续判断,如果还是文件夹,递归直到非文件夹
                    loadBean(childFile);
                } else {
                    //4.2 如果遍历得到的是文件
                    //4.2.1 通过字符串截取,得到包路径+类名称部分
                    String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);

                    //4.2.2 判断当前文件是否为 .class(编译后)
                    if (pathWithClass.contains(".class")) {
                        //4.2.3 如果是 .class,把路径 \ 替换为 . 把 .class去掉
                        //最终得到路径的格式:com.atguigu.service.UserServiceImpl 则可以使用反射进行实例化
                        String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");

                        //4.2.4 获取类的 Class 从而判断是否为接口
                        Class<?> clazz = Class.forName(allName);
                        //接口不需要实例化,如果不是接口再判断是否有 @Bean 注解
                        if (!clazz.isInterface()) {
                            //4.2.5 判断类上是否有 @Bean注解
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            //4.2.6 如果有 @Bean注解则进行反射实例
                            if (annotation != null) {
                                Object instance = clazz.getConstructor().newInstance();

                                //5 将实例化后的对象存入 map集合 beanFactory中
                                //5.1 判断当前类是否有接口
                                if (clazz.getInterfaces().length > 0) {
                                    //5.1.1 如果有接口,让接口的 class 作为 map 中的 key
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                } else {
                                    //5.1.2 如果没有接口,让自己的 class 作为 map 中的 key
                                    beanFactory.put(clazz, instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
posted @   LaViez  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示