【手写一个spring】spring源码的简单实现--bean对象的创建

文章目录

前置知识

Spring框架中,BeanSpring容器管理的对象,它们由容器创建、连接、配置,并管理其整个生命周期

单例or多例

单例和多例是对 bean作用域的描述.

  • 单例bean:指的是在Spring IoC容器中,对于某个Bean的定义,无论被注入或请求多少次,Spring容器都只会返回同一个Bean实例。这意味着,对于同一个Bean定义,整个应用中只有一个共享的实例存在
  • 多例bean:指的是每次注入或请求时,Spring容器都会创建一个新的Bean实例。这意味着,对于同一个Bean定义,每次请求都会得到一个全新的实例。

这里我们采用注解 (scope)的方式去获取一个bean对象的作用域情况,其中:singleton代表单例,prototype代表多例.

这里我们先要自定义一个注解scope

复制代码
@Retention(RetentionPolicy.RUNTIME)//生效时间
@Target(ElementType.TYPE)   //写在类上面的注解
public @interface Scope {
    String value() default "";//单例or多例
}

beanDefinition的概念

BeanDefinitionSpring框架中的一个核心概念,它用于描述一个Bean实例的属性和行为,并提供了创建和管理Bean实例的基础信息 。这些信息包括Bean的类名、作用域、生命周期回调方法、依赖关系、属性值 等。

也就是说,通过beanDefinition,我们可以来描述一个bean对象,并且spring容器可以根据beanDefinition的描述来实例化一个bean对象.

beanDefinition的代码实现:后续我们还会进行补充.

复制代码
public class BeanDefinition {
    public Class type;
    public String scope;//单例多例

    public Class getType() {
        return type;
    }

    public void setType(Class type) {
        this.type = type;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }
}

简单实现一个bean对象的创建

a.扫描到bean对象,先生成beanDefinition对象

扫描到bean对象后,是直接实例化一个bean对象吗?

并不是,spring容器采用beanDefinition对象对bean进行统一描述,方便spring的管理

所以,我们先生成一个对bean对象的描述,再考虑实例化的问题 ,再将生成的beanDefinition存储在一个ConcurrentHashMap<String,BeanDefinition>维护的池中,方便后续的查找

复制代码
 //获取对象
 
 /***
 *			beanDefinition池
 **/
 
 private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap=new ConcurrentHashMap<>();
                        try {
                            Class<?> clazz = classLoader.loadClass(className);//传入全限定类名
                            if (clazz.isAnnotationPresent(Component.class)) {
                             	//建立BeanDefintion
                                BeanDefinition beanDefinition=new BeanDefinition();

                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    //是否存在scope,表示单例or多例
                                    Scope scopeAnnotation=clazz.getAnnotation(Scope.class);
                                    String scope=scopeAnnotation.value();
                                    beanDefinition.setScope(scope);
                                } else{
                                    beanDefinition.setScope("singleton"); //默认是单例
                                }
                                beanDefinition.setType(clazz);
                                beanDefinitionMap.put(beanName,beanDefinition);//放入到map中
                                
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }

beanDefinition对象中,记录当前的bean对象的几个属性:

  • 作用域:单例or多例
  • 类型:clazz

b.根据单例or多例,进行不同的实例化

单例:在容器启动过程中实例化,放入单例池中

如果该bean对象的作用域是单例 ,那么直接在容器启动的过程中进行实例化,并且存储在单例池中:

复制代码
public class BaiLuApplicationContext {

		/**
       * 单例池
       * */
      private ConcurrentHashMap<String,Object> singletonBeanMap=new ConcurrentHashMap<>();
		
		/**
         * 将单例bean创建出来放入单例池中
         * */
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals("singleton")){
                //说明是单例的
                 Object bean=creanBean(beanName,beanDefinition);
                 singletonBeanMap.put(beanName,bean);//放入到单例池中

            }
        }
  • 这里进行了单例bean的实例化,并且放入到单例池中,后续进行getBean的时候,如果判断出来是一个单例bean,那么就可以直接从单例池中取出bean对象,而无需再次实例化;
  • 多例的bean,则需要每次getBean的时候,都去进行实例化.

c.createBean()方法:实例化一个bean对象

i.调用该类的无参的构造方法

复制代码
private Object creanBean(String beanName, BeanDefinition beanDefinition){
		Class clazz=beanDefinition.getType();
		//调用无参的构造方法
 		Object instance = clazz.getConstructor().newInstance();
 		//....
 		}

ii.实现依赖注入

稍微了解spring的同学都知道,spring中存在依赖注入 的概念,也就是将一个被spring管理的对象可以接收到另一个被spring管理的对象.

假设,此时我们要实例化的对象UserService,存在需要依赖注入的属性OrderService:

复制代码
@Component
public class OrderService {
}

---------------------------------------------------
@Component("userService")
public class UserService{
    @Autowird
    private OrderService orderService;
}

我们在第一步中,调用了该类的无参的构造方法之后,userService中的成员属性orderService是否已经被注入呢?答案肯定是否定 的,那么我们该如何实现依赖注入这一行为呢?

答案:其实也很容易想到,让spring容器创建出orderService之后,再将这个orderService放到usrService中,就可以了.

复制代码
 		/**
         * 依赖注入@Autowird的实现
         * */

        //当前遍历clazz的所有属性
        for (Field f : clazz.getDeclaredFields()) {
            if (f.isAnnotationPresent(Autowird.class)) {
                //该属性被@Autowird标注
                f.setAccessible(true);//标注为true,才可以进行赋值
                //属性注入
                f.set(instance,getBean(f.getName()));  //先by type 再by name
            }
        }
  1. 判断出该类中有哪些成员属性是被@Autowird修饰的;
  2. 如果该成员属性是被@Autowird修饰的,那么就进行属性注入行为

getBean()的实现

复制代码
 /**
     *
     * 获取一个bean对象
     *
     *  */
    public Object getBean(String beanName) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition==null){
                  throw new NullPointerException();//抛出空指针
        }else {
            String scope = beanDefinition.getScope();
            if (scope.equals("singleton")) {
                //说明是单例:在容器启动的时候,直接生成一个单例bean,直接去单例池中取出bean即可
                Object bean = singletonBeanMap.get(beanName);
                if (bean == null) {
                    bean = creanBean(beanName, beanDefinition);
                    singletonBeanMap.put(beanName, bean);
                }
                return bean;

            } else {
                //多例:每次获取bean的时候,都去创建一个bean
                return creanBean(beanName,beanDefinition);
            }
        }
    }

spring容器中,获取一个bean对象

  • 先判断该bean对象是否在spring容器中注册 (判断是否能获取到beanDefinition)
  • 获取到beanDefinition对象后,判断是作用域是单例还是多例
    • 如果是单例,直接去单例池中获取;
    • 如果是多例,那么每次getBean()时,都去创建一个新的bean对象(调用createBean方法)

以上就是一个bean对象的简单创建,但是bean对象的创建过程远不止与此,还包括spring框架中常见的初始化机制 ,回调机制等,在后续的章节中我们会仔细讲解.

相关推荐
ChinaRainbowSea6 分钟前
补充:问题:CORS ,前后端访问跨域问题
java·spring boot·后端·spring
KiddoStone16 分钟前
多实例schedule job同步数据流的数据一致性设计和实现方案
java
岁忧37 分钟前
(LeetCode 每日一题) 1865. 找出和为指定值的下标对 (哈希表)
java·c++·算法·leetcode·go·散列表
YuTaoShao40 分钟前
【LeetCode 热题 100】240. 搜索二维矩阵 II——排除法
java·算法·leetcode
考虑考虑2 小时前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干2 小时前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying2 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·2 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
春生野草3 小时前
关于SpringMVC的整理
spring
Bug退退退1233 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq