Bootstrap

源码系列(Spring/SpringBoot/HashMap)

SpringBoot源码

效果:加入springBoot注解后,在run方法中传入application.class就能实现自动启动并匹配到对象的controller返回结果。

  1. 导入Spring依赖

  2. 定义注解(在自己的应用Apllication加入该注解,作为SpringBoot项目启动),注解上加入@ComponentScan。当应用添加后,相当于添加@ComponentScan注解在自己的类上,就会默认扫描自己包下的所有文件,就可以扫描到controller文件夹下的文件。

  3. 自定义Run方法,并在其中启动tomcat

  4. 添加DispatchServlet(SpringMVC),需要接受一个spring容器,因为DispathServlet是在该容器中找对应的Controller.

  5. 添加Spring容器,需要传入一个配置类

Spring源码

基础准备

使用Spring框架时,用到的创建语句

public class MiaoboApplicationContext {
   
    Class configClass;
    public MiaoboApplicationContext(Class configClass){
   
        this.configClass = configClass;
    }

    public Object getBean(){
   
        return null;
    }
}

定义ApplicationContext类,定义需要填入的配置文件属性以及getBean方法

public class MiaoboApplicationContext {
   
    Class configClass;
    public MiaoboApplicationContext(Class configClass){
   
        this.configClass = configClass;
    }

    public Object getBean(){
   
        return null;
    }
}

用户的配置类添加@ComponentScan,自定义@ComponentScan,定义属性指定扫描路径

@ComponentScan("com.miaobo.service")
public class AppConfig {
   
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
   
    String value(); //传入扫描路径的值
}

Service类添加@Component注解表示定义的Bean,自定义@Component

@Component("userService")
public class UserService {
   
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
   
    String value(); //传入BeanName
}

基础完成后,Application需要在初始化后解析配置类,通过@ComponentScan扫描带有@Component

扫描

获取扫描路径,并通过类加载器得到所有该路径下的文件

Class configClass;
    public MiaoboApplicationContext(Class configClass) throws ClassNotFoundException {
   
        this.configClass = configClass;
        //配置类注解获取扫描路径
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScanAnnotation.value();
        path = path.replace(".","/");
        //获取应用类加载器
        ClassLoader configClassLoader = MiaoboApplicationContext.class.getClassLoader();
        //加载扫描路径,获取目录。特别注意所有目录不允许空格和中文
        URL resource = configClassLoader.getResource(path);//类加载器位于的目录为\Spring-miaobo\target\classes"
        File directory = new File(resource.getFile());
        //遍历文件,查看是否带有@Component注解
        if(directory.isDirectory()){
   
            File[] files = directory.listFiles();
            for(File file: files){
   
                System.out.println(file);//获取到扫描路径下的文件地址
            }
        }

    }

在获取到的文件中寻找带有@Component注解的类。即先根据包名得到所有的类,再判断是否有@Component注解。

//遍历文件,查看是否带有@Component注解
if(directory.isDirectory()){
   
    File[] files = directory.listFiles();
    for(File file: files){
   
        //绝对路径转为类加载器使用的路径格式
        String fileName = file.getAbsolutePath();
        if (fileName.endsWith(".class")) {
   
            fileName = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
            fileName = fileName.replace("\\", ".");
            //使用类加载器加载类
            Class<?> clazz = configClassLoader.loadClass(fileName);
            //判断是否有@Component注解
            if (clazz.isAnnotationPresent(Component.class)) {
   
                System.out.println("yes");
            }
        }
    }
}

实例化Bean(单例/原型)

getBean方法中需要通过传入的BeanName去寻找类的信息(困难),因此在ApplicationContex初始化时就创建每个Bean的BeanDefinition对象(包含类信息、是否单例等)。

HashMap singlePool = new HashMap<String, Object>();//单例池
ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();//放入BeanDefinition

发现@Component后,需要判断是否为原型Bean或单例Bean。添加@Scope(“Prototype”)表示原型Bean。单例就直接创建Bean放入单例池。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
   
    String value();
}
//判断是否有@Component注解
if (clazz.isAnnotationPresent(Component.class)) {
   
//获取BeanName
    Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
    String beanName = componentAnnotation.value();
    BeanDefinition beanDefinition = new BeanDefinition();
    beanDefinition.setClazz(clazz);
    //判断是否为单例是则创建并返回单例池
    Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
    if (scopeAnnotation.value().equals("prototype")) {
   
        beanDefinition.setScope("prototype");
    }
    else {
   
        beanDefinition.setScope("singleton");
    }
    beanDefinitionMap.put(beanName, beanDefinition);
;