手撸一个IOC容器

手撸一个IOC容器,完成依赖的注入以及类的查找。

分两步

1、进行类的加载,将带有特殊注解的类注册到容器当中

2、进行依赖的注入,完成带有特定标签的属性的值的自动注入。

一、创建一堆注解,用于表示标识类需要被加载到容器中 @Component 、@Controller、@Service、@Repository

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}

 

 

二、创建一个单例BeanContainer,作为IOC容器

 

/**
 * 下面是实现一个单例的容器
 */
private  BeanContainer(){}
public static BeanContainer getInstance(){
    return ContainerHolder.HOLDER.instance;
}

//利用枚举的特性,创建单例,可以防止反射和序列化造成多个实例的问题。
private enum ContainerHolder{
    HOLDER;
    private BeanContainer instance;
    ContainerHolder(){
        instance = new BeanContainer();
    }
}
  /**
  * 存放所有的被标记的目标对象
  * 想当与从一个包中查找出的带有特定标签(待注入的类)的类都放在这个MAP当中
  * object相当于newInstance创建的实例
  */
  private final static ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<Class<?>, Object>();

  /**
  * 将需要被处理的注解放到一个列表当中
  */
  private final static List<Class<? extends Annotation>> beanAnno = new ArrayList<Class<? extends Annotation>>(){{
    add( Component.class);
    add( Controller.class);
    add( Repository.class);
    add( Service.class);
  }

 

核心代码loadBeans,把包内的类加载到容器当中

    /**
     * 加载所有的Bean
     * @param packageName
     */
    public void loadBeans(String packageName){

        Set<Class<?>> classSet = new HashSet<Class<?>>();
        classSet = ClassUtil.extractPackageClass(packageName);
        if(classSet == null || classSet.isEmpty()){
            System.out.println("包下没有任何类");
            return;
        }

        //如果存在class
        for (Class<?> clazz : classSet){
            //查看某个类型是不是有列表中的注解
            for (Class<? extends Annotation> annoClass:beanAnno){
                if(clazz.isAnnotationPresent(annoClass)){
                    beanMap.put(clazz,getNewInstance(clazz,true));
                }
            }
        }
    }

    /**
     * 获取新实例
     * @param clazz
     * @param accessible
     * @param <T>
     * @return
     */
    private <T> T getNewInstance(Class<?> clazz,boolean accessible )   {
        try{
            Constructor<?> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(accessible);
            return (T)constructor.newInstance();
        }
        catch (Exception ex){
            ex.printStackTrace();
            throw  new  RuntimeException();
        }
    }

 

三、创建一些容器的操作

    /**
     * 添加bean
     * @param clazz
     * @param obj
     * @return
     */
    public  Object addBean(Class<?> clazz,Object obj){
        return beanMap.put(clazz,obj);
    }

    /**
     * 删除Bean
     * @param clazz
     * @return
     */
    public  Object removeBean(Class<?> clazz){
        return  beanMap.remove(clazz);
    }

    /**
     * 获取bean
     * @param clazz
     * @return
     */
    public  Object getBean(Class<?> clazz){
        return beanMap.get(clazz);

    }

    /**
     * 获取所有的bean
     * @return
     */
    public  Set<Object> getBeans(){
        return  new HashSet<Object>(beanMap.values());
    }

    /**
     * 获取所有的Classes
     * @return
     */
    public  Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }

    /**
     * 获取指定注解的类
     * @param annoClass
     * @return
     */
    public  Set<Class<?>> getClassesByAnno(Class<? extends Annotation> annoClass){
        Set<Class<?>> classes = getClasses();
        if(classes ==null || classes.isEmpty()){
            System.out.println("没找到指定注解的类型");
        }

        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for(Class<?> clazz : classes){
            if(clazz.isAnnotationPresent(annoClass)){
                classSet.add(clazz);
            }
        }
        return classSet;
    }

    /**
     * 获取指定接口的类
     * @param interfaceOrClass
     * @return
     */
    public  Set<Class<?>> getClassesByInterfaceOrClass(Class<?> interfaceOrClass){
        Set<Class<?>> classes = getClasses();
        if(classes ==null || classes.isEmpty()){
            System.out.println("没找到指定接口的类型");
        }

        Set<Class<?>> classSet = new HashSet<Class<?>>();
        for(Class<?> clazz : classes){
            if(clazz.isAssignableFrom(interfaceOrClass)){
                classSet.add(clazz);
            }
        }
        return classSet;
    }

 

创建一个工具类

public class ClassUtil {

    public static final String FILE_PROTOCOL = "file";

    /**
     * 获取包下的所有的类的集合
     * @param packageName
     */
    public static Set<Class<?>> extractPackageClass(String packageName){
        Set<Class<?>> classSet = null;
        //获取类的加载器,主要为了解决实际路径的问题,包名没办法定位
        ClassLoader classLoader = getClassLoader();
        //通过类加载器加载资源
        URL url = classLoader.getResource(packageName.replace('.', '/'));
        if(url==null){
            System.out.println("资源不存在");
            return classSet;
        }

        //根据不同的资源类型,通过不同的方式获取资源
        //如果是文件协议
        if(url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
            classSet = new HashSet<Class<?>>();
            File packageDir = new File(url.getPath());
            extractClassFile(classSet,packageDir,packageName);
        }

        return classSet;
    }

    /**
     * 提取类文件(需要进行递归,判断是不是文件夹还是文件)
     * @param classSet
     * @param fileSoucre
     * @param packageName
     */
    private static void extractClassFile(final Set<Class<?>> classSet, File fileSoucre, final String packageName) {
        //如果是文件的情况
        if(!fileSoucre.isDirectory()){
            return;
        }
        else {
            //文件夹的情况,列出文件夹下的所有文件
            File[] files = fileSoucre.listFiles(new FileFilter() {
                public boolean accept(File file) {
                     if(file.isDirectory()){
                         return true;
                     }
                     else {
                         //获取文件的绝对路径
                         String absolutePath = file.getAbsolutePath();
                         if(absolutePath.endsWith(".class")){
                                //class文件直接加载
                                add2ClassSet(absolutePath);
                                return true;
                         }

                     }
                     return false;
                }

                /**
                 * 添加class到classSet当中
                 * @param absolutePath
                 */
                private void add2ClassSet(String absolutePath) {
                    System.out.println(absolutePath);
                    //从绝对路径中获取到包名+类名
                    absolutePath = absolutePath.replace(File.separator,".");
                    //根据传进来的packagename,去掉路径中的包名,只得到
                    String className = absolutePath.substring(absolutePath.indexOf(packageName));
                    className = className.substring(0,className.lastIndexOf('.'));
                    Class<?> aClass = loadClass(className);
                    classSet.add(aClass);
                }

                private Class<?> loadClass(String className) {
                    try{
                        return Class.forName(className);
                    }
                    catch (Exception ex){
                        System.out.println("load class error");
                        throw new RuntimeException();
                    }
                }

            });

            if(files!=null){
                //遍历每一个文件
                for (File f : files){
                    extractClassFile(classSet,f,packageName);
                }
            }
        }
    }

    /**
     * 获取ClassLoader
     * @return
     */
    public static ClassLoader getClassLoader(){
        //通过这个方式获取ClassLoader实例
        return Thread.currentThread().getContextClassLoader();
    }

}

  

四、实现依赖注入,创建一个注解@Autowire,限定为Field使用

@Retention(value = RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowire {

    /**
     * 定义一个属性,用于存储默认的实现
     * @return
     */
    public String value() default "";
}

创建一个DependcyInjector类,可以实现类中的依赖注入。

public class DependcyInjector {

    private BeanContainer beanContainer;

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

    /**
     * 执行依赖注入
     */
    public void doIoc(){
        //1、遍历Bean容器当中所有的Class对象
        Set<Class<?>> iocClasses = beanContainer.getClasses();
        for (Class<?> clazz : iocClasses){
            //2、遍历类中的所有的属性
            Field[] fields = clazz.getDeclaredFields();
            for(Field field : fields){
                //3、判断属性是否带有AutoWire注解,然后通过注入该属性
                if(field.isAnnotationPresent(Autowire.class)){
                    //4、获取这些被Autowire注解修饰的类
                    Class<?> autowireType = field.getType();
                    //5、获取这个类型的实例,要考虑多个实现的情况如何获取,获取哪一个
                    String autowireValue = field.getDeclaredAnnotation(Autowire.class).value();

                    Object fieldInstance = getFieldInstance(autowireType, autowireValue);
                    if(fieldInstance ==null){
                        System.out.println(MessageFormat.format("注入失败,属性{0}实现类不存在",autowireType.getSimpleName()));
                    }
                    //6、将属性的实现类实例,注入到属性当中
                    Object classBean = beanContainer.getBean(clazz);
                    //设置值到Field当中
                    SetField(field,fieldInstance,classBean,true);
                }
            }
        }
    }

    /**
     * 设置属性
     */
    private void SetField(Field field,Object fieldInstance,Object target,boolean access) {
        field.setAccessible(access);
        try{
            field.set(target,fieldInstance);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    /**
     * 获取属性的实例
     * @param fieldClass
     * @param autowireValue
     * @return
     */
    private Object getFieldInstance(Class<?> fieldClass,String autowireValue) {

        //从容器中获取实例
        Object filedClassInstance = beanContainer.getBean(fieldClass);
        if(filedClassInstance !=null){
            return filedClassInstance;
        }
        else {
            //可能传入的不是类,而是接口,需要获取接口对应的实现类
            Class<?> implementClass = getImplementClass(fieldClass, autowireValue);
            if(implementClass!=null){
                return beanContainer.getBean(implementClass);
            }
            return  null;
        }

    }

    /**
     * 获取接口对应的实现类
     * @param interfaceClass
     * @return
     */
    private Class<?> getImplementClass(Class<?> interfaceClass,String autowireValue) {

        Set<Class<?>> implClasses = beanContainer.getClassesByInterfaceOrClass(interfaceClass);

        //如果实现类不存在
        if(implClasses == null || implClasses.isEmpty()){
            System.out.println(MessageFormat.format("接口{0}的实现类型不存在",interfaceClass.getName().toString()));
            return null;
        }

        //如果autowirevalue是默认值,并且实现类只有一个,此时返回对应的默认的实现类
        if(autowireValue.equals("") && implClasses.size() ==1){
            return implClasses.iterator().next();
        }

        //如果指定了实现类名称
        if(!autowireValue.equals("")){
            //存在多个
            for (Class<?> clazz : implClasses){
                if(clazz.getSimpleName().equals(autowireValue)){
                    return  clazz;
                }
            }
            System.out.println(MessageFormat.format("接口{0}存在的实现类中不存在名为{1}的实现",interfaceClass.getName().toString(),autowireValue));
        }

        return null;
    }
}

五、使用测试

创建几个测试用的类

 

@Component
public class User {

    private String name;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

@Service
public class Student extends User{
    public String getLevel() {
        return level;
    }
    public void setLevel(String level) {
        this.level = level;
    }
    private String level;
    public String Hello(){
        return "HelloWorld";
    }
}

//学生服务 @Service public class StudentService { @Autowire private Student stu ; public Student getStudent(){ stu.setLevel("2"); stu.setAge(11); stu.setName("123123"); return stu; } }
//Hello服务 @Service public class HelloService { @Autowire private StudentService studentService; public void sayHello(){ System.out.println(studentService.getStudent().getName() + " Hello"); } }

Main方法运行

    public static void main(String[]args){

        //初始化容器
        BeanContainer ioc = BeanContainer.getInstance();
        ioc.loadBeans("org.simpleframework.entity");
        ioc.loadBeans("org.simpleframework.service");
        //实现依赖注入
        DependcyInjector injector = new DependcyInjector();
        injector.doIoc();

        //执行代码
        HelloService helloService =(HelloService)ioc.getBean(HelloService.class);
        helloService.sayHello();

    }

  

执行结果可以看到类都加载到BeanContainer,并且服务内方法也成功调用了:

E:\Projects\myioc\target\classes\org\simpleframework\entity\Student.class
E:\Projects\myioc\target\classes\org\simpleframework\entity\User.class
E:\Projects\myioc\target\classes\org\simpleframework\service\HelloService.class
E:\Projects\myioc\target\classes\org\simpleframework\service\StudentService.class
123123 Hello

 

总结,可以简单归纳如图

 

 

posted @ 2020-11-23 17:31  奋斗的大橙子  阅读(202)  评论(0编辑  收藏  举报