文章目录
- 前置知识
- 单例or多例
- `beanDefinition`的概念
- 简单实现一个`bean`对象的创建
- a.扫描到`bean`对象,先生成`beanDefinition`对象
- b.根据单例or多例,进行不同的实例化
- 单例:在容器启动过程中实例化,放入单例池中
- c.`createBean()`方法:实例化一个`bean`对象
- i.调用该类的无参的构造方法
- ii.实现依赖注入
- `getBean()的实现`
前置知识
在Spring
框架中,Bean
是Spring
容器管理的对象,它们由容器创建、连接、配置,并管理其整个生命周期。
单例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
的概念
BeanDefinition
是Spring
框架中的一个核心概念,它用于描述一个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}}
- 判断出该类中有哪些成员属性是被
@Autowird
修饰的; - 如果该成员属性是被
@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
框架中常见的初始化机制,回调机制等,在后续的章节中我们会仔细讲解.