文章目录
前置知识
在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)) {
//建立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
}
}
- 判断出该类中有哪些成员属性是被
@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的时候,都去创建一个bean
return creanBean(beanName,beanDefinition);
}
}
}
从
spring
容器中,获取一个bean
对象
- 先判断该
bean
对象是否在spring
容器中注册 (判断是否能获取到beanDefinition
)- 获取到
beanDefinition
对象后,判断是作用域是单例还是多例
- 如果是单例,直接去单例池中获取;
- 如果是多例,那么每次
getBean()
时,都去创建一个新的bean
对象(调用createBean
方法)
以上就是一个bean
对象的简单创建,但是bean
对象的创建过程远不止与此,还包括spring
框架中常见的初始化机制 ,回调机制等,在后续的章节中我们会仔细讲解.