1.spring 启动和扫描逻辑实现


文章目录

  • 1.spring 启动和扫描逻辑实现
  • 1.spring 启动和扫描逻辑实现
  • 基础配置类
  • ComponentScan
  • Component
  • scope
  • 包扫描逻辑
  • BeanDefinition
  • 单例池 singletonObjects
  • getBean
  • 参考


1.spring 启动和扫描逻辑实现

在spring 容器启动的过程中,会扫描指定包路径下的class 文件,判断当前类是否是一个bean 对象,如果是一个bean对象,将其注入到spring 容器中。

基础配置类

AppConfig 配置类的信息

@ComponentScan("com.zhouyu.service")
public class AppConfig {

}

ComponentScan

ComponentScan——主要定义包扫描路径——spring在容器启动的过程中,会通过读取配置类AppConfig类的class 对象获取 @ComponentScant定义的扫描路径

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

Component

表示这个类是一个bean

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

scope

定义Bean的作用域,是单例singleton还是原型 prototype

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

    String value();
}

包扫描逻辑

新建 ZhouyuApplicationContext.java,作为spring容器的模拟实现

启动类

public class Test {
    public static void main(String[] args) throws Exception {
        ZhouyuApplicationContext applicationContext = new ZhouyuApplicationContext(AppConfig.class);

        Object userUservice = applicationContext.getBean("userService");
        Object userUservice1 = applicationContext.getBean("userService");

        System.out.println(userUservice);
        System.out.println(userUservice1);
    }
}


ZhouyuApplicationContext

  • Class configClass 属性
  • 带Class 参数的构造方法

在spring 启动类上通过传入一个Config配置类,作为Spring启动类的配置信息

scan——方法完成了包扫描逻辑,通过传入的AppConfig类的class对象,我们可以读取到AppConfig类上的@ComponentScan注解,其中定义了包扫描路径

// 解析配置类
// ComponentScan注解 --> 扫描路径 --> 扫描
ComponentScan annotations = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = annotations.value();// 扫描路径 com.zhouyu.service

在Java中,需要加class加载到JVM中,不同的类是由不同的类加载器去完成加载的,Bootstrap类加载器加载jre/lib目录下的类,Ext类加载器加载jre/ext/lib目录下的类,App类加载器加载classpath目录下的类。其中classpath就是我们的项目路径。

private void scan(Class configClass) throws ClassNotFoundException {
        // 解析配置类
        // ComponentScan注解 --> 扫描路径 --> 扫描
        ComponentScan annotations = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = annotations.value();// 扫描路径 com.zhouyu.service

        //扫描
        // Bootstrap --> jre/lib
        // Ext ------> jre/ext/lib
        // App ------> classpath
        ClassLoader classLoader = ZhouyuApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource("com/zhouyu/service");
        File file = new File(resource.getFile());

        if(file.isDirectory()){
            File[] files = file.listFiles();
            for(File f: files){
                //D:\2022-04-22-program\zhouYu\spring01\target\classes\com\zhouyu\service\UserService.class -> com.zhouyu.service.UserService
                String fileName = f.getAbsolutePath();
                if(fileName.endsWith(".class")){
                    String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                    className = className.replace("\\",".");

                    Class<?> clazz = classLoader.loadClass(className);
                    if(clazz.isAnnotationPresent(Component.class)){
                        // 表示当前这个类是一个Bean
                        // 解析类,判断当前bean是单例bean 还是prototype原型bean
                        // BeanDefinition
                        Component componentAnnotaion = clazz.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotaion.value();

                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setClazz(clazz);
                        // 判断是不是单例模式
                        if(clazz.isAnnotationPresent(Scope.class)){
                            Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                            beanDefinition.setScope(scopeAnnotation.value());
                        }else {
                            beanDefinition.setScope("singleton");
                        }

                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                }

            }
        }
    }

BeanDefinition

对Bean 信息的定义

描述一个bean 的全部信息,比如 class 类型,bean 的作用域,是否懒加载

常用属性

  • beanClass:表示Bean类型,未加载类的时候存放Bean的名字,加载类后存放Bean的class信息。
  • scope:表示Bean的作用域,一般值为单例或者原型。
  • lazyInit:表示Bean是否是懒加载。
  • initMethodName:Bean初始化需要执行的方法。
  • destroyMethodName:Bean销毁时要执行的方法。
  • factoryBeanName:创建当前Bean的工厂。

BeanDefinition是如何存放的?

  • private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    当Bean注册完成后,会在spring容器中把扫描到的beanDefinition存放到beanDefinitionMap中,方便后续的使用。

单例池 singletonObjects

spring中定义了单例池存放单例bean,在spring扫描逻辑scan方法执行完之后,spring会将非懒加载的单例bean注入到spring容器中

// 单例池
private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();

getBean

单例直接从单例池获取

多列通过createBean方法创建bean

public Object getBean(String beanName) throws Exception{
        if(beanDefinitionMap.containsKey(beanName)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

            if(beanDefinition.getScope().equals("singleton")){
                Object o = singletonObjects.get(beanName);
                return o;
            } else {
                // 创建 bean
                Object bean = createBean(beanDefinition);
                return bean;
            }
        }else {
            throw new NullPointerException();
        }
    }