Spring源码学习--03(实现自己的IOC)

目录

1.框架具备的基本能力

2.第一步创建注解

  2.提取注解标记的对象

 3.实现容器

 容器的操作方式的实现

 4.依赖注入

5.测试:


1.框架具备的基本能力

                                    

                    

2.第一步创建注解

     

    

  2.提取注解标记的对象

    

              首先指定范围,指定范围是由用户传入包的名字。

       

       根据传入的包的名字解析资源。

                       

       原因:

             获取项目的发布的实际路径,拿到一个com.imooc是拿不到实际发布路径的

                              

                              

             类加载器:能定位到加载的资源

                  

            获取类加载器:

              

             通过类加载器加载相应的资源:这些资源包括了哪些呢,这个方法返回值是URL

  

   URL:

 

  

完成extractPackageClass方法:

 

根据不同的资源类型,采用不同的方式获取资源集合,

过滤出当前目录下的文件夹

  

 

 

package org.silmpleframework.utils;

import com.sun.xml.internal.ws.util.StringUtils;
import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;


/**
 * Created by wangxiang on 2020/11/15
 */
public class ClassUtils {

    private static final Logger LOGGER = Logger.getLogger(ClassUtils.class);


    /**
     * 加不加?都无所谓,主要为了避免某些类型转换是的unchecked错误
     *
     * @param packageName 传入的包名 com.wx
     * @return
     */
    public static Set<Class<?>> extractPackageClass(String packageName) {
        //1.获取到类的加载器
        ClassLoader classLoader = getClassLoader();
        //2.通过类加载器获取到类加载的资源 (接收的包名是以/来区分)
        URL url = classLoader.getResource(packageName.replace(".", "/"));
        if (url == null) {
            LOGGER.warn("unable to retrieve anything for packageName:" + packageName);
            return null;
        }
        //3.根据不同的资源类型采用不用的方式获取资源的集合 (首先Class放在目录下面)
        Set<Class<?>> classSet = new HashSet<>();
        if ("file".equals(url.getProtocol())) {
            File packageDirectory = new File(url.getPath());
            // 抽取文件夹下的Class类

            extractClassFile(classSet, packageDirectory, packageName);
        }

        return classSet;

    }


    private static void extractClassFile(Set<Class<?>> classSet, File packageDirectory, String packageName) {
        //遍历文件夹,拿到Class类
        if (!packageDirectory.isDirectory()) {
            return;
        }
        //过滤出包路劲下的文件夹和class
        File[] files = packageDirectory.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {

                if (pathname.isDirectory()) {
                    return true;
                } else {
                    //如果是文件,判断是不是class文件
                    String absolutePath = pathname.getAbsolutePath();
                    if (absolutePath.endsWith(".class")) {
                        //将class转换为Class类,加入到集合中
                        addClassToSet(absolutePath);
                    }

                }
                return false;
            }

            private void addClassToSet(String absolutePath) {
                //1.从class文件的绝对值路径找到包含了package的类名 absolutePath: User/xx/xx/com/wx/UserDTO.class
                String replace = absolutePath.replace("/", ".");
                String substring = absolutePath.substring(absolutePath.indexOf(replace));
                //包名加类名
                String rePackageName = substring.substring(0, substring.lastIndexOf("."));
                //2.利用反射生成Class类
                try {
                    Class<?> aClass = Class.forName(rePackageName);
                    classSet.add(aClass);
                } catch (ClassNotFoundException e) {
                    LOGGER.error(e);
                }
            }
        });
        //foreach之前一定要判空
        if (files != null) {
            for (File file : files) {
                extractClassFile(classSet, file, packageName);
            }
        }


    }


    /**
     * 获取当前线程的类加载器,获取到当前程序的资源信息
     *
     * @return
     */
    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }
}

 3.实现容器

 容器需要使用单例来实现:

 

 使用饿汉模式,懒汉模式 但是单例不是足够的安全,使用反射getDeclaredConstructors()

方法,即可获得类private修饰的无参构造函数,攻破私有构造函数去创建实例,:

    

   

   使用枚举实现单例模式:使用枚举创建单例模式可以防住反射的冲击

  

 枚举根本没有无参构造函数,所以反射自然无法攻破。枚举有有参的构造函数,但是通过有参的构造函数去攻破依然不行。

我们反编译代码看看为什么不行:安装jad工具

在类加载的时候,枚举就已经被创建出来了,所以这个模式也是属于恶汉模式

除了反射,序列化也能入侵单例,怎么入侵呢?就是将创建出来的单例写入到文件里面,再逆序列化创建单例,这时很有可能出现两个不同的实例。

  线程安全且能抵御反射,序列化入侵的容器:

package org.silmpleframework.core;

/**
 * Created by wangxiang on 2021/8/22
 */
public class BeanContainer {


    public static BeanContainer getInstance() {
        return HolderContainer.HOLDER.instance;
    }

    private BeanContainer() {

    }


    //容器的实例化放到了枚举的无参构造函数里面去了,这个在类加载的时候执行
    private enum HolderContainer {
        HOLDER;
        private BeanContainer instance;

        HolderContainer() {
            instance = new BeanContainer();
        }
    }
}

  

1.容器的载体:
 

 2.容器的加载

  

   

   

   

  

 

 

 容器的操作方式的实现

 

package org.silmpleframework.core;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.log4j.Logger;
import org.silmpleframework.core.annotation.WXComponent;
import org.silmpleframework.core.annotation.WXController;
import org.silmpleframework.core.annotation.WXRepository;
import org.silmpleframework.core.annotation.WXService;
import org.silmpleframework.utils.ClassUtils;
import org.silmpleframework.utils.ValidationUtils;

/**
 * Created by wangxiang on 2021/8/22
 */
public class BeanContainer {
    private static final Logger LOGGER = Logger.getLogger(BeanContainer.class);

    /**
     * 判断容器是否已经被加载过
     */
    private boolean loaded = false;

    public boolean isLoaded() {
        return this.loaded;
    }


    /**
     * 存放所有被配置标记的目标对象的Map
     */
    private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>();

    private static final List<Class<? extends Annotation>> CLASS_MAP =
            Arrays.asList(WXController.class, WXService.class, WXComponent.class, WXRepository.class);

    public static BeanContainer getInstance() {
        return HolderContainer.HOLDER.instance;
    }

    private BeanContainer() {

    }


    //容器的实例化放到了枚举的无参构造函数里面去了,这个在类加载的时候执行
    private enum HolderContainer {
        HOLDER;
        private BeanContainer instance;

        HolderContainer() {
            instance = new BeanContainer();
        }
    }

    /**
     * 扫描加载所有的Bean进Map中
     *
     * @param packageName
     */
    public synchronized void loadBeans(String packageName) {
        if (loaded) {
            LOGGER.info("已经被加载过");
            return;
        }
        Set<Class<?>> classSet = ClassUtils.extractPackageClass(packageName);
        if (ValidationUtils.classSetEmpty(classSet)) {
            LOGGER.warn("extract nothing from packageName:" + packageName);
            return;
        }
        for (Class<?> aClass : classSet) {
            for (Class<? extends Annotation> annotation : CLASS_MAP) {
                //如果存在标记的注解,将目标Class类和他的实例加入容器中
                if (aClass.isAnnotationPresent(annotation)) {
                    beanMap.put(aClass, ClassUtils.newInstance(aClass, true));
                }
            }
        }
        loaded = true;

    }

    //对容器的增删操作
    public Object addBean(Class<?> aClass, Object bean) {
        return beanMap.put(aClass, bean);
    }

    public Object removeBean(Class<?> aClass) {
        return beanMap.remove(aClass);
    }

    public Object getBean(Class<?> aClass) {
        return beanMap.get(aClass);
    }

    public Set<Class<?>> getClasses() {
        return beanMap.keySet();
    }

    public Set<Object> getBeans() {
        return new HashSet<>(beanMap.values());
    }

    /**
     * 根据注解获取对象的集合
     *
     * @param annotationClass
     * @return
     */
    public Set<Class<?>> getClassByAnnotation(Class<? extends Annotation> annotationClass) {
        Set<Class<?>> classes = getClasses();
        if (ValidationUtils.classSetEmpty(classes)) {
            LOGGER.info("nothing in bean Map");
            return Collections.EMPTY_SET;
        }
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> aClass : classes) {
            if (aClass.isAnnotationPresent(annotationClass)) {
                classSet.add(aClass);
            }
        }
        return classSet;
    }

    //通过接口或者父类获取实现类或者子类的class集合,不包括其本身
    public Set<Class<?>> getClassBySupper(Class<?> interfaceOrClass) {
        Set<Class<?>> classes = getClasses();
        if (ValidationUtils.classSetEmpty(classes)) {
            LOGGER.info("nothing in bean Map");
            return Collections.EMPTY_SET;
        }
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> aClass : classes) {
            //aClass是不是interfaceOrClass的实现类或者子类
            if (interfaceOrClass.isAssignableFrom(aClass) && !aClass.equals(interfaceOrClass)) {
                classSet.add(aClass);
            }
        }
        return classSet;
    }

    public int beanSize() {
        return beanMap.size();
    }
}

 4.依赖注入

  实现思路:只支持成员变量级别的注入

定义标签:

package org.silmpleframework.core.inject.annotation;

import java.lang.annotation.*;

/**
 * Created by wangxiang on 2020/11/15
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WXAutowired {

    //这个value简化Spring中Qualifier的操作
    String value() default "";
}

依赖注入的步骤:

//1.获取bean容器中所有的Class对象
//2.遍历class对象的所有成员变量
//3.找出被WXAutowired标记的成员变量
//4.获取成员变量的类型
//5.z找到成员变量的实例
//6.通过反射将成员变量的实例注入到成员变量实例所在的类的实例中
package org.silmpleframework.core.inject.annotation;

import java.lang.reflect.Field;
import java.util.Set;
import org.silmpleframework.core.BeanContainer;
import org.silmpleframework.utils.ClassUtils;
import org.silmpleframework.utils.ValidationUtils;

/**
 * Created by wangxiang on 2021/8/22
 */
public class DependencyInjector {
    private BeanContainer beanContainer;

    public DependencyInjector(BeanContainer beanContainer) {
        //获取单例的容器
        this.beanContainer = BeanContainer.getInstance();
    }


    public void doIOC() {
        //1.获取bean容器中所有的Class对象
        //2.遍历class对象的所有成员变量
        //3.找出被WXAutowired标记的成员变量
        //4.获取成员变量的类型
        //5.z找到成员变量的实例
        //6.通过反射将成员变量的实例注入到成员变量实例所在的类的实例中

        Set<Class<?>> classes = beanContainer.getClasses();
        if (ValidationUtils.classSetEmpty(classes)) {
            return;
        }
        for (Class<?> aClass : classes) {
            Field[] declaredFields = aClass.getDeclaredFields();
            if (declaredFields != null || declaredFields.length > 0) {
                for (Field declaredField : declaredFields) {
                    //找到成员变量上有WXAutowired注解的
                    if (declaredField.isAnnotationPresent(WXAutowired.class)) {
                        //拿到成员变量的类型
                        Class<?> fieldType = declaredField.getType();
                        //拿到成员上的注解,看看注解上面有没有指定注入实例的名称
                        WXAutowired annotation = declaredField.getAnnotation(WXAutowired.class);
                        String wXAutowiredValue = annotation.value();
                        //拿到成员变量的实例
                        Object fieldInstance = getFieldInstance(fieldType, wXAutowiredValue);
                        Object instance = beanContainer.getBean(aClass);
                        ClassUtils.setFiled(declaredField, instance, fieldInstance, true);
                    }
                }
            }
        }
    }

    private Object getFieldInstance(Class<?> fieldType, String wXAutowiredValue) {
        Object bean = beanContainer.getBean(fieldType);
        if (bean != null) {
            return bean;
        } else {
            //如果成员变量是接口
            //接口上面没有注解,要找到他的实现类才会被标记管理起来, 所以走这里
            Class<?> aClass = getImplementedClass(fieldType, wXAutowiredValue);
            if (aClass != null) {
                return beanContainer.getBean(aClass);
            } else {
                return null;
            }
        }
    }

    //获取接口的实现类
    private Class<?> getImplementedClass(Class<?> fieldType, String wXAutowiredValue) {
        Set<Class<?>> classBySupper = beanContainer.getClassBySupper(fieldType);
        if (ValidationUtils.classSetEmpty(classBySupper)) {
            return null;
        }

        if (classBySupper.size() == 1) {
            return classBySupper.iterator().next();
        } else {
            if (wXAutowiredValue == null || wXAutowiredValue == "") {
                throw new RuntimeException(fieldType + " are multiple implementation classes");
            } else {
                for (Class<?> aClass : classBySupper) {
                    //看看注解的值和类名字是不是相等
                    if (aClass.getSimpleName().equals(wXAutowiredValue)) {
                        return aClass;
                    }
                }
            }
        }
        return null;
    }
}
package org.silmpleframework.utils;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;


/**
 * Created by wangxiang on 2020/11/15
 */
public class ClassUtils {

    private static final Logger LOGGER = Logger.getLogger(ClassUtils.class);


    /**
     * 加不加?都无所谓,主要为了避免某些类型转换是的unchecked错误
     *
     * @param packageName 传入的包名 com.wx
     * @return
     */
    public static Set<Class<?>> extractPackageClass(String packageName) {
        //1.获取到类的加载器
        ClassLoader classLoader = getClassLoader();
        //2.通过类加载器获取到类加载的资源 (接收的包名是以/来区分)
        URL url = classLoader.getResource(packageName.replace(".", "/"));
        if (url == null) {
            LOGGER.warn("unable to retrieve anything for packageName:" + packageName);
            return null;
        }
        //3.根据不同的资源类型采用不用的方式获取资源的集合 (首先Class放在目录下面)
        Set<Class<?>> classSet = new HashSet<>();
        if ("file".equals(url.getProtocol())) {
            File packageDirectory = new File(url.getPath());
            // 抽取文件夹下的Class类

            extractClassFile(classSet, packageDirectory, packageName);
        }

        return classSet;

    }


    private static void extractClassFile(Set<Class<?>> classSet, File packageDirectory, String packageName) {
        //遍历文件夹,拿到Class类
        if (!packageDirectory.isDirectory()) {
            return;
        }
        //过滤出包路劲下的文件夹和class
        File[] files = packageDirectory.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {

                if (pathname.isDirectory()) {
                    return true;
                } else {
                    //如果是文件,判断是不是class文件
                    String absolutePath = pathname.getAbsolutePath();
                    if (absolutePath.endsWith(".class")) {
                        //将class转换为Class类,加入到集合中
                        addClassToSet(absolutePath);
                    }

                }
                return false;
            }

            private void addClassToSet(String absolutePath) {
                //1.从class文件的绝对值路径找到包含了package的类名 absolutePath: D:\spring-framework\silmpleframework\target\classes\org\silmpleframework\SpringRun.class
                String replace = absolutePath.replace(File.separator, ".");
                String substring = replace.substring(replace.indexOf(packageName));
                //包名加类名
                String rePackageName = substring.substring(0, substring.lastIndexOf("."));
                //2.利用反射生成Class类
                try {

                    Class<?> aClass = Class.forName(rePackageName);
                    classSet.add(aClass);
                } catch (ClassNotFoundException e) {
                    LOGGER.error(e);
                }
            }
        });
        //foreach之前一定要判空
        if (files != null) {
            for (File file : files) {
                extractClassFile(classSet, file, packageName);
            }
        }


    }


    /**
     * 获取当前线程的类加载器,获取到当前程序的资源信息
     *
     * @return
     */
    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

    public static <T> T newInstance(Class<?> aClass, boolean b) {
        try {
            //这里可以直接newInstance,也可以通过无参构造有参构造来实例化
            Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
            declaredConstructor.setAccessible(b);
            return (T) declaredConstructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 给实例的某一个属性设置某个实例值
     *
     * @param declaredField
     * @param target
     * @param value
     * @param accessible
     */
    public static void setFiled(Field declaredField, Object instance, Object fieldInstance, boolean accessible) {
        declaredField.setAccessible(accessible);
        try {
            declaredField.set(instance, fieldInstance);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

5.测试:

controller:

 service:

 

 

 DTO:

测试步骤:

//1.获得IOC容器
//2.初步加载Bean
//3.执行依赖注入
//4.从容器中取出bean执行方法

依赖注入成功:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空恋旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值