Simple implementation of Spring container (4) dependency injection

Stage 4:

// 1.编写自己的Spring容器,实现扫描包,得到bean的class对象.
// 2.扫描将 bean 信息封装到 BeanDefinition对象,并放入到Map.
 //3.初始化单例池并完成getBean() createBean()方法
 4.完成依赖注入(如果创建某个Bean对象,存在依赖注入,需要进行bean组装操作)

Idea:

1. Create a new annotation @autowired under the annotation package
2. Add an attribute monsterDao under the MonsterService class under the component package and reference the MonsterDao class
3. Key: In the creatBean method, when reflection obtains an instance, this instance is added with dependency injection business logic. (Because the class object cla of the Component component has been obtained here, through this cla object, the fields of the class can be obtained filed. Get this field and then judge whether your field has an autowired annotation modification, and then proceed to the next step)

Code

Annotation @autowired

package com.elf.spring.annotation;

import java.lang.annotation.*;

/**
 * @author 45~
 * @version 1.0
 */
@Target({
    
     ElementType.METHOD,  ElementType.FIELD}) //实现在方法上和字段上即可
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    
    
   //required()属性是true,表示,如果字段用@Autowired修饰,那么一定要找到匹配的,找不到会报错
    // boolean required() default true;
    //这里简单做的,只要用@Autowired修饰了,那么就完成依赖属性,这里required属性没有也无所谓,
    //这里默认为true
}

2. Add a method to MonsterDao to verify whether the injection is successful

package com.elf.spring.component;
import com.elf.spring.annotation.Component;
/**
 * @author 45~
 * @version 1.0
 * 写第二个类是因为要有两个类才能做依赖注入
 */
@Component(value = "monsterDao")
public class MonsterDao {
    
    
    public void hi(){
    
    
        System.out.println("MonsterDao~hi()");
    }
}

3.MonsterService.java

package com.elf.spring.component;
import com.elf.spring.annotation.Autowired;
import com.elf.spring.annotation.Component;
import com.elf.spring.annotation.Scope;

/**
 * @author 45~
 * @version 1.0
 * 说明 MonsterService 是一个Servic
 */
@Component //把MonsterService注入到我们自己的spring容器中
@Scope(value = "prototype")
public class MonsterService {
    
    
    //这里@Autowired匹配的形式很多,不用每个都去实现,这里先按名字来装配
    //这里使用的是自己定义的@Autowired来修饰属性,表示该属性,是通过容器完成依赖注入的
    //说明:我们实现按照名字来进行组装
    @Autowired
    private MonsterDao monsterDao;
    public void m1(){
    
    
        monsterDao.hi();
    }
}

4. Container files
Insert image description here

java.lang.IllegalAccessException: Class
com.elf.spring.ioc.ElfSpringApplicationContext can not access a member
of class com.elf.spring.component.MonsterService with modifiers
“private”

Exception in thread “main” java.lang.NullPointerException at
com.elf.spring.AppMain.main(AppMain.java:22)
Null pointer and illegal access exception are reported. If the attribute is private in reflection, it must be blasted.

package com.elf.spring.ioc;

import com.elf.spring.annotation.*;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author 45~
 * @version 1.0
 */
public class ElfSpringApplicationContext {
    
    
    //第一步,扫描包,得到bean的class对象,排除包下不是bean的,因此还没有放到容器中
    //因为现在写的spring容器比原先的基于注解的,要更加完善,所以不会直接把它放在ConcurrentHashMap
    private Class configClass;

    //定义属性BeanDefinitionMap -> 存放BeanDefinition对象
    private  ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
    //定义属性SingletonObjects -> 存放单例对象 (跟原生容器的名字保持一致)
    //因为将来存放单例池的时候肯定要指定单例对象是对应哪个Bean的,所以k用String来充当
    //存放单例对象的类型是不确定的,可能是Dog,Cat,或者其他的对象,所以用Object
    private  ConcurrentHashMap<String, Object> singletonObjects =
            new ConcurrentHashMap<>();

    //构造器
    public ElfSpringApplicationContext(Class configClass) {
    
    

        //完成扫描指定包
        beanDefinitionsByScan(configClass);

        //通过beanDefinitionMap,初始化singletonObjects单例池
        //封装成方法
        //遍历所有的beanDefinition,用到集合和枚举的知识
        Enumeration<String> keys = beanDefinitionMap.keys();//把所有bean的名字拿到
        while (keys.hasMoreElements()) {
    
    
            //得到beanName
            String beanName = keys.nextElement();
            //通过beanName得到对应的beanDefinition对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //判断该bean是singleton还是prototype
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
    
    
                //将该bean实例放入到singletonObjects集合
                Object bean = createBean(beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
//        System.out.println("singletonObjects 单例池=" + singletonObjects);
//        System.out.println("beanDefinitionMap=" + beanDefinitionMap);

    }//构造器结束

    //该方法完成对指定包的扫描,并将Bean信息封装到BeanDefinition对象,在放入到Map
    public void beanDefinitionsByScan(Class configClass) {
    
    
        this.configClass = configClass;

        /**获取要扫描的包:
         1.先得到ElfSpringConfig配置的 @ComponentScan(value= "com.elf.spring.component")
         2.通过 @ComponentScan的value => 即要扫描的包 **/
        ComponentScan componentScan =
                (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
//        System.out.println("要扫描的包path=" + path);

        /**
         * 得到要扫描包下的所有资源(类.class)
         * 1.得到类的加载器 -> APP类加载器是可以拿到 target目录下的classes所有文件的
         * 2.通过类的加载器获取到要扫描的包的资源url => 类似一个路径
         * 3.将要加载的资源(.class)路径下的文件进行遍历 => io
         */
        ClassLoader classLoader = ElfSpringApplicationContext.class.getClassLoader();
        path = path.replace(".", "/"); // 把.替换成 /
        URL resource = classLoader.getResource(path);
//        System.out.println("resource=" + resource);

        File file = new File(resource.getFile());
        if (file.isDirectory()) {
    
    
            File[] files = file.listFiles();
            for (File f : files) {
    
     //把所有的文件都取出来
//                System.out.println("============================");
//                System.out.println("f.getAbsolutePath()=" + f.getAbsolutePath());
                String fileAbsolutePath = f.getAbsolutePath();//到target的classes目录下了

                //这里只处理.class文件
                if (fileAbsolutePath.endsWith(".class")) {
    
    
                    //1.获取类名
                    String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
                            fileAbsolutePath.indexOf(".class"));
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    //System.out.println("classFullName=" + classFullName);
                    //3.判断该类是否需要注入,就看是不是有注解@Component @Service @Contoller @Re....
                    try {
    
    
                        Class<?> cla = classLoader.loadClass(classFullName);
                        if (cla.isAnnotationPresent(Component.class) ||
                                cla.isAnnotationPresent(Controller.class) ||
                                cla.isAnnotationPresent(Service.class) ||
                                cla.isAnnotationPresent(Repository.class)) {
    
    
                            //演示机制
                            //如果该类使用了@Component注解,说明是一个Spring bean
                            System.out.println("这是一个Spring bean=" + cla + " 类名=" + className);

                            //先得到beanName(有可能通过经典4注解,例如Component注解的value值来指定)
                            //1.得到类上的Component注解,此时的clazz已经是当前bean的class对象,通过类加载器得到的 反射知识
                            Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);
                            //2.得到配置的value
                            String beanName = componentAnnotation.value();
                            if ("".equals(beanName)) {
    
    //如果没有写value,空串
                                //将该类的类名首字母小写作为beanName
                                //StringUtils其实是在springframework的框架下面的类,而这里本身我就是要自己写所以不用

                                beanName = StringUtils.uncapitalize(className);
                            }
                            //3.将Bean的信息封装到BeanDefinition对象中,然后将其放入到BeanDefinitionMap集合中
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //!!!多看看这里多理解
                            beanDefinition.setClazz(cla);//由类加载器通过反射得到对象,Class<?> cla = classLoader.loadClass(classFullName);
                            //4.获取Scope值
                            if (cla.isAnnotationPresent(Scope.class)) {
    
    
                                //如果配置了Scope,就获取它配置的值
                                Scope scopeAnnotation = cla.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
    
    
                                //如果没有配置Scope,就以默认的值singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinitionMap对象放入Map
                            beanDefinitionMap.put(beanName, beanDefinition);
                        } else {
    
    
                            //如果该类没有使用了@Component注解,说明是一个Spring bean
                            System.out.println("这不是一个Spring bean" + cla + " 类名=" + className);
                        }
                    } catch (Exception e) {
    
    
                        e.printStackTrace();
                    }
                }
//                System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
            }//遍历文件结束
        }
    }

    //完成createBean(BeanDefinition beanDefinition)方法
    private  Object createBean(BeanDefinition beanDefinition) {
    
    
        //得到Bean的Clazz对象
        Class clazz = beanDefinition.getClazz();
        try {
    
    
            //使用反射得到实例
            Object instance = clazz.getDeclaredConstructor().newInstance();

            //这里加入依赖注入的业务逻辑!!!
            //1.遍历当前要创建的对象的所有字段( 比如:要创建MonsterService,需要把所有的字段都扫描一遍,看看字段上有没有@Autowired的注解)
            for(Field declaredField : clazz.getDeclaredFields()){
    
    
               //2.判断这个字段是否有@Autwired的修饰
                if(declaredField.isAnnotationPresent(Autowired.class)){
    
    
                    //提示:这里处理@Autowired的required 
//                    Autowired annotation = declaredField.getAnnotation(Autowired.class);
//                    annotation.required()=>进行下一步其他处理
                    
                    //3.得到这个字段的名字
                    String name = declaredField.getName();
                    //4.通过getBean()方法来获取要组装的对象
                    Object bean = getBean(name);
                    //5.进行组装
                    declaredField.setAccessible(true);//因为反射里属性是私有的,须进行暴破.
                    declaredField.set(instance,bean);
                }
            }

            return instance;
        } catch (InstantiationException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        } catch (InvocationTargetException e) {
    
    
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
    
    
            e.printStackTrace();
        }
        //如果反射创建对象失败
        return null;
    }

    //编写getBean(String name)方法,返回容器中的对象.
    // 既然是getBean()那么返回类型是Object,因为不管是单例池还是创建的对象也好,类型是不确定的
    public  Object getBean(String name) {
    
    
        //加一个判断,严谨. 传入的beanName是否在beanDefinitonMap中存在..
        if(beanDefinitionMap.containsKey(name)) {
    
    //如果存在
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            //得到beanDefinition的scope,分别进行处理
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
    
    
                //说明是单例配置,就直接从单例池获取
                return singletonObjects.get(name);
            } else {
    
    //如果不是单例的,我们就调用createBean, 反射一个对象
                return createBean(beanDefinition);
            }
        } else {
    
     //如果不存在,就抛出一个空指针异常,也可自定义
            throw new NullPointerException("没有该bean");

        }

    }

}

5.MainApp.java

package com.elf.spring;
import com.elf.spring.component.MonsterDao;
import com.elf.spring.component.MonsterService;
import com.elf.spring.ioc.ElfSpringApplicationContext;
import com.elf.spring.ioc.ElfSpringConfig;

/**
 * @author 45~
 * @version 1.0
 */
public class AppMain {
    
    
    public static void main(String[] args) {
    
    
        //把容器创建起来,在创建的时候传入了配置类的class类型/class对象
        //传进去后会根据自己写的容器机制 进行解析
        ElfSpringApplicationContext elfSpringApplicationContext =
                new ElfSpringApplicationContext(ElfSpringConfig.class);

        //测试一下依赖注入的功能
        MonsterService monsterService =
                (MonsterService)elfSpringApplicationContext.getBean("monsterService");

        monsterService.m1();
//        MonsterService monsterService =
//                (MonsterService)ElfSpringApplicationContext.getBean("monsterService");
//        MonsterService monsterService2 =
//                (MonsterService)ElfSpringApplicationContext.getBean("monsterService");
//        System.out.println("monsterService" + monsterService);
//        System.out.println("monsterService2" + monsterService2);
//
//        MonsterDao monsterDao =
//                (MonsterDao)ElfSpringApplicationContext.getBean("monsterDao");
//        System.out.println("monsterDao" + monsterDao);
//        MonsterDao monsterDao2 =
//                (MonsterDao)ElfSpringApplicationContext.getBean("monsterDao");
//        System.out.println("monsterDao2" + monsterDao2);
//        System.out.println("ok");
        System.out.println("ok");
    }
}

Insert image description here

Guess you like

Origin blog.csdn.net/weixin_45036508/article/details/134900960