侧边栏壁纸
  • 累计撰写 57 篇文章
  • 累计创建 98 个标签
  • 累计收到 5 条评论

目 录CONTENT

文章目录

手把手带你写一个Mini版的Ioc容器

Sir丶雨轩
2020-05-18 / 0 评论 / 1 点赞 / 475 阅读 / 7497 字

PS:本人才疏学浅,如代码中出现什么纰漏,忘大神们不吝赐教

首先呢我们需要先了解一下什么是IOC

IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。
1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展

让我们开始吧,首先建立两个自定义注解,@Bean(声明这是一个Bean),@Inject(声明需要注入)

我会在@Bean注解上 详细的描述 2个常用的元注解的含义

package com.yuxuan66.ioc.support.annotation;

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

/**
 * @author Sir丶雨轩
 * @date 2020-05-18
 *
 * 元注解
 * 1.@Retention(RetentionPolicy value)  注解的生命周期 default = RetentionPolicy.CLASS
 * RetentionPolicy.SOURCE   表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中
 * RetentionPolicy.CLASS    表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候
 * RetentionPolicy.RUNTIME  表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时
 *
 * 2.@Target(ElementType\[\] value)  注解的作用域 可同时使用多个 @Target({ElementType.TYPE,ElementType.METHOD})
 * ElementType.TYPE                          接口、类、枚举、注解
 * ElementType.FIELD                         字段、枚举的常量
 * ElementType.METHOD                        方法
 * ElementType.PARAMETER                     方法参数
 * ElementType.CONSTRUCTOR                   构造函数
 * ElementType.LOCAL_VARIABLE                局部变量
 * ElementType.ANNOTATION_TYPE               注解
 * ElementType.PACKAGE                       包
 * ElementType.TYPE_PARAMETER (@since 1.8)   泛型参数
 * ElementType.TYPE_USE (@since 1.8)         使用泛型的地方
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {

    /**
     * beanName
     * @return beanName
     */
    String value() default "";
}

package com.yuxuan66.ioc.support.annotation;

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

/**
 * @author Sir丶雨轩
 * @date 2020-05-18
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {

    String value() default "";
}


定义一个Bean工厂接口

package com.yuxuan66.ioc.support.bean;

/**
 * @author Sir丶雨轩
 * @date 2020/5/18
 */
public interface BeanFactory {

    /**
     * 根据类型获取一个bean
     * @param type 类型
     * @param  泛型
     * @return bean
     */
    public  T getBean(Class type);

    /**
     * 根据名称获取一个bean
     * @param beanName 名称
     * @return bean
     */
    public Object getBean(String beanName);

    /**
     * 根据名称和类型获取一个bean
     * @param beanName 名称
     * @param clazz 类型
     * @param  泛型
     * @return bean
     */
    public  T getBean(String beanName,Class clazz);

}


实现这个接口

package com.yuxuan66.ioc.support.bean;

import com.yuxuan66.ioc.support.annotation.Bean;
import com.yuxuan66.ioc.support.annotation.Inject;
import com.yuxuan66.ioc.support.common.ClassScanner;
import com.yuxuan66.ioc.support.common.StrUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author Sir丶雨轩
 * @date 2020/5/18
 */
public class ApplicationContext implements BeanFactory {

    private final String packageName;

    private Map<String, Object> beans = new HashMap<String, Object>();

    private ApplicationContext(String packageName) {
        this.packageName = packageName;
    }

    private void refresh() {
        Set<Class<?>> classes = ClassScanner.getClasses(packageName);

        //初始化Bean
        classes.forEach(clazz -> {

            Bean bean = clazz.getAnnotation(Bean.class);

            if (bean != null) {

                String beanName = StrUtil.toLowerCaseFirstOne(clazz.getSimpleName());

                try {
                    beans.put(beanName, clazz.getConstructor().newInstance());
                } catch (RuntimeException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }

            }

        });

        //注入Bean
        beans.forEach((beanName, bean) -> {

            //获取所有字段
            Field[] fields = bean.getClass().getDeclaredFields();

            for (Field field : fields) {
                Inject inject = field.getAnnotation(Inject.class);
                if (inject != null) {
                    String injectBeanName = inject.value();
                    if (injectBeanName.length() == 0) {
                        injectBeanName = field.getName();
                    }
                    Object _bean = beans.get(injectBeanName);
                    if (_bean == null) {
                        throw new RuntimeException("bean " + injectBeanName + " not found");
                    }
                    field.setAccessible(true);
                    try {
                        field.set(bean, _bean);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        });

    }

    public static ApplicationContext start(String packageName) {
        ApplicationContext applicationContext = new ApplicationContext(packageName);

        applicationContext.refresh();

        return applicationContext;
    }

    @Override
    public  T getBean(Class type) {
        for (Map.Entry<String, Object> stringObjectEntry : beans.entrySet()) {
            if(stringObjectEntry.getValue().getClass().equals(type)){
                return (T) stringObjectEntry.getValue();
            }
        }
        return null;
    }

    @Override
    public Object getBean(String beanName) {
        return beans.get(beanName);
    }

    @Override
    public  T getBean(String beanName, Class clazz) {
        return (T) getBean(beanName);
    }

}


还有一些工具类我将发布在Gitee

测试使用方法

package com.yuxuan66.ioc.test;

import com.yuxuan66.ioc.support.bean.ApplicationContext;

/**
 * @author Sir丶雨轩
 * @date 2020/5/18
 */
public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = ApplicationContext.start("com.yuxuan66");
        Study study = applicationContext.getBean(Study.class);
        study.say();
    }
}


项目地址 [https://gitee.com/yuxuan-old-project/mini-ioc](https://gitee.com/yuxuan-old-project/mini-ioc)
1

评论区