在spring容器初始化的时候,BeanDefinition中配置的bean的属性值一般都为String类型,如何将String类型转换为Bean中属性对应的类型呢,在这个过程中就需要用到类型转换器了。spring实例化bean过程中对属性值的转换主要是使用BeanWrapperImpl实现的。
首先来看下BeanWrapperImpl的使用
定义一个处理日期的转换器
创建一个BeanWrapperImpl用于包装目标bean(这里来模拟spring的内部实现)。然后注册Date类型的转换器,将值使用DatePropertyEditor转换为Date类型。调用setPropertyValue的时候,给testBean的birthday字段设置一个字符串类型的时间,在实际赋值的过程中会调用到类型转换器将字符串转换为日期类型。
同样可以实现一个String trim的转换器
BeanWrapper支持嵌套属性的赋值,当存在嵌套属性的时候需要设置 setAutoGrowNestedPaths=true。
接下来我们就来研究一下BeanWrapperImpl的实现过程
javabeans包中声明了一个PropertyEditor接口,用于提供对属性的访问和赋值。
PropertyEditor有个默认实现类PropertyEditorSupport,如果要自定义属性转换器,直接继承该类,然后覆写setAsText、getAsText、setValue、getValue方法。在类型转换的时候如果值是字符串会调用setAsText来赋值value,其他情况下会调用setValue来赋值value,然后再调用getValue获取改变后的value值完成类型转换。相当于将PropertyEditor当做一个加工作坊,传进去一个值,返回想要类型的值
注意 :PropertyEditor是线程不安全的,有状态的,因此每次使用时都需要创建一个,不可重用;
首先先看下BeanWrapperImpl的继承图。还有一个跟BeanWrapperImpl平级的实现类DirectFieldAccessor,用于处理getter和setter的字段访问
从图可以看出,BeanWrapperImpl实现了3个顶级接口,提供了对象属性访问、类型转换器的注册和查找、类型转换功能
从继承图可以看到PropertyEditorRegistrySupport实现了PropertyEditorRegistry接口,该类默认实现了类型转换器的注册和查找功能
在PropertyEditorRegistrySupport声明了多个存储结构用于存储不同的类型转换器来源。这里需要注意的是PropertyEditor是存在在Map中的,目标类型作为key,所以对于一个类型只能注册一个PropertyEditor,后面注册的会覆盖前面注册的
同时还看到一个conversionService变量,spring提供了另一种类型转换接口Converter,通过conversionService调用对应的Converter进行类型转换,在PropertyEditorRegistrySupport同样可以注册进来conversionService,用于使用Converter进行类型转换。conversionService的详细使用会在下篇文章中讲到。
接下来来看PropertyEditor的注册过程。
在上面提到过PropertyEditor存在在一个Map中,key是目标类型,那么这个参数propertyPath是干嘛的呢?这是为了给属性名指定专属的类型的转换器。因为一个目标类型只能有一个PropertyEditor的限制。但是有时候确实某个属性的类型转换比较特殊,这个时候就可以给这个属性名单独注册一个类型转换器,不会覆盖其他的哦。在类型转换的时候,会先根据属性名去customEditorsForPath中找可以用的PropertyEditor。
来看PropertyEditor的查找流程
首先根据属性名从customEditorsForPath查找特定的类型转换器
根据类型查找定义的类型转换器,如果没有对应的,则查找父类对应的类型转换器
PropertyEditorRegistrySupport 中默认添加了一些转换器,当调用 getDefaultEditor(requiredType)的时候会进行注册
TypeConverter接口定义了将一个值转换为目标类型的值的功能。在继承图中可以看出TypeConverterSupport对类型转换提供了默认实现。
TypeConverterSupport将类型转换功能委托给typeConverterDelegate实现
TypeConverterDelegate实现了类型转换的功能,创建TypeConverterDelegate的时候需要一个propertyEditorRegistry对象,用于查找匹配的类型转换器
首先通过propertyEditorRegistry查找自定义的类型转换器PropertyEditor和ConversionService
当该类型没有注册自定义的PropertyEditor,并且存在conversionService的时候,使用conversionService进行类型转换。如果conversionService中没有配置对应的converter,那么继续尝试使用默认注册的PropertyEditor。
如果目标类型存在自定义PropertyEditor 或者 目标类型和值类型不一样则需要进行类型转换(当没有找到ProperEditor的时候会尝试查找默认注册的PropertyEditor)
类型转换,主要分三种情况处理,值类型不是字符串,值类型是字符串数组,值类型是字符串
最后,需要对转换后的值和目标类型进行判断,是否符合要求,如果不符合继续处理。这里主要处理了集合类型还有Number类型、基本类型转String、枚举类型。
在attemptToConvertStringToEnum方法中自动根据枚举的名称转换为枚举的对象
PropertyAccessor接口定义了属性访问的功能。通过实现setPropertyValue 和 getPropertyValue方法实现对象属性的赋值和访问。
AbstractPropertyAccessor实现了PropertyAccessor接口,不过没有对setPropertyValue和getPropertyValue进行实现,而是单独提供了一个 setPropertyValues的方法, 用于批量设置属性值,同时可以通过 ignoreUnknown和ignoreInvalid参数忽略未知的属性
在AbstractNestablePropertyAccessor类中实现了setPropertyValue和getPropertyValue功能。
由于存在嵌套属性赋值的情况,对于嵌套的处理,其实只需要对嵌套的最底层进行类型转换,上层每一层就创建默认的值然后set到再上层对象的属性中。在这里spring使用了递归解决这个问题,创建每一层属性的对象值,使用BeanWrapper包装该对象,那么又是一个BeanWrapperImpl的赋值流程。
来看这个递归的内部实现,在getNestedPropertyAccessor完成对外层属性的初始化和将该值赋值到所依赖的对象中。然后使用BeanWrapper封装属性对象,后续走属性对象的赋值流程
创建属性对象,并将该对象set到宿主对象。因为对象是指针引用的,所以在这步已经完成对宿主对象的属性赋值,接下来的流程只要对属性对象中的依赖属性进行赋值。
使用递归解决了嵌套赋值的问题,那么接下来就是针对最底层BeanWrapperImpl的属性赋值流程
在processLocalProperty方法中,首先通过子类获取属性处理器,通过PropertyHandler对属性赋值。在赋值之前再次判断属性是否已经进行了类型转换,如果没有再次调用类型转换器进行转换,如果已经完成类型转换,使用ConvertedValue
对属性的访问和设置spring进行了更小粒度的封装。提供了 PropertyHandler抽象类。为什么在这里进行抽象,看PropertyHandler的两个实现,可以看到一个是BeanPropertyHandler,一个是FieldPropertyHandler,不难想象,属性一种是由getter和setter方法进行访问,一种是没有getter和setter直接反射字段进行的。
如果要对map进行控制,我们可以再提供一个专门处理map的实现了类handler
BeanWrapperImpl提供了BeanPropertyHandler,将setter和getter传入
BeanPropertyHandler提供对setter和getter的访问
AbstractNestablePropertyAccessor的另一个实现类DirectFieldAccessor,专门用于给字段赋值,不依赖setter和getter,那么这个是怎么实现的,看源码发现是DirectFieldAccessor中提供了一个PropertyHandler的实现类,通过Field的反射实现了setValue和getValue
批处理任务是大多数IT项目的一个重要组成部分,批处理在业务系统中负责处理海量的数据,无须人工干预就能够自动高效的进行复杂的数据分析和处理。批处理会定期读入批量数据,经过相应的业务处理进行归档的业务 *** 作,批处理的特征是自动执行,处理的数据量大,定时执行。将整个批处理的流程按逻辑划分可以分为读数据,处理数据和写数据。以下是整理的springbatch框架的简介知识,希望能帮助到大家。
springbatch对批处理本身的特性进行了抽象,将批处理作业抽象为job和jobstep,将批处理的处理过程分解为数据读,数据处理和数据写。
将异常处理机制分为跳过,重启和重试。将作业的分区分为多线程,并行远程和分区。
springbatch不是一个调度框架,但需要调度框架来配合完成批处理任务,它只关注批处理相关的任务问题,但没有提供相应的调度功能,如果需要使用调度功能,需要使用调度框架,这里介绍一个比较常用的调度框架quartz,可以配合springbatch完成批处理的任务调度。
springbatch的架构分为三层:基础架构层,核心层和应用层。应用层包含所有的批处理作业,核心层主要提供JobLauncher、Job和step,基础架构层主要提供通用的读(ItemReader)、写(ItemWriter)和服务处理(如:RetryTemplate重试模板;RepeatTemplate:重复模板),Spring
batch的三层架构体系使得Springbatch框架可以在不同的层级进行扩展,避免不同层级之间的相互影响。
job的介绍
批处理的作业是由一组step组成,同时job本身也是配置文件的顶级元素。每个作业都有自己的名字,可以定义step的执行顺序,以及定义作业是否可以重启。job执行的时候会生成一个jobinstance(作业实例)和一个jobexecution(作业执行器)。jobinstance包含执行job期间产生的数据以及job执行的状态信息;1个job可以对应多个jobinstance,1个jobinstance可以对应多个jobexecution。
job的配置的主要属性有id(作业的唯一标识)、job-repository(定义作业仓库)、incrementer(作业参数递增器)、restartable(作业是否重启)、parent(指定该作业的父作业)、abstract(定义作业是否抽象)。
step的介绍
step表示作业中一个完整的步骤,一个job可以由一个或者多个step组成,step主要负责批处理运行过程中的主要业务逻辑的实现。回龙观电脑培训认为每次step执行的时候会生成一个或者多个jobexecution,每次任务执行失败的时候,等到下次重新执行该任务的时候就会为该任务的step重新生成一个stepexecution。
springJdbcTemplate批量例子:
@Override
public
void
saveBatch(final
List<Employee>
employeeList)
{
final
int
batchSize
=
500;
for
(int
j
=
0;
j
<
employeeListsize();
j
+=
batchSize)
{
final
List<Employee>
batchList
=
employeeListsubList(j,
j
+
batchSize
>
employeeListsize()
employeeListsize()
:
j
+
batchSize);
getJdbcTemplate()batchUpdate(QUERY_SAVE,
new
BatchPreparedStatementSetter()
{
@Override
public
void
setValues(PreparedStatement
ps,
int
i)
throws
SQLException
{
Employee
employee
=
batchListget(i);
pssetString(1,
employeegetFirstname());
pssetString(2,
employeegetLastname());
pssetString(3,
employeegetEmployeeIdOnSourceSystem());
}
@Override
public
int
getBatchSize()
{
return
batchListsize();
}
});
}
}
主键自动增加可以在db层面建立sequence来实现,在插入的时候这么写:
Long
newPersonId
=
holdergetKey()longValue();
Spring可以做什么?
微服务
微服务架构是“新常态”。构建小型、独立的、随时可以运行的应用程序可以为您的代码带来极大的灵活性和d性。SpringBoot的许多定制的功能是在生产中大规模构建和运行微服务变得容易。并且不要忘记,没有SpringCloud的微服务架构是不完整的,SpringCloud可以简化管理并提高容错能力。
响应式
响应式系统具有某些特性,使其成为低延迟、高吞吐量工作负载的理想选择。ProjectReactor和Spring产品组合协同工作,使开发人员能够构建具有响应式、d性和消息驱动的企业级响应式式系统。
云
开发分布式系统可能具有挑战性。复杂性从应用层转移到网络层,需要服务之间进行更多的交互。使您的代码成为“云原生”意味着处理12要素诸如外部配置、无状态、日志记录和连接到支持服务等问题。SpringCloud项目套件包含使应用程序在云中运行所需的许多服务。
网络应用
Spring使构建Web应用程序变得快速而轻松。通过删除与Web开发相关的大部分样板代码和配置,您可以获得一个现代Web编程模型,该模型简化了服务器端HTML应用程序、RESTAPI和双向、基于事件的系统的开发。
无服务器
无服务器应用程序利用现代云计算功能和抽象,让您专注于逻辑而不是基础设施。在无服务器环境中,您可以专注于编写应用程序代码,而底层平台负责扩展、运行时、资源分配、安全性和其他“服务器”细节。
事件驱动
事件驱动的系统反映了现代企业的实际运作方式——每天都在发生数以千计的小变化。Spring能够处理事件并使开发人员能够围绕它们构建应用程序,这意味着您的应用程序将与您的业务保持同步。Spring有许多事件驱动的选项可供选择,从集成和流式传输一直到云功能和数据流。
批量
有效处理大量数据的能力使其成为许多用例的理想选择。SpringBatch对行业标准处理模式的实现让您可以在JVM上构建健壮的批量处理作业。从Spring产品组合中添加SpringBoot和其他组件可让您构建任务关键型批量处理应用程序。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)