Spring:BeanDefination (上)
前言:
关于 BeanDefination 在Spring 官方文档中介绍:官方文档,第1.3条;
摘抄:
文档翻译如下:
SpringIoc容器管理一个Bean或多个Bean,这些Bean通过我们提供给容器的配置元数据被创建出来(例如,在xml中的定义)
在容器中,这些Bean的定义用BeanDefinition对象来表示,包含以下元数据:
- 全限定类名, 通常是Bean的实际实现类;
- Bean行为配置元素,它们说明Bean在容器中的行为(作用域、生命周期回调等等);
- Bean执行工作所需要的的其他Bean的引用,这些Bean也称为协作者或依赖项;
- 其他配置信息,例如,管理连接池的bean中,限制池的大小或者使用的连接的数量。
正常创建一个Bean
Spring 通过 BeanDefination 创建Bean
通过上面的比较,我们可以发现,相比于正常的对象的创建过程,Spring对其管理的bean没有直接采用new的方式,而是先通过解析配置数据以及根据对象本身的一些定义而获取其对应的beandefinition,并将这个beandefinition作为之后创建这个bean的依据。同时Spring在这个过程中提供了一些扩展点,例如我们在图中所提到了BeanfactoryProcessor。
BeanDefinition的方法分析
这里对于每个字段我只保留了一个方法,只要知道了字段的含义,方法的含义我们自然就知道了
// 获取父BeanDefinition,主要用于合并,下节中会详细分析
String getParentName();// 对于的bean的ClassName
void setBeanClassName(@Nullable String beanClassName);// Bean的作用域,不考虑web容器,主要两种,单例/原型,见官网中1.5内容
void setScope(@Nullable String scope);// 是否进行懒加载
void setLazyInit(boolean lazyInit);// 是否需要等待指定的bean创建完之后再创建
void setDependsOn(@Nullable String... dependsOn);// 是否作为自动注入的候选对象
void setAutowireCandidate(boolean autowireCandidate);// 是否作为主选的bean
void setPrimary(boolean primary);// 创建这个bean的类的名称
void setFactoryBeanName(@Nullable String factoryBeanName);// 创建这个bean的方法的名称
void setFactoryMethodName(@Nullable String factoryMethodName);// 构造函数的参数
ConstructorArgumentValues getConstructorArgumentValues();// setter方法的参数
MutablePropertyValues getPropertyValues();// 生命周期回调方法,在bean完成属性注入后调用
void setInitMethodName(@Nullable String initMethodName);// 生命周期回调方法,在bean被销毁时调用
void setDestroyMethodName(@Nullable String destroyMethodName);// Spring可以对bd设置不同的角色,了解即可,不重要
// 用户定义 int ROLE_APPLICATION = 0;
// 某些复杂的配置 int ROLE_SUPPORT = 1;
// 完全内部使用 int ROLE_INFRASTRUCTURE = 2;
void setRole(int role);// bean的描述,没有什么实际含义
void setDescription(@Nullable String description);// 根据scope判断是否是单例
boolean isSingleton();// 根据scope判断是否是原型
boolean isPrototype();// 跟合并beanDefinition相关,如果是abstract,说明会被作为一个父beanDefinition,不用提供class属性
boolean isAbstract();// bean的源描述,没有什么实际含义
String getResourceDescription();// cglib代理前的BeanDefinition
BeanDefinition getOriginatingBeanDefinition();
BeanDefinition的继承关系
类图如下:
1、BeanDefition 继承的接口
- org.springframework.core.AttributeAccessor
先来看接口上标注的这段java doc
Interface defining a generic contract for attaching and accessing metadata to/from arbitrary objects.
翻译下来就是:
这个接口为从其它任意类中获取或设置元数据提供了一个通用的规范。
其实这就是访问者模式的一种体现,采用这方方法,我们可以将数据接口跟操作方法进行分离。
我们再来看这个接口中定义的方法:
void setAttribute(String name, @Nullable Object value);Object getAttribute(String name);Object removeAttribute(String name);boolean hasAttribute(String name);String[] attributeNames();
就是提供了一个获取属性跟设置属性的方法
那么现在问题来了,在我们整个BeanDefiniton体系中,这个被操作的数据结构在哪呢?不要急,在后文中的AbstractBeanDefinition会介绍。
- org.springframework.beans.BeanMetadataElement
我们还是先看 java doc:
Interface to be implemented by bean metadata elements that carry a configuration source object.
翻译:这个接口提供了一个方法去获取配置源对象,其实就是我们的原文件。
这个接口只提供了一个方法:
@Nullable
Object getSource();
我们可以理解为,当我们通过注解的方式定义了一个 IndexService 时,那么此时的IndexService对应的BeanDefinition通过getSource方法返回的就是IndexService.class这个文件对应的一个File对象。
2.AbstractBeanDefinition
AbstractBeanDefinition的继承关系
类图:
- org.springframework.core.AttributeAccessorSupport
可以看到这个类实现了 AttributeAccerror接口,我们在上文中已经提到过,AttributeAccerror采用了访问者的涉及模式,将数据结构跟操作方法进行了分离,数据结构在哪呢?就在AttributeAccessorSupport这个类中,我们看下它的代码:
public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable {/** Map with String keys and Object values. */private final Map<String, Object> attributes = new LinkedHashMap<>();@Overridepublic void setAttribute(String name, @Nullable Object value) {Assert.notNull(name, "Name must not be null");if (value != null) {this.attributes.put(name, value);}else {removeAttribute(name);}}......省略下面的代码
可以看到,在这个类中,维护了一个map,这就是BeanDefinition体系中,通过访问者模式所有操作的数据对象。
- org.springframework.beans.BeanMetadataAttributeAccessor
这个类主要就是对我们上面的map中的数据操作做了更深一层的封装,我们就看其中的两个方法:
public void addMetadataAttribute(BeanMetadataAttribute attribute) {super.setAttribute(attribute.getName(), attribute);
}
public BeanMetadataAttribute getMetadataAttribute(String name) {return (BeanMetadataAttribute) super.getAttribute(name);
}
可以发现,它只是将属性统一封装成了一个 BeanMetadataAttribute,然后就调用了父类的方法,将其放入到map中。
我们的 AbstractBeanDefinition通过继承了 BeanMetadataAttributeAccessor这个类,可以对 BeanDefinition中的属性进行操作。这里说的属性仅仅指的是BeanDefinition中的一个map,而不是它的其它字段。
为什么需要AbstractBeanDefinition?
对比 BeanDefinition的源码我们可以发现, AbstractBeanDefinition对 BeanDefinition的大部分方法做了实现(没有实现 parentName相关方法)。同时定义了一系列的常量及默认字段。这是因为 BeanDefinition接口过于顶层,如果我们依赖 BeanDefinition这个接口直接去创建其实现类的话过于麻烦,所以通过 AbstractBeanDefinition做了一个下沉,并给很多属性赋了默认值,例如:
// 默认情况不是懒加载的
private boolean lazyInit = false;
// 默认情况不采用自动注入
private int autowireMode = AUTOWIRE_NO;
// 默认情况作为自动注入的候选bean
private boolean autowireCandidate = true;
// 默认情况不作为优先使用的bean
private boolean primary = false;
........
这样可以方便我们创建其子类,如我们接下来要讲的:ChildBeanDefinition,RootBeanDefinition等等
3.AbstractBeanDefinition的三个子类
GenericBeanDefinition
- 替代了原来的ChildBeanDefinition,比起ChildBeanDefinition更为灵活,ChildBeanDefinition在实例化的时候必须要指定一个parentName,而GenericBeanDefinition不需要。我们通过注解配置的bean以及我们的配置类(除@Bean外)的BeanDefiniton类型都是GenericBeanDefinition。
ChildBeanDefinition
现在已经被GenericBeanDefinition所替代了。我在5.1.x版本没有找到使用这个类的代码。
RootBeanDefinition
- Spring在启动时会实例化几个初始化的BeanDefinition,这几个BeanDefinition的类型都为RootBeanDefinition
- Spring在合并BeanDefinition返回的都是RootBeanDefinition
- 我们通过@Bean注解配置的bean,解析出来的BeanDefinition都是RootBeanDefinition(实际上是其子类ConfigurationClassBeanDefinition)
4.AnnotatedBeanDefinition
这个接口继承了我们的BeanDefinition接口,我们查看其源码可以发现:
AnnotationMetadata getMetadata();@Nullable
MethodMetadata getFactoryMethodMetadata();
这个接口相比于BeanDefinition, 仅仅多提供了两个方法
- getMetadata(),主要用于获取注解元素据。从接口的命名上我们也能看出,这类主要用于保存通过注解方式定义的bean所对应的BeanDefinition。所以它多提供了一个关于获取注解信息的方法
- getFactoryMethodMetadata(),这个方法跟我们的@Bean注解相关。当我们在一个配置类中使用了@Bean注解时,被@Bean注解标记的方法,就被解析成了FactoryMethodMetadata。
5.AnnotatedBeanDefinition的三个实现类
AnnotatedGenericBeanDefinition
- 通过形如下面的API注册的bean都是AnnotatedGenericBeanDefinition</font
public static void main(String[] args) {AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();ac.register(Config.class);
}
这里的 config 对象,最后在Spring容器中就是一个 AnnotatedGenericBeanDefinition。
- 通过@Import注解导入的类,最后都是解析为 AnnotatedGenericBeanDefinition。
ScannedGenericBeanDefinition
- 都过注解扫描的类,如@Service,@Compent等方式配置的Bean都是ScannedGenericBeanDefinition
ConfigurationClassBeanDefinition
- 通过@Bean的方式配置的Bean为ConfigurationClassBeanDefinition
最后,我们还剩一个ClassDerivedBeanDefinition,这个类是跟kotlin相关的类,一般用不到,笔者也不熟,这里就不管了!
总结
至此,我们算完成了BeanDefinition部分的学习,在下一节中,我将继续跟大家一起学习BeanDefinition合并的相关知识。这篇文章中,主要学习了
- 什么是BeanDefinition,总结起来就是一句话,Spring创建bean时的建模对象。
- BeanDefinition的具体使用的子类,以及Spring在哪些地方使用到了它们。这部分内容在后面的学习中很重要,画图总结如下:
1.5小结内容的补充
单例
一个单例的bean意味着,这个bean只会容器创建一次。在创建后,容器中的每个地方使用的都是同一个bean对象。这里用Spring官网上的一个原图:
在上面图片的例子中,accountDao在被其它三个bean引用,这三个引用指向的都是同一个bean。
在上面图片的例子中,accountDao在被其它三个bean引用,这三个引用指向的都是同一个bean。
在默认情况下,Spring中bean的默认域就是单例的。分XML跟注解两种配置方式:
<!--即使配置singleton也是单例的,这是Spring的默认配置-->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
@Component
// 这里配置singleton,默认就是singleton
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class LuBanService{
}
原型
一个原型的bean意味着,每次我们使用时都会重新创建这个bean。
在上面图片的例子中,accountDao在被其它三个bean引用,这三个引用指向的都是一个新建的bean。
两种配置方式:
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
@Component
// 这里配置prototype
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LuBanService{}
End !!!
Spring:BeanDefination (上)相关推荐
- Spring Boot 上传文件(spring boot upload file)
本篇文章将说明在Spring Boot web程序中如何上传文件. 开发环境: 1. eclipse Oxygen Release (4.7.0) 2. Spring Boot 1.4.3 RELEA ...
- 利用 Spring MVC 上传多文件到指定目录 spring upload files
本篇文章,我们会教你通过eclipse构建一个创建一个Java web项目并转为maven工程,实现用spring mvc将所选文件上传到指定目录 开发环境: 1.JDK 1.7 2.Maven 3. ...
- Spring Boot(十七):使用Spring Boot上传文件
Spring Boot(十七):使用Spring Boot上传文件 环境:Spring Boot最新版本1.5.9.jdk使用1.8.tomcat8.0 一.pom包配置 <parent> ...
- Spring MVC上传文件原理和resolveLazily说明
问题:使用Spring MVC上传大文件,发现从页面提交,到进入后台controller,时间很长.怀疑是文件上传完成后,才进入.由于在HTTP首部自定义了"Token"字段用于权 ...
- Spring CommonsMultipartResolver 上传文件
http://yanglei008.iteye.com/blog/246920 Spring CommonsMultipartResolver 上传文件 博客分类: spring SpringMVCJ ...
- java spring mvc 上传_Java Spring MVC 上传下载文件配置及controller方法详解
下载: 1.在spring-mvc中配置(用于100M以下的文件下载) 下载文件代码 @RequestMapping("/file/{name.rp}") public Respo ...
- Spring文件上传
2019独角兽企业重金招聘Python工程师标准>>> Spring文件上传 1.所需依赖包:commons-fileupload-1.3.1.jar 2.Maven配置文件pom. ...
- (转)Spring Boot(十七):使用 Spring Boot 上传文件
http://www.ityouknow.com/springboot/2018/01/12/spring-boot-upload-file.html 上传文件是互联网中常常应用的场景之一,最典型的情 ...
- springboot上传文件同时传参数_Spring Boot 系列:使用 Spring Boot 上传文件
上传文件是互联网中常常应用的场景之一,最典型的情况就是上传头像等,今天就带着带着大家做一个 Spring Boot 上传文件的小案例. 1.pom 包配置 我们使用 Spring Boot 版本 2. ...
- Spring boot上传文件(图片)到阿里云OSS(直接上传到应用服务器)
Spring boot上传文件(图片)到阿里云OSS(直接上传到应用服务器) 主要思路 上传图片的思路有两种: 第一种是上传到应用服务器,再由应用服务器上传到OSS.这种适合上传的文件较小,较为简单, ...
最新文章
- 面试官:说说什么是Java内存模型?
- 周末一起用文本数据库玩玩Code First
- alter table add column多个字段_SQL对表中字段的操作 alter
- 零基础学Python-爬虫-5、下载音频
- GraphQL入门之工程搭建
- 麦芒装饰装修小程序源码V3.2.59
- 经典算法系列三----堆排序
- JCreator 写applet
- 恢复云数据库MySQL的备份文件到自建数据库
- 遇到一个难题:如何从java中调用到C的功能
- 百度富文本编辑器修改图片上传尺寸
- linux中gnuplot给定文本,Gnuplot (三)输出图片/字体支持、eps/png/pdf/enhanced文本
- uni-app的基本使用
- jQuery+Ajax+PHP无刷新分页
- 图灵停机问题(halting problem)
- Error:A JNI error has occurred,please check your installation and try again
- navigationController的一些用法
- Mac Google浏览器出现:您目前无法访问 XX.XX.XX.XX,因为此网站发送了 Google Chrome 无法处理的杂乱凭据
- html中radio设置默认值
- 意料之外,这个应用居然霸榜了苹果APP Store排行榜