Spring注解这篇文章中讲到了Spring的组件,组件加载到Spring容器中也就是Spring容器中的Bean对象,想要更深理解Spring中的Bean对象,那对这个BeanDefinition一定要有深入的了解,它是构造出来Bean对象的一切基础,比如Bean的作用域,Bean的注入模型,Bean是否进行加载等等信息,都需要一个BeanDefinition类来定义描述这些Bean的信息。如下Spring对BeanDefinition的描述:

简单翻译就是BeanDefinition描述了Bean的实例,该实例具有的属性值,构造函数参数值。有兴趣这个类的可以查看Spring源码,列举部分方法,方法太多如果想看全部,请直接看Spring源码

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* Scope identifier for the standard singleton scope: "singleton".
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* Scope identifier for the standard prototype scope: "prototype".
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
/**
* Specify the bean class name of this bean definition.
*/
void setBeanClassName(@Nullable String beanClassName);
/**
* Return the current bean class name of
* this bean definition.
*/
@Nullable
String getBeanClassName();
/**
* Override the target scope of this bean, specifying
* a new scope name.E
*/
void setScope(@Nullable String scope);
/**
* Return the name of the current target scope for this bean,
* or {@code null} if not known yet.
*/
@Nullable
String getScope();
/**
* Set whether this bean should be lazily initialized.
*/
void setLazyInit(boolean lazyInit);
/**
* Return whether this bean should be lazily initialized,
* i.e. not eagerly instantiated on startup.
* Only applicable to a singleton bean.
*/
boolean isLazyInit();

如果还不是很理解,那我们可以做一个对比。Java中的Class大家都很熟悉。Class可以用来描述一个类的属性和方法等等其他信息,而BeanDefintion可以描述Bean当中的scope,lazy,以及属性和方法等等其他信息

对上图的文字说明一下:假设磁盘上有1个.java文件,首先我们把这些java文件编译成class文件,继而java虚拟机启动会把这些class文件加载到内存,当遇到new关键字的时候会根据类的模板信息实例化这个对象也就是在堆上面分配内存。

但是Spring的Bean实例化过程和一个普通java对象的实例化过程还是有区别的,同样用一幅图来说明一下

先解释一下对Bean重定义能做些什么事情,比如你想吃面包但是兜里的钱只能买一个馒头,但是经过对馒头一些重新定义,你可以把馒头变成面包,面包吃着是比馒头香【PS为了能够理解只是做一个通俗的比方,年轻人还是要好好挣钱,争取买的起面包吃,后面会举一个列子】。然后对每个步骤进行解释如下。

当Spring容器启动的时候会去调用ConfigurationClassPostProcessor这个Bean工厂的后置处理器完成扫描,其实所谓的Spring扫描就是把类的信息读取到,比如类的类型(class),比如类的名字,类的构造方法。可能大家会有疑问这些信息不需要存,直接存在class对象里面不就可以?比如当Spring扫描到Student的时候Class clazz = Student.class;那么这个class里面就已经具备的前面说的那些信息了,确实如此。但是Spring实例化一个Bean不仅仅只需要这些信息,还有我上文说到的scope,lazy等等信息需要存储,所以Spring设计了一个BeanDefintion的类用来存储这些信息。故而当Spring读取到类的信息之后会实例化一个BeanDefinition的对象,继而调用这个对象的各种set方法存储信息;每扫描到一个符合规则的类,Spring都会实例化一个BeanDefinition对象,然后把根据类的类名生成一个Bean的名字(比如一个类UserService,Spring会根据类名UserService生成一个Bean的名字userService,Spring内部有一套默认的名字生成规则,但是程序员可以重写覆盖这个规则,然后Spring会把这个beanDefinition对象和生成的beanName放到一个map当中,key=beanName,value=beanDefinition对象。

当Spring把所有对应类的beanDefintion对象存到map之后,Spring会调用bean工厂后置处理器BeanFactoryPostProcessor【PS,Spring内部很多这个类的实现类,我们程序员也可以自己实现这个类】,这个是很重要类,如下图在Spring给它的定义:

 

主要意思就是:在应用程序上下文的标准初始化之后修改它的内部Bean工厂。

可以看到BeanFactoryPostProcessor里唯一的方法postProcessBeanFactory中唯一的参数就是一个标准的beanFactory对象——ConfigurableListableBeanFactory,Spring在调用postProcessBeanFactory方法的时候把已经实例化好的beanFactory对象传过来了,那么我们可以对这个beanFactory肆意妄为了。我们上文说的馒头变面包也就是在这一个步骤执行的。下面我们就写一个代码把馒头变面包。

1:先建3个类,为啥是三个因为商家善良买馒头的时候送你一个烧饼
package io.renren.service;
import org.springframework.stereotype.Service;
//馒头加入Spring容器了
@Service
public class ManTou {
}
package io.renren.service;
//烧饼没加入spring容器
public class ShaoBing {
} package io.renren.service;
//面包也没加入Spring容器
public class MianBao {
} //2:馒头变成面包代码如下:
package io.renren.service;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.stereotype.Component;
@Component
public class MyBeanFactoryPostPorcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//转换为子类,因为父类没有添加beanDefintion对象的api
DefaultListableBeanFactory defaultbf = (DefaultListableBeanFactory) beanFactory;
//new一个beanDefinition对象,这是隐含的送了一个烧饼
GenericBeanDefinition shaoBing= new GenericBeanDefinition();
shaoBing.setBeanClass(ShaoBing.class);
//添加一个beanDefinition对象,原本这个Y没有被spring扫描到
defaultbf.registerBeanDefinition("shaoBing", shaoBing);
// **********************上面是其他作用,买馒头的时候商家善良给你一个烧饼***************** // **************下面是馒头变面包*******************
//得到一个已经被扫描出来的beanDefintion对象x
//因为馒头本来就被扫描出来了,所以是直接从map中获取
BeanDefinition manTou = defaultbf.getBeanDefinition("manTou");
//修改这个馒头的c对象的class为面包
//原本这个mantou代表的class为manTou.class;现在为mianMao.class
manTou.setBeanClassName("io.renren.service.MianBao");
}
}

上面的代码主要有2个用途

1:买馒头送了一个烧饼【PS想送烧饼只要加入一个烧饼的BeanDefinition对象就可以了,这也说明了BeanDefinition的重要性】。

2:把馒头换成了面包也就是偷梁换柱改变馒头的BeanDefinition就可以了。

然后运行如下图,我们明明放入Spring容器中的是馒头,结果馒头没有获取到,得到了烧饼和面包。

如果说上面感觉用途不太大。上面的用途很广,大名鼎鼎的spring-mybatis就用了上面的原理了,mybatis利用了spring的BeanDefinitionRegistryPostProcessor这个扩展点,去把我们程序员提供的mapper接口,放入到了beanDefinitionMap中。

深入理解Spring的Bean定义对象BeanDefinition-面试重点的更多相关文章

  1. 通过BeanPostProcessor理解Spring中Bean的生命周期

    通过BeanPostProcessor理解Spring中Bean的生命周期及AOP原理 Spring源码解析(十一)Spring扩展接口InstantiationAwareBeanPostProces ...

  2. 深入理解Spring系列之二:BeanDefinition解析

    转载 https://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483814&idx=1&sn=ddf49931d55 ...

  3. Spring中 bean定义的parent属性机制的实现分析

    在XML中配置bean元素的时候,我们常常要用到parent属性,这个用起来很方便就可以让一个bean获得parent的所有属性 在spring中,这种机制是如何实现的?     对于这种情况 tra ...

  4. 理解Spring的Bean工厂

    一提到工厂,我们先来回顾前面学习过的工厂方法和抽象工厂模式: 工厂方法:针对产品维度,能够产生新的产品,也能够产生新的产品工厂,既能够扩展产品维度.可是假设我们想在普通工厂上生产产品系列,就会特别麻烦 ...

  5. Spring的Bean定义

    以下内容引用自http://wiki.jikexueyuan.com/project/spring/bean-definition.html: Bean定义 被称作bean的对象是构成应用程序的支柱也 ...

  6. 品Spring:bean定义上梁山

    认真阅读,收获满满,向智慧又迈进一步... 技术不枯燥,先来点闲聊 先说点好事高兴一下.前段时间看新闻说,我国正式的空间站建设已在进行当中.下半年,长征五号B运载火箭将在海南文昌航天发射场择机将空间站 ...

  7. 深入理解Spring中bean的生命周期

    [Spring中bean的生命周期] bean的生命周期 1.以ApplocationContext上下文单例模式装配bean为例,深入探讨bean的生命周期: (1).生命周期图: (2).具体事例 ...

  8. Spring XML Bean 定义的加载和注册

    前言 本篇文章主要介绍 Spring IoC 容器怎么加载 bean 的定义元信息. 下图是一个大致的流程图: 第一次画图,画的有点烂.

  9. 品Spring:负责bean定义注册的两个“排头兵”

    别看Spring现在玩的这么花,其实它的“筹码”就两个,“容器”和“bean定义”. 只有先把bean定义注册到容器里,后续的一切可能才有可能成为可能. 所以在进阶的路上如果要想走的顺畅些,彻底搞清楚 ...

  10. Spring技术内幕_IOC容器载入Bean定义资源文件

    转自:http://blog.csdn.net/chjttony/article/details/6259723 1.当spring的IoC容器将Bean定义的资源文件封装为Spring的Resour ...

随机推荐

  1. 【ubuntu】解决无法打开终端:gnome-terminal找不到路径问题

    因为之前安装pyton的时候把电脑本身的python路径给改了,所以出现了这样的问题:图形化启动系统自带终端时,报错找不到gnome-terminal的路径 后来找到解决方法: 先桌面右键-终端(E) ...

  2. python基础(数据库、可视化软件Navicat、python操作MySQL)

    多表查询的两种方法 数据准备: 建表 create table dep( id int primary key auto_increment, name varchar(20) ); create t ...

  3. 推荐8个提高工作效率的IntelliJ插件

    前言 欢迎关注微信公众号「JAVA旭阳」交流和学习 IntelliJ目前已经成为市面上最受欢迎的Java开发工具,这得益于里面非常丰富的插件机制.本文我将分享在日常开发中我经常使用的5个插件,它们可以 ...

  4. 宝塔渗透之msf代理入侵

    前言 在渗透中遇到内网主机是一层接一层的拓扑形式,可以采用多层代理加路由转发访问,便于在渗透中出现网段隔绝可以使用此方法跳出局限 实验环境 kali: 192.168.75.131 target-ce ...

  5. 7、解决swagger测试接口报错:TypeError: Failed to execute ‘fetch‘ on ‘Window‘: Request with GET/HEAD method cannot have body

    一.Swagger报错: 1.报错类型: TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method ...

  6. Windows 平台计算 CPU 总利用率

    利用 GetSystemTimes 可以获得 Windows 系统的 Idle Time. Kernel Time 和 User Time.Idle Time 是系统空闲的时间,也就是系统没有利用的时 ...

  7. Redis缓存何以一枝独秀?——从百变应用场景与热门面试题中感受下Redis的核心特性与使用注意点

    大家好,又见面了. 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面.如果感兴趣,欢迎关注以获取后续更新. 作为<深入理解缓存原理与实战设计 ...

  8. MySQL优化三,SQL语法

    ## 1.3.MySQL调优 前言:在前面的基础之上把相应的数据库表设计得很完美,建立了好用的索引,如果SQL语句中没有使用到相应索引的话,也是白搭,如何设计好一点的SQL,则是一大问题 ### 1. ...

  9. [cocos2d-x]关于坐标系

    本文从cocos2dx官网看到,搬运过来学习一下. cocos2d-x3.X的坐标系 Cocos2d-x坐标系和OpenGL坐标系相同,都是起源于笛卡尔坐标系. 笛卡尔坐标系中定义右手系原点在左下角, ...

  10. Java学习笔记:2022年1月6日(补充)

    Java学习笔记:2022年1月6日(补充) ​ 摘要:这篇笔记主要记录了2022年1月6日下午的笔记,主要内容为Java语言中的基础操作,以及基础知识点,了解这些后基本上就可以使用Java写算法了. ...