欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 八卦 > 【手写一个spring】spring源码的简单实现--bean对象的创建

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

2024/11/29 18:05:29 来源:https://blog.csdn.net/2301_78320637/article/details/143946043  浏览:    关键词:【手写一个spring】spring源码的简单实现--bean对象的创建

文章目录

  • 前置知识
    • 单例or多例
    • `beanDefinition`的概念
  • 简单实现一个`bean`对象的创建
    • a.扫描到`bean`对象,先生成`beanDefinition`对象
    • b.根据单例or多例,进行不同的实例化
      • 单例:在容器启动过程中实例化,放入单例池中
    • c.`createBean()`方法:实例化一个`bean`对象
      • i.调用该类的无参的构造方法
      • ii.实现依赖注入
    • `getBean()的实现`

前置知识

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)) {//建立BeanDefintionBeanDefinition 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{@Autowirdprivate 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的时候,都去创建一个beanreturn creanBean(beanName,beanDefinition);}}}

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

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

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com