原创

模拟Spring创建Bean流程

温馨提示:
本文最后更新于 2023年09月06日,已超过 504 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

流程说明

main方法中,我们模拟使用Spring获取我们定义的bean流程;

定义Spring容器:

首先,定义一个Spring容器,命名为ZhuziApplicationContext,并定义我们的构造方法(参数:配置文件或配置类);

定义配置类:

这里我们采用配置类方式实现;所以我们需要定义一个配置类:SpringConfig;

定义注解:

  • 定义@ComponentScan来标识Spring的扫描范围(路径);
  • 定义@Component来标识当前Bean需要交给Spring管理;
  • 定义@Scope来标识当前Bean是单例还是多例;

定义Bean

定义一个Bean交给Spring来管理,如:UserService;

代码

Main

public class Main {
    public static void main(String[] args) {
        ZhuziApplicationContext zhuziApplicationContext = new ZhuziApplicationContext(SpringConfig.class);

        UserService userService = (UserService) zhuziApplicationContext.getBean("userService");
    }
}

自定义注解

@ComponentScan

//注解位置
@Target(ElementType.TYPE)
//生效时间
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String[] value() default {};
}

@Component

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

@Scope

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

定义配置类

import com.zhuzi.spring.ComponentScan;

@ComponentScan(value = "com.zhuzi.service")
public class SpringConfig {
}

定义Spring容器

1.读取配置信息;
2.判断是否交给Spring加载;
3.如果交给Spring,则扫描配置路径(编译文件)下所有的类;
4.判断当前类是否交给Spring管理
5.如果是,保存所有Bean定义信息;
6.循环所有Bean定义,实例化单例Bean;
7.多例Bean每次获取时实例化;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public class ZhuziApplicationContext {

    private Class clazz;
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();
    private static final String SINGLETON = "singleton";

    public ZhuziApplicationContext(Class clazz) {
        this.clazz = clazz;
        // Spring容器启动要做什么?
        // 扫描
        if (clazz.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = (ComponentScan) clazz.getAnnotation(ComponentScan.class);
            String[] paths = componentScan.value();
            for (String path : paths) {
                path = path.replace(".", "/");

                // 获取类加载器
                ClassLoader classLoader = clazz.getClassLoader();
                URL resource = classLoader.getResource(path);

                assert resource != null;
                // 获取File
                File file = new File(resource.getFile());
                // 判断是否是文件夹
                if (file.isDirectory()) {
                    File[] files = file.listFiles();
                    assert files != null;
                    for (File file1 : files) {
                        // 获取所有的文件
                        String absolutePath = file1.getAbsolutePath();
                        if (absolutePath.endsWith(".class")) {
                            String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                            className = className.replace("\\", ".");
                            Class<?> aClass;
                            try {
                                aClass = classLoader.loadClass(className);
                                if (aClass.isAnnotationPresent(Component.class)) {
                                    BeanDefinition beanDefinition = new BeanDefinition();
                                    beanDefinition.setClazz(aClass);
                                    if (aClass.isAnnotationPresent(Scope.class)) {
                                        Scope annotation = aClass.getAnnotation(Scope.class);
                                        beanDefinition.setScope(annotation.value());
                                    } else {
                                        beanDefinition.setScope(SINGLETON);
                                    }

                                    Component component = aClass.getAnnotation(Component.class);
                                    String beanName = component.value();
                                    if (Objects.equals("", beanName)) {
                                        beanName = Introspector.decapitalize(aClass.getSimpleName());
                                    }
                                    beanDefinitionMap.put(beanName, beanDefinition);
                                }
                            } catch (ClassNotFoundException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                }
            }
        }

        // 实例化单例Bean
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (Objects.equals(SINGLETON, beanDefinition.getScope())) {
                this.getSingletonBean(beanName);
            }
        }
    }

    private Object createBean(BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getClazz();
        try {
            return clazz.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (Objects.isNull(beanDefinition)) {
            throw new NullPointerException();
        }
        if (Objects.equals(SINGLETON, beanDefinition.getScope())) {
            return this.getSingletonBean(beanName);
        }
        return this.createBean(beanDefinition);
    }

    /**
     * 获取单例Bean
     * @param beanName
     * @return
     */
    private Object getSingletonBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (Objects.equals(SINGLETON, beanDefinition.getScope())) {
            Object bean = singletonMap.get(beanName);
            if (Objects.isNull(bean)) {
                bean = this.createBean(beanDefinition);
                singletonMap.put(beanName, bean);
            }
            return bean;
        }
        throw new RuntimeException();
    }
}
正文到此结束
本文目录