Spring源码简易手写实现(学习过程记录)(二)

2.1 创建Scope注解

 context.getBean("userService");

在创建bean对象的时候,会传入一个beanName,根据这个beanName,首先框架会去判断这个beanName是单例bean还是原型bean

在实体类中被componen注解的类,我们需要一个Scope注解,来表示这个类是单例还是原型,如果没有Scope注解默认是单例

@Component("userService")
@Scope("prototype")
public class UserService {
}

首先我们来创建一个Scope注解

package com.rainwood.spring;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value();
}

value并不是缺省的

那么如何通过简单得context.getBean("userService")

就可以知道这个beanName是单例还是原型呢

单例代表着,每次getBean取出来的是同一个bean对象,而原型每次取出来的都是新创建的bean对象

我们可以通过map (key value)存取的方式来存bean对象,这样getBean的时候,传进一个beanName,就可以通过map取出这个bean对象

//定义一个map,来存放单例的bean对象
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//单例池

可以创建一个单例池来存放单例bean对象

那么如何根据beanName来判断是单例还是原型

2.2 beanDefination对象

我们需要创建一个beanDefination对象来实现

package com.rainwood.spring;

public class BeanDefination {

    private Class clazz;
    private String scope;


    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }
}

这个对象存储了bean所对应的class类还有它的scope

在扫描包的时候,创建该beanDefination对象,然后将beanDefination对象存在map里

   private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();//存储所有扫描到的bean的定义

调用getBean时候:

 public Object getBean(String beanName) {

        if(beanDefinationMap.containsKey(beanName)) {
            BeanDefination beanDefination = beanDefinationMap.get(beanName);
            String scope = beanDefination.getScope();
            Class clazz = beanDefination.getClazz();
            System.out.println(clazz);
            if(scope == "singleton") {
                Object o = singletonObjects.get(beanName);
                return o;
            } else {
                Object bean = createBean(beanDefination);
                return bean;
            }
        } else {
            //不存在对应的Bean
            throw new NullPointerException();
        }
    }

先是根据beanName取到对应的class和scope,判断scope是单例还是原型

如果是单例,从单例池里拿到对应的bean对象,singletonObjects.get(beanName);

如果是原型,我们需要创建一个createBean方法,将beanDefination传进去进一步处理(下一节再学)

 public  Object createBean(BeanDefination beanDefination) {
        Class clazz = beanDefination.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

2.3 扫描并且创建单例池和beanDefinationMap

上一节中,我们已经通过扫描获取了Component注解的类

扫描:

 try {
                        //这个地方不是放的class文件的url,而是这个类的全限定名
                        Class<?> aClass = classLoader.loadClass(className);
                        //判断这个类是不是有Component注解
                        if(aClass.isAnnotationPresent(Component.class)) {
                            //表示当前这个类是一个Bean
                            //解析类,判断当前bean是单例bean还是prototype的bean 解析之后会生成一个BeanDefination对象
                            //BeanDefination

                            //拿到Component 对应的beanName
                            Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();

                            BeanDefination beanDefination = new BeanDefination();
                            beanDefination.setClazz(aClass);
                            //如果可以拿到Scope注解
                            //如果拿不到表示这个类默认是一个单例
                            if(aClass.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                                String scope = scopeAnnotation.value();
                                beanDefination.setScope(scope);

                            } else {
                                beanDefination.setScope("singleton");
                            }
                            beanDefinationMap.put(beanName, beanDefination);


                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

调用scan方法扫描后,已经将beanDefinationMap中放入了所有的beanDefination对象和beanName

还需要将bean单例依次放入单例池中:

for (Map.Entry<String, BeanDefination> entry : beanDefinationMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefination beanDefination = entry.getValue();

            if(beanDefination.getScope().equals("singleton")) {
                Object bean = createBean(beanDefination); // 创建单例bean对象
                singletonObjects.put(beanName, bean);
            }
        }

下面是limingApplicationContext.java:

package com.rainwood.spring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class limingApplicationContext {
    private  Class configClass;

    //定义一个map,来存放单例的bean对象
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//单例池

    private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();//存储所有扫描到的bean的定义


    public limingApplicationContext(Class configClass) throws ClassNotFoundException {
        this.configClass = configClass;
        //解析配置类
        //ComponentScan注解--》扫描路径 -->扫描-->Beandefination--->BeanDefinationMap
        scan(configClass);

        for (Map.Entry<String, BeanDefination> entry : beanDefinationMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefination beanDefination = entry.getValue();

            if(beanDefination.getScope().equals("singleton")) {
                Object bean = createBean(beanDefination); // 创建单例bean对象
                singletonObjects.put(beanName, bean);
            }
        }
    }

    public  Object createBean(BeanDefination beanDefination) {
        Class clazz = beanDefination.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();

            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 configClass) {
        //解析配置类
        //ComponentScan注解--》扫描路径
        ComponentScan componentScanAnnotation = (ComponentScan) 		           configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScanAnnotation.value(); //扫描路径
        path = path.replace(".","/");

        System.out.println(path);
        //扫描
        //类加载器:
        //Bootstrap -->jre/lib
        //Ext----->jre/ext/lib
        //App---->classpath
        ClassLoader classLoader = limingApplicationContext.class.getClassLoader(); //app 取得的是classpath对应的类加载器
        //resource对应的是一个目录 file:/D:/spring%e6%ba%90%e7%a0%81%e5%ad%a6%e4%b9%a0/target/classes/com/rainwood/liming/service
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());

        //判断是不是一个目录
        if(file.isDirectory()) {
            //可以获取到这个目录下所有的编译好的class文件路径
            File[] files = file.listFiles();
            for (File file1 : files) {
                System.out.println(file1);

                //D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class 转化成com.rainwood.liming.service.UserService
                String fileName = file1.getAbsolutePath();

                //判断是不是class文件再进一步处理
                if(fileName.endsWith(".class")) {
                    String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
                    className = className.replace("\\",".");

                    try {
                        //这个地方不是放的class文件的url,而是这个类的全限定名
                        Class<?> aClass = classLoader.loadClass(className);
                        //判断这个类是不是有Component注解
                        if(aClass.isAnnotationPresent(Component.class)) {
                            //表示当前这个类是一个Bean
                            //解析类,判断当前bean是单例bean还是prototype的bean 解析之后会生成一个BeanDefination对象
                            //BeanDefination

                            //拿到Component 对应的beanName
                            Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();

                            BeanDefination beanDefination = new BeanDefination();
                            beanDefination.setClazz(aClass);
                            //如果可以拿到Scope注解
                            //如果拿不到表示这个类默认是一个单例
                            if(aClass.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                                String scope = scopeAnnotation.value();
                                beanDefination.setScope(scope);

                            } else {
                                beanDefination.setScope("singleton");
                            }
                            beanDefinationMap.put(beanName, beanDefination);


                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

            }

        }
    }

    public Object getBean(String beanName) {

        if(beanDefinationMap.containsKey(beanName)) {
            BeanDefination beanDefination = beanDefinationMap.get(beanName);
            String scope = beanDefination.getScope();
            Class clazz = beanDefination.getClazz();
            System.out.println(clazz);
            if(scope == "singleton") {
                Object o = singletonObjects.get(beanName);
                return o;
            } else {
                Object bean = createBean(beanDefination);
                return bean;
            }
        } else {
            //不存在对应的Bean
            throw new NullPointerException();
        }
    }
}

test

package com.rainwood.liming;
import com.rainwood.spring.limingApplicationContext;
public class test {
    public static void main(String[] args) throws ClassNotFoundException {
        limingApplicationContext context = new limingApplicationContext(AppConfig.class);

        //如果是单例bean getBean调用多次获得的都是同一个bean对象 (可以通过map来实现, key就是beanName, 对应的value就是bean对象) 单例池
        //如果是原型bean 会返回不同的bean对象
//        Object bean = context.getBean("userService");
        System.out.println(context.getBean("userService"));
        System.out.println(context.getBean("userService"));
        System.out.println(context.getBean("userService"));
//        System.out.println(bean);
    }
}

打印出getBean,如果是原型的话运行结果:

可见哈希值不同

com/rainwood/liming/service
D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class
D:\springResourceLearn\target\classes\com\rainwood\liming\service\XxUtil.class
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@5e2de80c
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@1d44bcfa
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@266474c2

如果是单例则:

com/rainwood/liming/service
D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class
D:\springResourceLearn\target\classes\com\rainwood\liming\service\XxUtil.class
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93

哈希值相同

上一篇:(II)第九节:bean 的后置处理器


下一篇:高频面试题:一张图彻底搞懂Spring循环依赖