Spring之手撕框架核心逻辑

Posted 愉悦滴帮主)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring之手撕框架核心逻辑相关的知识,希望对你有一定的参考价值。

Spring之手撕框架核心逻辑

前言:

相比大多数开发者都用过Spring,但是由于工作性质原因一直没有时间或经历去研究和整理Spring底层到底是怎么运作的,核心逻辑又是什么。下面小编通过手写一套简单的Spring框架底层逻辑,帮助大家更好的认识Spring,也为想要冲击大厂的小伙伴提供一些文章资料。

相关项目资源已经上传。下载地址:https://download.csdn.net/download/qq_45065241/19496977?spm=1001.2014.3001.5503

手写实现Spring的扫描逻辑

我们知道Spring在启动的时候,会去通过@ComponentScan注解提供的路径去扫描对应路径下带有bean标识的注解(例如:@bean、@Component、@Service等等),然后将带有这些注解的类实例化成bean对象,然后将这些bean对象交给Spring容器去管理。

下面我们来手动实现Spring的扫描逻辑。

 1. 我们在启动Spring的时候一般会通过ClassPathXmlApplicationContext 解析XML文件来启动Spring,或者使用AnnotationConfigApplicationContext可以实现基于Java的配置类加载Spring的应用上下文。使用AnnotationConfigApplicationContext的好处可以避免使用application.xml进行配置,而是通过注解的形式。相比XML配置,更加便捷。

  ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("config.xml");
      
  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfigure.class);

 2. 想要启动Spring我们需要手动创建一个AnnotationConfigApplicationContext类与一个配置类。代码如下:

创建TguoConfigApplicationContext 类来模拟AnnotationConfigApplicationContext类作为Spring的启动类。通过构造方法对属性进行赋值。


public class TguoConfigApplicationContext {

    private Class configClass;

    public TguoConfigApplicationContext(Class configClass) {
        this.configClass = configClass;
    }
}

自定义配置类ApplicationConfigure 。注意其中的@Configuration、@ComponentScan注解都需要手动实现。

@Configuration
@ComponentScan("com.tguo.demo.service")
public class ApplicationConfigure {
}

2.Spring在扫描的时候会通过@ComponentScan注解指定扫描路径,然后去扫描带有@Compoment注解的.Class文件。所以我们需要手动建立@ComponentScan,@Compoment注解。代码如下:

我们需要提供一个属性字段来帮助开发者指定扫描路径。

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

我们需要提供一个属性字段来帮助开发者指定类的beanName。

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

3.但是随着业务的不断扩展,有些bean我们需要的不是单例的,有些bean我们需要getBean的时候才会去创建。Spring为我们提供了两个类:一个是@Scope用来指定模式,一个是@Lazy用来作懒加载。我们自定义这两个注解,代码如下:

@Scope注解 

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

 @Lazy 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Lazy {
}

4.准备工作暂时完毕,这时我们实例化TguoConfigApplicationContext并运行我们的模拟启动类。这时TguoConfigApplicationContext会去解析我们提供的配置类ApplicationConfigure 。然后将解析出来的注解信息放入我们的TguoConfigApplicationContext类的Class字段中。步骤:1.解析@ComponentSacn注解得到路径。2.扫描路径下的所有.Class文件。3.将文件放入List集合。代码如下:

 /**
     * 扫描,得到class
     *
     * @param configClass
     * @return
     */
    private List<Class> scan(Class configClass) {
        //1.得到扫描路径
        //将扫描得到的所有.Class文件放入集合中
        List<Class> classList = new ArrayList<>();
        //得到@ComponentScan注解的值
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        String path = componentScanAnnotation.value();
        //2.去该路径下扫描对应的Class文件
        //将路径转换为目录路径形式
        path = path.replace(".", "/");
        //获取TguoConfigApplicationContext的类加载器,通过类加载器去获取对应路径目录的Class文件
        ClassLoader classLoader = TguoConfigApplicationContext.class.getClassLoader();
        URL url = classLoader.getResource(path);
        File file = new File(url.getFile());
        //判断该文件是否为一个文件夹
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                //我们得到了Class文件,然后进行Class文件的加载
                String absolutePath = f.getAbsolutePath();
                absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                absolutePath = absolutePath.replace("\\\\", ".");
                try {
                    Class<?> clazz = classLoader.loadClass(absolutePath);
                    classList.add(clazz);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return classList;
    }

以上我们的扫描逻辑就写好了。

手写实现beanDefination的创建

通过了解过bean的生命周期,我们知道在拿到.Class文件之后我们需要创建对应的beanDefiniation。代码如下:

不了解bean生命周期的小伙伴请参考:https://blog.csdn.net/qq_45065241/article/details/117597180 

简述过程:1.遍历所有的.Class文件。2.创建BeaDefinition 对象。 3.将BeaDefinition 对象放入Map中。

public class BeaDefinition {
    private Class beanClass;
    private String scope;
    private Boolean isLazy;

   ......省略set,get方法
}

public class TguoConfigApplicationContext {

    private Class configClass;
    //要来存放BeaDefinition
    private Map<String, BeaDefinition> beaDefinitionMap = new ConcurrentHashMap<>();

    public TguoConfigApplicationContext(Class configClass) {
        this.configClass = configClass;
        //扫描,得到class
        List<Class> ClassList = scan(configClass);
        //解析这些类----》beaDefinition---》beaDefinitionMap
        //过滤,在实例化bean之前要先判断是否有Component注解
        for (Class clazz : ClassList) {
            //如果Class含有Component注解
            if (clazz.isAnnotationPresent(Component.class)) {
               //生成BeaDefinition 对象
                BeaDefinition beaDefinition = new BeaDefinition();
                beaDefinition.setBeanClass(clazz);
                Component componentAnnotation = (Component) clazz.getAnnotation(Component.class);
                String beanName = componentAnnotation.value();
                //如果有Scope注解
                if (clazz.isAnnotationPresent(Scope.class)) {
                    Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
                    beaDefinition.setScope(scopeAnnotation.value());
                }
                //否则表示单例的
                else {
                    beaDefinition.setScope("singleton");
                }
                beaDefinitionMap.put(beanName, beaDefinition);

            }
        }
        //基于Class去创建单例bean,实例化单例bean
        instanceSingletonBean();
    }
}

手写实现Spring中的bean初始化的实现原理

根据bean的生命周期,我们知道在创建完所有的BeanDefination对象之后需要实例化。代码如下:

通过类加载器去实例化bean。实例化之后需要进行属性填充-->Aware回调---->初始化。


    /**
     * 实例化单例bean
     *
     * @param beanName
     * @param beaDefinition
     * @return
     */
    private Object doCreateBean(String beanName, BeaDefinition beaDefinition) {
        try {
            //1.实例化
            Class beanClass = beaDefinition.getBeanClass();
            Object bean = beanClass.getDeclaredConstructor().newInstance();
            //2.属性填充
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                //判断属性是否有Autowired注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    //属性赋值
                    Object o = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(bean, o);
                }
            }
            //3.Aware
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            //初始化之前BeanPostProcessor
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
            }
            //4.初始化
            if (bean instanceof InitializingBean) {
                ((InitializingBean) bean).afterPropertiesSet();
            }
            //初始化之后BeanPostProcessor
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
            }
            return bean;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }


手写实现@Autowired注解的实现逻辑

我们在用Spring开发中,创建一个类可能需要依赖其他的类,这时就需要@Autowired注解。例如:

@Component("userService")
public class UserService{

    @Autowired
    private User user;

    private String beanName;

}

在上面代码中,我们的UserService需要依赖User类。也就是通过@Autowired注解从Spring容器中获取到User对应的bean对象,赋值给UserService类中的User属性。所以我们需要创建@Autowired注解。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

我们需要在实例化bean的过程中,也就是对原始对象填充属性的时候判断这个bean的字段中是否包含@Autowired注解。如果存在调用getBean的方法。 

 //2.属性填充
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                //判断属性是否有Autowired注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    //属性赋值
                    Object o = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(bean, o);
                }
            }

getBean的方法如下:

逻辑:判断这个bean是单例的还是原型的,如果是单例的则取单例池中获取,获取不到则创建bean,如果是原型的则直接创建一个bean并返回。

 /**
     * 获取bean实例对象
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        BeaDefinition beaDefinition = beaDefinitionMap.get(beanName);
        //如果beaDefinition是原型的则创建bean
        if (beaDefinition.getScope().equals("propotype")) {
            //创建bean
            Object bean = doCreateBean(beanName, beaDefinition);
            return bean;
        }
        //如果beaDefinition是单例的则去单例池中获取bean
        else if (beaDefinition.getScope().equals("singleton")) {
            Object bean = singletonObjects.get(beanName);
            if (bean == null) {
                Object bean1 = doCreateBean(beanName, beaDefinition);
                return bean1;
            }
            return bean;
        }
        return null;
    }
此上就完成了Spring中bean的创建并放入Spring容器中。

全部核心代码如下: 

package com.tguo.spring;

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.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TguoConfigApplicationContext {
    //配置类
    private Class configClass;
    //要来存放BeaDefinition
    private Map<String, BeaDefinition> beaDefinitionMap = new ConcurrentHashMap<>();
    //单例池
    private Map<String, Object> singletonObjects = new ConcurrentHashMap();
    //用来存放BeanPostProcessor
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
    
    //构造方法,实例化的时候执行
    public TguoConfigApplicationContext(Class configClass) {
        this.configClass = configClass;
        //扫描,得到class
        List<Class> ClassList = scan(configClass);
        //解析这些类----》beaDefinition---》beaDefinitionMap
        //过滤,在实例化bean之前要先判断是否有Component注解
        for (Class clazz : ClassList) {
            //如果Class含有Component注解
            if (clazz.isAnnotationPresent(Component.class)) {
                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                    try {
                        BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                        beanPostProcessorList.add(instance);
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }


                BeaDefinition beaDefinition = new BeaDefinition();
                beaDefinition.setBeanClass(clazz);
                Component componentAnnotation = (Component) clazz.getAnnotation(Component.class);
                String beanName = componentAnnotation.value();
                //如果有Scope注解
                if (clazz.isAnnotationPresent(Scope.class)) {
                    Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
                    beaDefinition.setScope(scopeAnnotation.value());
                }
                //否则表示单例的
                else {
                    beaDefinition.setScope("singleton");
                }
                beaDefinitionMap.put(beanName, beaDefinition);

            }
        }
        //基于Class去创建单例bean,实例化单例bean
        instanceSingletonBean();
    }


    /**
     * 扫描,得到class
     *
     * @param configClass
     * @return
     */
    private List<Class> scan(Class configClass) {
        //1.得到扫描路径
        List<Class> classList = new ArrayList<>();
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        String path = componentScanAnnotation.value();
        //2.去该路径下扫描对应的Class文件
        //将路径转换为目录路径形式
        path = path.replace(".", "/");
        //获取TguoConfigApplicationContext的类加载器,通过类加载器去获取对应路径目录的Class文件
        ClassLoader classLoader = TguoConfigApplicationContext.class.getClassLoader();
        URL url = classLoader.getResource(path);
        File file = new File(url.getFile());
        //判断该文件是否为一个文件夹
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                //我们得到了Class文件,然后进行Class文件的加载
                String absolutePath = f.getAbsolutePath();
                absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                absolutePath = absolutePath.replace("\\\\", ".");
                try {
                    Class<?> clazz = classLoader.loadClass(absolutePath);
                    classList.add(clazz);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return classList;
    }


    /**
     * 获取bean实例对象
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        BeaDefinition beaDefinition = beaDefinitionMap.get(beanName);
        //如果beaDefinition是原型的则创建bean
        if (beaDefinition.getScope().equals("propotype")) {
            //创建bean
            Object bean = doCreateBean(beanName, beaDefinition);
            return bean;
        }
        //如果beaDefinition是单例的则去单例池中获取bean
        else if (beaDefinition.getScope().equals("singleton")) {
            Object bean = singletonObjects.get(beanName);
            if (bean == null) {
                Object bean1 = doCreateBean(beanName, beaDefinition);
                return bean1;
            }
            return bean;
        }
        return null;
    }

    /**
     * 校验并实例化单例bean
     */
    public void instanceSingletonBean() {
        for (String beanName : beaDefinitionMap.keySet()) {
            BeaDefinition beaDefinition = beaDefinitionMap.get(beanName);
            if (beaDefinition.getScope().equals("singleton")) {
                //校验单例池中是否已经存在bean
                if (!singletonObjects.containsKey(beanName)) {
                    //创建bean
                    Object bean = doCreateBean(beanName, beaDefinition);
                    //将bean放入单例池中
                    singletonObjects.put(beanName, bean);
                }
            }
        }
    }

    /**
     * 实例化单例bean
     *
     * @param beanName
     * @param beaDefinition
     * @return
     */
    private Object doCreateBean(String beanName, BeaDefinition beaDefinition) {
        try {
            //1.实例化
            Class beanClass = beaDefinition.getBeanClass();
            Object bean = beanClass.getDeclaredConstructor().newInstance();
            //2.属性填充
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                //判断属性是否有Autowired注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    //属性赋值
                    Object o = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(bean, o);
                }
            }
            //3.Aware
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            //初始化之前BeanPostProcessor
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
            }
            //4.初始化
            if (bean instanceof InitializingBean) {
                ((InitializingBean) bean).afterPropertiesSet();
            }
            //初始化之后BeanPostProcessor
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
            }
            return bean;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
}

简述BeanNameAware接口。

BeanNameAware接口是提供给开发者在bean创建过程,也就是执行生命周期的过程中调用的接口。例如:

@Component("userService")
public class UserService implements BeanNameAware {

    @Autowired
    private User user;

    private String beanName;

    public void test() {
        System.out.println(beanName);
    }

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

}

我们需要在UserService创建实例化之后完成对UserService的属性beanName赋值。这时我们就可以实现BeanNameAware 接口。该接口的作用就是在bean实例化之后去回调,从而再次对bean对象进行属性赋值。

public Object doCreateBean(String beanName, BeaDefinition beaDefinition){  
           .....此处省略   
            //3.Aware
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
           .....此处省略   
return bean;
}

简述InitializingBean接口

InitializingBean接口与BeanNameAware类似,接口是提供给开发者在bean创建过程,也就是执行生命周期的过程中调用的接口。例如:

@Component("userService")
public class UserService implements BeanNameAware , InitializingBean {

    @Autowired
    private User user;

    private String userName;

    public void test() {
        System.out.println(userName);
    }

    @Override
    public void afterPropertiesSet() {
        this.userName = "xxxx";
    }
}


手写实现BeanPostprocessor的实现原理

BeanPostProcessor 接口中有两个默认方法,一个是postProcessBeforeInitialization该方法在初始化bean之前调用。一个是postProcessAfterInitialization该方法在初始化bean之后调用。

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean,String beanName);

    Object postProcessAfterInitialization(Object bean,String beanName);
}

范例: 

@Component
public class TguoBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("初始化之前");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("初始化之后");
        return bean;
    }
}
public Object doCreateBean(String beanName, BeaDefinition beaDefinition){   
         .....此处省略
 //初始化之前BeanPostProcessor
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
            }
            //4.初始化
            if (bean instanceof InitializingBean) {
                ((InitializingBean) bean).afterPropertiesSet();
            }
            //初始化之后BeanPostProcessor
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
            }
     .....此处省略
}

 

以上是关于Spring之手撕框架核心逻辑的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript之手撕callapply

JavaScript之手撕callapply

#yyds干货盘点# JavaScript之手撕callapply

JavaScript之手撕常用数组高阶函数

JavaScript之手撕new

JavaScript之手撕new