手写Spring容器

这篇博客主要是记录一下如何简单的实现一个Spring容器

1.首先一个maven项目结构如下 主要有两个包spring这个包就是实现spring容器的代码

在这里插入图片描述

2.这里展示的是Test类的代码,我们通过自己定义的BaoboApplicationContext 类根据配置类AppConfig得到我们

UserService 对象

public class Test {
    
    public static void main(String[] args) throws Exception {
    	//BaoboApplicationContext 这个我们自己创建的容器类似于spring的
        BaoboApplicationContext baoboApplicationContext = new BaoboApplicationContext(AppConfig.class);
        UserService UserService = (UserService) baoboApplicationContext.getBean("UserService");


        UserService.test();

    }
}

3.AppConfig使我们自定的类,上面有我们自定的注解ComponentScan标志我们扫描的类的目录

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

4.自定义ComponentScan注解,用来告诉spring要扫描的包的路径

@Retention(RetentionPolicy.RUNTIME)//运行时有效
@Target(ElementType.TYPE)//类上
public @interface ComponentScan {
    String value() default "";
}

5.自定义Component注解,用来告诉spring这个注解标注的类交给spring管理

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

6.自定义Scope注解,告诉spring这类是原型还单例

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

7.接下来这个类BaoboApplicationContext 就是我们实现spring容器的重点,下面是这个类的结构:

首先是这个类的构造方法,构造方法通过传入的配置类AppConfig,然后调用scan方法来解析的这个类,可以参照下面的代码,主要的解析过程:
1、通过反射拿到配置类上的注解;
2、通过注解拿到注解的值也就是我们要扫描的包的路径;
3、通过类加载器来加载路径的类
4、加载完类后通过Cpmpomemt注解来确定哪些类是交给spring容器管理的
5. 通过Scope来判断哪些类是单例,哪些类是原型

在这个类里面还有几个比较重要的集合:
   //单例池,用来存放创建好的容器对象
    private ConcurrentHashMap<String, Object> single = new ConcurrentHashMap<>();
    //bean定义集合,用来存放交给spring管理的哪些对象的信息
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionConcurrentHashMap = new ConcurrentHashMap<>();
    //spring提供的类增强工具
    private ArrayList<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

解析完配置类,接下来就是创建Bean的过程主要调用的是createBean方法,同样可参照下面的代码,主要的创建过程:
1、通过bean的定义信息得到一个对象,接下来就是依赖注入,判断这个类的属性有没有Autowired注解,如果有根据属性的名字创建bean,然后将这个bean注入到对象中
2、判断这个对象是否实现BeanNameAware这个接口 aware回调,这个spring提供的回调方法
3、同理判断这个对象是否实现BeanPostProcessor,InitializingBean这些接口,它们都是spring在创建对象的过程中,提供给程序员可以对对象进行增强的方法

创建完容器之后,我们就可以通过getBean方法得到我们要的对象
这是一个简单的spring的容器的实现过程
在这里插入图片描述

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

public class BaoboApplicationContext {
    private Class configClass;
    //单例池
    private ConcurrentHashMap<String, Object> single = new ConcurrentHashMap<>();
    //bean定义集合
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionConcurrentHashMap = new ConcurrentHashMap<>();
    private ArrayList<BeanPostProcessor> beanPostProcessors = new ArrayList<>();


    public BaoboApplicationContext( Class configClass) throws Exception {

        this.configClass=configClass;
        //调用scan解析配置类,主要是解析配置类上的注解
        scan(configClass);
        //通过bean定义信息来创建帝乡 如果是单例先给它创建好
        Set<Map.Entry<String, BeanDefinition>> entries = beanDefinitionConcurrentHashMap.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entries) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = beanDefinitionConcurrentHashMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")){
                Object bean = createBean(beanName,beanDefinition);
                single.put(beanName,bean);
            }
        }

    }
    public Object createBean(String beanName,BeanDefinition beanDefinition) throws Exception {
        Class clazz = beanDefinition.getClazz();
        Object instance = clazz.getDeclaredConstructor().newInstance();
        //依赖注入
        Field[] declaredField = clazz.getDeclaredFields();
        for (Field field : declaredField) {
            //如果有自动注入的
            if (field.isAnnotationPresent(Autowired.class)){
                //得到属性的名字,根据名字创建bean,将bean注入
                String name = field.getName();
                Object bean = getBean(name);
                //
                field.setAccessible(true);
                field.set(instance,bean);
            }
        }
        //这个对象是否实现BeanNameAware这个接口 aware回调
        if (instance instanceof BeanNameAware){
            ((BeanNameAware) instance).setBeanName(beanName);
        }
        //初始化前
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            instance = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
        }
        //初始化
        if (instance instanceof InitializingBean){
            ((InitializingBean) instance).afterPropertiesSet();
        }
        //初始化后
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            instance = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
        }
        //BeanPostProcessor
        return instance;
    }

    private void scan(Class configClass) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        //得到扫描的路径
        String path = componentScan.value();
        System.out.println(path);
        //扫描
        //得到app类加载器
        ClassLoader classLoader = BaoboApplicationContext.class.getClassLoader();
        //通过注解进行扫描,得到该路径
        String path1 = path.replace(".", "//");
        URL resource = classLoader.getResource(path1);
        System.out.println(resource);
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File file1 : files) {
                String file1Name = file1.getAbsolutePath();
                //返回一个字符串,该字符串是该字符串的子字符串。 *子字符串以指定索引处的字符开头,并且*扩展到该字符串的末尾
                String className = file1Name.substring(file1Name.indexOf("com"),file1Name.indexOf(".class"));
                String replace = className.replace("\\", ".");
                System.out.println(replace);
                //根据路径,用类加载器加载类
                Class<?> aClass = classLoader.loadClass(replace);

                //如果这个类存在component注解,表示这个一个bean
                if (aClass.isAnnotationPresent(Component.class)) {
                    //判断这个类是不是实现BeanPostProcessor
                    if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                        BeanPostProcessor instance = (BeanPostProcessor) aClass.getConstructor().newInstance();
                        beanPostProcessors.add(instance);

                    }
                    //判断这个类是单例还是原型
                    //BeanDefinition
                    BeanDefinition beanDefinition = new BeanDefinition();
                    Component declaredAnnotation = aClass.getDeclaredAnnotation(Component.class);
                    beanDefinition.setClazz(aClass);
                    //拿到Component注解的值
                    String beanName = declaredAnnotation.value();
                    if (aClass.isAnnotationPresent(Scope.class)){
                        Scope declaredAnnotation1 = aClass.getDeclaredAnnotation(Scope.class);
                        String value2 = declaredAnnotation1.value();
                        beanDefinition.setScope(value2);
                    }else {
                        //默认是单例bbean
                        beanDefinition.setScope("singleton");

                    }
                    beanDefinitionConcurrentHashMap.put(beanName,beanDefinition);


                }

            }
        }
    }


    public Object getBean(String beanName) throws Exception {
        //判断这个bean是不是在容器里面
        if (beanDefinitionConcurrentHashMap.containsKey(beanName)){
            //判断是不是单例bean
            BeanDefinition beanDefinition = beanDefinitionConcurrentHashMap.get(beanName);
            if (single.containsKey(beanName)) {
                Object o = single.get(beanName);
                return o;
            }else {
                //不是单例,就新建一个bean
                Object bean = createBean(beanName,beanDefinition);
                return bean;
            }

        }else {
            //没有这个bean
            throw new NullPointerException();
        }

    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值