【手写一个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框架中常见的初始化机制 ,回调机制等,在后续的章节中我们会仔细讲解.

相关推荐
s:1031 小时前
【框架】参考 Spring Security 安全框架设计出,轻量化高可扩展的身份认证与授权架构
java·开发语言
南山十一少4 小时前
Spring Security+JWT+Redis实现项目级前后端分离认证授权
java·spring·bootstrap
427724005 小时前
IDEA使用git不提示账号密码登录,而是输入token问题解决
java·git·intellij-idea
chengooooooo6 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
李长渊哦6 小时前
常用的 JVM 参数:配置与优化指南
java·jvm
计算机小白一个6 小时前
蓝桥杯 Java B 组之设计 LRU 缓存
java·算法·蓝桥杯
Tirzano6 小时前
springsecurity自定义认证
spring boot·spring
南宫生9 小时前
力扣每日一题【算法学习day.132】
java·学习·算法·leetcode
计算机毕设定制辅导-无忧学长9 小时前
Maven 基础环境搭建与配置(一)
java·maven
逸狼10 小时前
【JavaEE进阶】Spring MVC(3)
spring·java-ee·mvc