spring IOC容器自自实现

流程

组件一、配置文件加载器 

public final class PropsUtil {
    /**
     * 获取文件流,转成properti map内存
     */
    public  static Properties loadProps(String fileName){
		InputStream inputStream=Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
		Properties properties=new Properties();
        properties.load(inputStream);
	}
	
	 /**
     * 通过key 获取对应value
     */
    public static String getPropertie(Properties properties,String key){
        String value="";
        if (properties.contains(key)){
            value=properties.getProperty(key);
        }
        return value;
    }
}

组件二、类加载器

/**
 * @author zhongailing
 * @version V1.0
 * @Description: 通过url加载对应类  如同scan配置 传入最基础的包名,通过包名装配初始化所有bean,
 * @date Created in 2018-11-5 15:51
 * 1.根据包名获取(文件路径),get类.class文件
 * 2.传入loadClass方法,返回类
 */
public class ClassLoadHandler {

    /**
     * 获取类加载期
     * @return
     */
    public static ClassLoader getClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }

    /**
     * 通过className加载类
     * @param className
     * @param isInitialized
     * @return
     */
    public static Class<?> loadClass(String className,Boolean isInitialized){
        Class<?> cls = null;
        try {
            //isInitialized标识是否初始化(执行静态代码块,new对象)
            cls=Class.forName(className,isInitialized,getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return cls;
    }

    /**
     * 获取该包名下的所有类
     * @param packageName
     * @return
     */
    public static Set<Class<?>> getClassSet(String packageName){
        /**
         * 1. 获取指定包名下的的所有类
         * 2. 根据包名将其转换为文件路径
         * 3. 读取class文件或jar包
         * 4. 获取指定的类名去加载类
         */
        Set<Class<?>> classSet = new HashSet<>();
        try {
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                String protocol = url.getProtocol(); //获取此 URL 的协议名称。
                if(protocol.equals("file")){
                    // %20 表示file协议?
                    String packagePath = url.getPath().replaceAll("%20", "");
                    addClass(classSet,packagePath,packageName);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classSet;
    }

    /**
     * 如果是文件,就根据包名 和 文件名 组成类的全限定名称,然后 加载类
     * @param classSet
     * @param packagePath 文件(夹)的绝对路径
     * @param packageName 和当前文件(夹) 对应的包名
     */
    public static  void addClass(Set<Class<?>> classSet,String packagePath,String packageName){

        File[] files = new File(packagePath).listFiles(file -> {
            // 只需要 文件并且是.class的文件,或则是目录 都返回true
            return file.isFile() && file.getName().endsWith(".class") || file.isDirectory();
        });

        for (File file : files) {
            String fileName = file.getName();
            if(file.isFile()){ // 是指定的文件 就获取到全限定类名 然后装载它
                String className = fileName.substring(0, fileName.lastIndexOf(".")); // 把.class后最截取掉
                if(StringUtils.isNotBlank(packageName)){
                    className = packageName + "." + className; // 根据包名 + 文件名 得到这个类的全限定名称,
                }
                Class<?> cls = loadClass(className, false);
                classSet.add(cls);
            }else { // 是文件 就递归自己. 获取 文件夹的绝对路径,和 当前文件夹对应的 限定包名.方便 文件里面直接使用
                String subPackagePath= fileName;
                if(StringUtils.isNotBlank(subPackagePath)){
                    subPackagePath = packagePath + "/" + subPackagePath; // 第一次:由基础包名 得到绝对路径,再加上当前文件夹名称 = 当前文件夹的绝对路径
                }
                subPackagePath = file.getAbsolutePath(); // 该方法获得文件的绝对路径.和上面的代码效果是一致的
                String subPackageName = fileName;
                if(StringUtils.isNotBlank(subPackageName)){
                    subPackageName = packageName + "." + subPackageName; // 第一次: 基础包名 加文件夹名称 组合成 当前包名 +
                }
                addClass(classSet,subPackagePath, subPackageName);
            }
        }
    }

    public static void main(String[] args) {
        getClassSet("java/ioc");
    }

增加按照注解区分@service@controller 不同bean的kv(class-obj)结构

/**
 * @author zhongailing
 * @version V1.0
 * @Description: 在装配的class中区分出@Service@Controller对应的bean
 * @date Created in 2018-11-5 16:15
 */
public final class AnnotationMapHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);
    private static final Set<Class<?>> CLASS_SET;
    static{
        LOGGER.debug("是否被加载了两次");
        CLASS_SET = ClassLoadHandler.getClassSet(PropsUtil.getPropertie(null,"scan-package-name"));
    }

    public static Set<Class<?>> getClassSet() {
        return CLASS_SET;
    }

    /**
     * 获取所有Services类型的注解类
     * @return
     */
    public static Set<Class<?>> getServiceClassSet(){
        Set<Class<?>> serviceClassSet = new HashSet<>();
        for (Class<?> c : CLASS_SET) {
            if(c.isAnnotationPresent(Services.class)){ // 是否存在 services注解
                serviceClassSet.add(c);
            };
        }
        return serviceClassSet;
    }

    /**
     * 获取所有Controller类型的注解类
     * @return
     */
    public static Set<Class<?>> getControllerClassSet(){
        Set<Class<?>> controllerClassSet = new HashSet<>();
        for (Class<?> c : CLASS_SET) {
            if(c.isAnnotationPresent(Controller.class)){
                controllerClassSet.add(c);
            };
        }
        return controllerClassSet;
    }

    /**
     * 获取所有的 bean : controller 和 Services 注解的类
     * 类似 component =所有controller、service、repository bean
     * @return
     */
    public static Set<Class<?>> getBeanClassSet(){
        Set<Class<?>> beanClassSet = new HashSet<>();
        beanClassSet.addAll(getControllerClassSet());
        beanClassSet.addAll(getServiceClassSet());
        return beanClassSet;
    }

组件三、bean容器

/**
 * 维护静态map<class类型,Object对象> 通过类名获取bean对象
 * bean初始化 即加载包下所有class,add到map中
 */
public class BeanContainer {
    private static final Map<Class<?>, Object> BEAN_MAP = new HashMap<>();

    static {
        Set<Class<?>> beanClassSet = AnnotationMapHandler.getClassSet();
        for (Class<?> c : beanClassSet) {
            BEAN_MAP.put(c, ReflectionHandler.newInstance(c));  //创建所有class的实例(自己定义的注解类)
        }
    }

    /**
     * bean 容器
     **/
    public static Map<Class<?>, Object> getBeanMap() {
        return BEAN_MAP;
    }

    /**
     * 获取bean
     **/
    public static <T> T getBean(Class<T> cls) {
        if (!BEAN_MAP.containsKey(cls)) {
            throw new RuntimeException("can not get bean by class : " + cls);
        }
        return (T) BEAN_MAP.get(cls);
    }

    /**
     * 添加bean 实例
     **/
    public static void setBean(Class<?> cls, Object obj) {
        BEAN_MAP.put(cls, obj);
    }
}

IOC 反射将bean 通过setField 设置到使用bean的成员变量中

/**
 * @author zhongailing
 * @version V1.0
 * @Description: 通过遍历BeanContainer里的所有cls,初始化对应bean实例,然后通过反射setField把注入的bean set到类中成为其成员变量。
 * @date Created in 2018-11-5 16:37
 */
public class IocHandler {
    static {
        Map<Class<?>, Object> beanMap = BeanContainer.getBeanMap(); //框架需要管理的bean映射
        if (MapUtils.isNotEmpty(beanMap)) {
            for (Map.Entry<Class<?>, Object> ent : beanMap.entrySet()) {
                Class<?> beanCls = ent.getKey();
                Object beanInstance = ent.getValue();
                // 获取该class所有的成员属性,包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段
                Field[] beanFields = beanCls.getDeclaredFields();
                for (Field beanField : beanFields) {
                    //判断 该字段是否 包含 inject注解
                    if (beanField.isAnnotationPresent(Inject.class)) {
                        Class<?> beanFieldType = beanField.getType(); //声明类型 成员变量的Class
                        Object beanFieldInstance = beanMap.get(beanFieldType); // 获取该类型的实例
                        if (beanFieldInstance != null) {
                            ReflectionHandler.setField(beanInstance, beanField, beanFieldInstance); // 把对应的成员变量属性 赋值
                        }
                    }
                }
            }
        }
    }
}

反射处理类

/**
 * @author zhongailing
 * @version V1.0
 * @Description: 通过ClassLoaderHandler 加载的class类来初始化对象
 * java反射 clazz.newInstance
 * @date Created in 2018-11-5 16:23
 */
public class ReflectionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class);

    /** 创建实例 **/
    public static Object newInstance(Class<?> cls){
        Object ins = null;
        try {
            ins = cls.newInstance();
        } catch (Exception e) {
            LOGGER.error("new instance failure");
            throw new RuntimeException(e);
        }
        return ins;
    }

    public static Object newInstance(String className){
        Object ins = null;
        try {
            Class<?> cls = Class.forName(className);
            ins = cls.newInstance();
        } catch (Exception e) {
            LOGGER.error("new instance failure");
            throw new RuntimeException(e);
        }
        return ins;
    }

    /**
     * 调用方法
     * @param obj 实例
     * @param method 方法
     * @param args 参数
     * @return
     */
    public static Object invokeMethod(Object obj, Method method, Object...args){
        method.setAccessible(true); //取消访问权限检查
        Object result = null;
        try {
            result = method.invoke(obj, args);
        } catch (Exception e) {
            LOGGER.error("invoe method fail",e);
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 调用不带参数的方法
     * @param obj
     * @param method
     * @return
     */
    public static Object invokeMethod(Object obj,Method method){
        method.setAccessible(true); //取消访问权限检查
        Object result = null;
        try {
            result = method.invoke(obj);
        } catch (Exception e) {
            LOGGER.error("invoe method fail",e);
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 设置成员变量
     * @param obj
     * @param field
     * @param value
     */
    public static void setField(Object obj, Field field, Object value){
        try {
            field.setAccessible(true); //取消访问权限检查
            field.set(obj,value);
        } catch (Exception e) {
            LOGGER.error("set Field fail",e);
            throw new RuntimeException(e);
        }
    }
}

最后流程总结,别人家的图一个意思

这里写图片描述

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值