手写Spring

复盘Spring手写过程

流程图

ioc容器代码

package Myspring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

public class lxyApplicationContext {
    private Class config;
    /*
    四、判断getBean获取到的是single还是prototype
      ConcurrentHashMap 二级哈希表
       优点: 线程安全执行效率高<非阻塞>
       用处: 多线程,高并发时使用较多
    * */
    private ConcurrentHashMap<String ,Object> Singletonpoolmap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    public lxyApplicationContext(Class config) {
        this.config = config;
       Scan(config);
       /*
       * 目的: 在lxyApplicationContext创建的时候将把单例Bean对象创建好
       * 在getBean的时候直接调用
       *  1. 先获取beanDefinitionMap中所有的key(BeanName)
       *  知识点:
       *   1.Map.Entry是Map的一个接口,属于Map遍历的其中一种方式
       * */
        for(Map.Entry<String,BeanDefinition> entry : beanDefinitionMap.entrySet() ){
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            if (beanName.equals("singleton")) {
                Object creatBean = creatBean(beanDefinition);
                Singletonpoolmap.put(beanName,creatBean);
            }
        }


    }

    /*
    * 通过Compontentclass的定义类(BeanDefinition)创建一个单例对象*/
    private Object creatBean(BeanDefinition beanDefinition) {
       /*在beanDefinition中有Class属性,使用反射.构造方法创建实例,并返回实例对象
          在这需要获取class
          Type FiledName ;通过这两个关键代表去找到Value(对象值)
         fields.set(Object lxyService, Object Singlelenpoor) 通过反射给属性赋值(依赖注入)
        */
        try {
            Object instance = beanDefinition.getBclass().getDeclaredConstructor().newInstance();
            for (Field fields : beanDefinition.getBclass().getDeclaredFields()) {
                if (fields.isAnnotationPresent(Autowired.class)) {
//                    Object bean = Singletonpoolmap.get(instance); //这样不太好
                    Object bean = getBean(fields.getName());
                    fields.setAccessible(true);//因为是私有属性,所以需要权限
                    fields.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;
    }

    private void Scan(Class config) {
         /*
        一、判断配置类中是否存在ComponentScan,并取值
           - Class getDeclaredAnnotation -->返回Appconfig中额所有注解(更加精准)
             原因: 解析类,并不是真的把Appconfig的类声明进行解析,而是更加关注于注解
           - 获取ComponentScan中自己写进去的value值(包名)
         */
        ComponentScan classCompontScan = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
        String value = classCompontScan.value();
        String valuesymbol = value.replace(".", "\\");

        /*
        二、通过提供包名的ioc容器类.class,获取包下所有类(.class的file类型)
        * 通过类加载器获取到Service下面的所有类(App<属于自定义类型>)
          判断这个文件是否是文件夹,是则获取全限包名
        * */
        ClassLoader classLoader = lxyApplicationContext.class.getClassLoader();
        File file =new File(classLoader.getResource(valuesymbol).getFile()) ;
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File fs : files) {
                String absFilepath = fs.getAbsolutePath();
                /*
                  abs: C:\Spring_review\target\classes\Service\Lxyservice.class
                * 格式处理: 反射获取类信息(Service\\lxyservice)-->(Service.lxyservice)<反射获取类的写法和格式!!>
                  - String substring(int x, int y); 分割字符串
                  - int indexof(String x); 截取字符串下标
                  - String replace("$","@") 符号替换
                * */
                if(absFilepath.endsWith(".class")){
                    String className = absFilepath.substring(absFilepath.indexOf("Service"), absFilepath.indexOf(".class"));
                    String classNamesymbol = className.replace("\\", ".");

                    /*
                    三、 使用同一个classLoader加载classNamesymbol得到类对象
                    * - classLoader.loadClass 得到的class只进行加载,并没有进行连接和初始化??
                    * - isAnnotationPresent Component类型的注解是否在lxyservice类上
                      - getDeclaredAnnotation
                        该方法获取直接修饰该class对象对应类的指定类型的Annotation,如果不存在,则返回null
                    * */
                    try {
                        Class<?> aClass = classLoader.loadClass(classNamesymbol);
                        /*
                         * 判断当前类对象是否存在此注解,存在意味着当前class是一个Bean
                         * Bean类型 1.单例 2.原型*/
                        //这个证明了它存在为true,因为执行了下面的语句
                        if (aClass.isAnnotationPresent(Component.class)) {
                            //为什么获取不到lxyservice类上的@Component注解,明明写了??
                            Component component = aClass.getDeclaredAnnotation(Component.class);
                            String beanName = component.value();
                            /*包空指针的原因: 空的东西调用了方法*/
                            BeanDefinition beanDefinition = new BeanDefinition();
                            /*
                            错误1: 未生成aclass
                            * 生成BeanDefinition对象,<String beanName, Class aclass(Lxyservice)>
                            */
                            beanDefinition.setBclass(aClass);
                            if(aClass.isAnnotationPresent(Scope.class)){
                                Scope scopeComponent = (Scope) aClass.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScopetype(scopeComponent.value());
                                beanDefinition.setScopetype("prototype");
                            }
                            else {
                                beanDefinition.setScopetype("singleton");
                            }
                            /*
                             * 当我扫描出一个带有component注解的class
                             * 将这个类的BeanName和类的定义放进Bean定义池中
                             * */
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    //问题1: 根据(Componentname)字符串找到对应的class,生成对应的Bean对象?
    public Object getBean(String beanName){
        if(beanDefinitionMap.containsKey(beanName)){
            /*
            * 从BeanDefinition池中通过BeanName获取BeanName对象
            * 判断此对象的作用域范围是singleton还是prototype
            *  1. 单例: 就直接从单例池中取出来
            *  2. 原型: 每次getBean的时候都应该创建BeanClass对象???
            * */
            BeanDefinition beanDefinitionobj = beanDefinitionMap.get(beanName);
            if(beanDefinitionobj.getScopetype().equals("singleton")){
                Object singletonBean = Singletonpoolmap.get(beanName);
                return singletonBean;//返回的是BeanDefinition对象
            }else{
                Object prototypeBean = creatBean(beanDefinitionobj);
                return prototypeBean;
            }

        } else{
            /*
            * java手动抛出异常
            * - throw new Exception("worry message");
            * */
            throw new NullPointerException("这个BeanName不存在@Component注解||BeanName写错");
        }
//        return null;
    }
}

抛出来的异常经验(VS)的知识

Spring手写的框架!
 - 1.API
     1.1 config(配置类.class)
     1.2 getBean(Compontent(value()))
 - 2.Spring的注解
     2.1 Component(BeanName)
     2.2 ComponentScan(需要扫描的包名)
 - 3.解析配置类(注解)
     3.1 判断是否是scan,是的话取value,通过类加载器获取所有的class,判断是否是compontent注解,
 - 4.main 测试类

 问题01:
   解决01:
  isAnnotationPresent
     判断注释Scope是否在aClass(类对象上)
  getDeclaredAnnotation
     返回直接存在此元素上的所有注释
  目的:
     得到返回值value();

   Class<?> aClass = classLoader.loadClass(classNamesymbol);
  if(aClass.isAnnotationPresent(Scope.class)){
      Scope scopeComponent = (Scope) config.getDeclaredAnnotation(Scope.class);
  }

问题02得不到lxyservice类上的Component注解??
通过配置类获取扫描路径
ComponentScan classCompontScan = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
通过类对象获取对象上的Component注解
Component component = (Component) aClass.getDeclaredAnnotation(Component.class);

问题03lxyservice得值为null
  原因lxyService没有开启单例模式
  Lxyservice lxyservice = (Lxyservice) lxyApplicationContext.getBean("lxyservice");
问题03.1可是我写的Autowired是单例模式的所以怎么办??
  解决直接@scope(value = "singleton")就好了!!!
注意: 
     依赖注入的abc属性值 = null的原因是它没有设置为单例模式

视频链接地址

Spring手写教程

最后总结

还会有后文,这里只是单纯的扫描+解析

 

posted on 2022-06-30 13:18  lxy_keep  阅读(79)  评论(0)    收藏  举报

导航