Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ma yanling
hutool-5-master
Commits
45cda665
Commit
45cda665
authored
Sep 25, 2024
by
ma yanling
Browse files
project commit
parent
ad2fb30a
Pipeline
#2354
failed with stages
in 0 seconds
Changes
369
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
3415 additions
and
0 deletions
+3415
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AnnotationScanner.java
.../cn/hutool/core/annotation/scanner/AnnotationScanner.java
+198
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java
...ool/core/annotation/scanner/ElementAnnotationScanner.java
+44
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java
...utool/core/annotation/scanner/EmptyAnnotationScanner.java
+31
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/FieldAnnotationScanner.java
...utool/core/annotation/scanner/FieldAnnotationScanner.java
+47
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java
...ool/core/annotation/scanner/GenericAnnotationScanner.java
+149
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MetaAnnotationScanner.java
...hutool/core/annotation/scanner/MetaAnnotationScanner.java
+110
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java
...tool/core/annotation/scanner/MethodAnnotationScanner.java
+133
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/TypeAnnotationScanner.java
...hutool/core/annotation/scanner/TypeAnnotationScanner.java
+105
-0
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/package-info.java
.../java/cn/hutool/core/annotation/scanner/package-info.java
+7
-0
hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
+324
-0
hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java
...core/src/main/java/cn/hutool/core/bean/BeanDescCache.java
+37
-0
hutool-core/src/main/java/cn/hutool/core/bean/BeanException.java
...core/src/main/java/cn/hutool/core/bean/BeanException.java
+32
-0
hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java
...core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java
+80
-0
hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java
hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java
+326
-0
hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
+1011
-0
hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java
hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java
+226
-0
hutool-core/src/main/java/cn/hutool/core/bean/NullWrapperBean.java
...re/src/main/java/cn/hutool/core/bean/NullWrapperBean.java
+29
-0
hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java
hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java
+404
-0
hutool-core/src/main/java/cn/hutool/core/bean/copier/AbsCopier.java
...e/src/main/java/cn/hutool/core/bean/copier/AbsCopier.java
+28
-0
hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
.../src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
+94
-0
No files found.
Too many changes to show.
To preserve performance only
369 of 369+
files are displayed.
Plain diff
Email patch
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/AnnotationScanner.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.annotation.scanner
;
import
cn.hutool.core.annotation.AnnotationUtil
;
import
cn.hutool.core.util.ArrayUtil
;
import
cn.hutool.core.util.ObjectUtil
;
import
java.lang.annotation.Annotation
;
import
java.lang.annotation.Inherited
;
import
java.lang.reflect.AnnotatedElement
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.function.BiConsumer
;
import
java.util.function.Predicate
;
import
java.util.stream.Collectors
;
import
java.util.stream.Stream
;
/**
* <p>注解扫描器,用于从支持的可注解元素上获取所需注解
*
* <p>默认提供了以下扫描方式:
* <ul>
* <li>{@link #NOTHING}:什么都不做,什么注解都不扫描;</li>
* <li>{@link #DIRECTLY}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解;</li>
* <li>
* {@link #DIRECTLY_AND_META_ANNOTATION}:扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,
* 以及这些注解的元注解;
* </li>
* <li>{@link #SUPERCLASS}:扫描元素本身以及父类的层级结构中声明的注解;</li>
* <li>{@link #SUPERCLASS_AND_META_ANNOTATION}:扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解;</li>
* <li>{@link #INTERFACE}:扫描元素本身以及父接口的层级结构中声明的注解;</li>
* <li>{@link #INTERFACE_AND_META_ANNOTATION}:扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
* <li>{@link #TYPE_HIERARCHY}:扫描元素本身以及父类、父接口的层级结构中声明的注解;</li>
* <li>{@link #TYPE_HIERARCHY_AND_META_ANNOTATION}:扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解;</li>
* </ul>
*
* @author huangchengxing
* @see TypeAnnotationScanner
* @see MethodAnnotationScanner
* @see FieldAnnotationScanner
* @see MetaAnnotationScanner
* @see ElementAnnotationScanner
* @see GenericAnnotationScanner
*/
public
interface
AnnotationScanner
{
// ============================ 预置的扫描器实例 ============================
/**
* 不扫描任何注解
*/
AnnotationScanner
NOTHING
=
new
EmptyAnnotationScanner
();
/**
* 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解的扫描器
*/
AnnotationScanner
DIRECTLY
=
new
GenericAnnotationScanner
(
false
,
false
,
false
);
/**
* 扫描元素本身直接声明的注解,包括父类带有{@link Inherited}、被传递到元素上的注解,以及这些注解的元注解的扫描器
*/
AnnotationScanner
DIRECTLY_AND_META_ANNOTATION
=
new
GenericAnnotationScanner
(
true
,
false
,
false
);
/**
* 扫描元素本身以及父类的层级结构中声明的注解的扫描器
*/
AnnotationScanner
SUPERCLASS
=
new
GenericAnnotationScanner
(
false
,
true
,
false
);
/**
* 扫描元素本身以及父类的层级结构中声明的注解,以及这些注解的元注解的扫描器
*/
AnnotationScanner
SUPERCLASS_AND_META_ANNOTATION
=
new
GenericAnnotationScanner
(
true
,
true
,
false
);
/**
* 扫描元素本身以及父接口的层级结构中声明的注解的扫描器
*/
AnnotationScanner
INTERFACE
=
new
GenericAnnotationScanner
(
false
,
false
,
true
);
/**
* 扫描元素本身以及父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器
*/
AnnotationScanner
INTERFACE_AND_META_ANNOTATION
=
new
GenericAnnotationScanner
(
true
,
false
,
true
);
/**
* 扫描元素本身以及父类、父接口的层级结构中声明的注解的扫描器
*/
AnnotationScanner
TYPE_HIERARCHY
=
new
GenericAnnotationScanner
(
false
,
true
,
true
);
/**
* 扫描元素本身以及父接口、父接口的层级结构中声明的注解,以及这些注解的元注解的扫描器
*/
AnnotationScanner
TYPE_HIERARCHY_AND_META_ANNOTATION
=
new
GenericAnnotationScanner
(
true
,
true
,
true
);
// ============================ 静态方法 ============================
/**
* 给定一组扫描器,使用第一个支持处理该类型元素的扫描器获取元素上可能存在的注解
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param scanners 注解扫描器
* @return 注解
*/
static
List
<
Annotation
>
scanByAnySupported
(
AnnotatedElement
annotatedEle
,
AnnotationScanner
...
scanners
)
{
if
(
ObjectUtil
.
isNull
(
annotatedEle
)
&&
ArrayUtil
.
isNotEmpty
(
scanners
))
{
return
Collections
.
emptyList
();
}
return
Stream
.
of
(
scanners
)
.
filter
(
scanner
->
scanner
.
support
(
annotatedEle
))
.
findFirst
()
.
map
(
scanner
->
scanner
.
getAnnotations
(
annotatedEle
))
.
orElseGet
(
Collections:
:
emptyList
);
}
/**
* 根据指定的扫描器,扫描元素上可能存在的注解
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param scanners 注解扫描器
* @return 注解
*/
static
List
<
Annotation
>
scanByAllSupported
(
AnnotatedElement
annotatedEle
,
AnnotationScanner
...
scanners
)
{
if
(
ObjectUtil
.
isNull
(
annotatedEle
)
&&
ArrayUtil
.
isNotEmpty
(
scanners
))
{
return
Collections
.
emptyList
();
}
return
Stream
.
of
(
scanners
)
.
map
(
scanner
->
scanner
.
getAnnotationsIfSupport
(
annotatedEle
))
.
flatMap
(
Collection:
:
stream
)
.
collect
(
Collectors
.
toList
());
}
// ============================ 抽象方法 ============================
/**
* 判断是否支持扫描该注解元素
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
default
boolean
support
(
AnnotatedElement
annotatedEle
)
{
return
false
;
}
/**
* 获取注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 注解
*/
default
List
<
Annotation
>
getAnnotations
(
AnnotatedElement
annotatedEle
)
{
final
List
<
Annotation
>
annotations
=
new
ArrayList
<>();
scan
((
index
,
annotation
)
->
annotations
.
add
(
annotation
),
annotatedEle
,
null
);
return
annotations
;
}
/**
* 若{@link #support(AnnotatedElement)}返回{@code true},
* 则调用并返回{@link #getAnnotations(AnnotatedElement)}结果,
* 否则返回{@link Collections#emptyList()}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 注解
*/
default
List
<
Annotation
>
getAnnotationsIfSupport
(
AnnotatedElement
annotatedEle
)
{
return
support
(
annotatedEle
)
?
getAnnotations
(
annotatedEle
)
:
Collections
.
emptyList
();
}
/**
* 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理。
* 调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
default
void
scan
(
BiConsumer
<
Integer
,
Annotation
>
consumer
,
AnnotatedElement
annotatedEle
,
Predicate
<
Annotation
>
filter
)
{
filter
=
ObjectUtil
.
defaultIfNull
(
filter
,
(
a
)->
annotation
->
true
);
for
(
final
Annotation
annotation
:
annotatedEle
.
getAnnotations
())
{
if
(
AnnotationUtil
.
isNotJdkMateAnnotation
(
annotation
.
annotationType
())
&&
filter
.
test
(
annotation
))
{
consumer
.
accept
(
0
,
annotation
);
}
}
}
/**
* 若{@link #support(AnnotatedElement)}返回{@code true},则调用{@link #scan(BiConsumer, AnnotatedElement, Predicate)}
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
default
void
scanIfSupport
(
BiConsumer
<
Integer
,
Annotation
>
consumer
,
AnnotatedElement
annotatedEle
,
Predicate
<
Annotation
>
filter
)
{
if
(
support
(
annotatedEle
))
{
scan
(
consumer
,
annotatedEle
,
filter
);
}
}
}
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/ElementAnnotationScanner.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.annotation.scanner
;
import
cn.hutool.core.util.ObjectUtil
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
java.util.function.BiConsumer
;
import
java.util.function.Predicate
;
import
java.util.stream.Stream
;
/**
* 扫描{@link AnnotatedElement}上的注解,不支持处理层级对象
*
* @author huangchengxing
*/
public
class
ElementAnnotationScanner
implements
AnnotationScanner
{
/**
* 判断是否支持扫描该注解元素,仅当注解元素不为空时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public
boolean
support
(
AnnotatedElement
annotatedEle
)
{
return
ObjectUtil
.
isNotNull
(
annotatedEle
);
}
/**
* 扫描{@link AnnotatedElement}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
@Override
public
void
scan
(
BiConsumer
<
Integer
,
Annotation
>
consumer
,
AnnotatedElement
annotatedEle
,
Predicate
<
Annotation
>
filter
)
{
filter
=
ObjectUtil
.
defaultIfNull
(
filter
,
a
->
t
->
true
);
Stream
.
of
(
annotatedEle
.
getAnnotations
())
.
filter
(
filter
)
.
forEach
(
annotation
->
consumer
.
accept
(
0
,
annotation
));
}
}
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/EmptyAnnotationScanner.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.annotation.scanner
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.function.BiConsumer
;
import
java.util.function.Predicate
;
/**
* 默认不扫描任何元素的扫描器
*
* @author huangchengxing
*/
public
class
EmptyAnnotationScanner
implements
AnnotationScanner
{
@Override
public
boolean
support
(
AnnotatedElement
annotatedEle
)
{
return
true
;
}
@Override
public
List
<
Annotation
>
getAnnotations
(
AnnotatedElement
annotatedEle
)
{
return
Collections
.
emptyList
();
}
@Override
public
void
scan
(
BiConsumer
<
Integer
,
Annotation
>
consumer
,
AnnotatedElement
annotatedEle
,
Predicate
<
Annotation
>
filter
)
{
// do nothing
}
}
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/FieldAnnotationScanner.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.annotation.scanner
;
import
cn.hutool.core.annotation.AnnotationUtil
;
import
cn.hutool.core.util.ObjectUtil
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
java.lang.reflect.Field
;
import
java.util.function.BiConsumer
;
import
java.util.function.Predicate
;
/**
* 扫描{@link Field}上的注解
*
* @author huangchengxing
*/
public
class
FieldAnnotationScanner
implements
AnnotationScanner
{
/**
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Field}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public
boolean
support
(
AnnotatedElement
annotatedEle
)
{
return
annotatedEle
instanceof
Field
;
}
/**
* 扫描{@link Field}上直接声明的注解,调用前需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
@Override
public
void
scan
(
BiConsumer
<
Integer
,
Annotation
>
consumer
,
AnnotatedElement
annotatedEle
,
Predicate
<
Annotation
>
filter
)
{
filter
=
ObjectUtil
.
defaultIfNull
(
filter
,
a
->
annotation
->
true
);
for
(
final
Annotation
annotation
:
annotatedEle
.
getAnnotations
())
{
if
(
AnnotationUtil
.
isNotJdkMateAnnotation
(
annotation
.
annotationType
())
&&
filter
.
test
(
annotation
))
{
consumer
.
accept
(
0
,
annotation
);
}
}
}
}
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/GenericAnnotationScanner.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.annotation.scanner
;
import
cn.hutool.core.map.multi.ListValueMap
;
import
cn.hutool.core.util.ObjectUtil
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
java.lang.reflect.Method
;
import
java.util.Collections
;
import
java.util.LinkedHashMap
;
import
java.util.function.BiConsumer
;
import
java.util.function.Predicate
;
/**
* <p>通用注解扫描器,支持按不同的层级结构扫描{@link AnnotatedElement}上的注解。
*
* <p>当{@link AnnotatedElement}类型不同时,“层级结构”指向的对象将有所区别:
* <ul>
* <li>
* 当元素为{@link Method}时,此处层级结构指声明方法的类的层级结构,
* 扫描器将从层级结构中寻找与该方法签名相同的方法,并对其进行扫描;
* </li>
* <li>
* 当元素为{@link Class}时,此处层级结构即指类本身与其父类、父接口共同构成的层级结构,
* 扫描器将扫描层级结构中类、接口声明的注解;
* </li>
* <li>当元素不为{@link Method}或{@link Class}时,则其层级结构仅有其本身一层;</li>
* </ul>
* 此外,扫描器支持在获取到层级结构中的注解对象后,再对注解对象的元注解进行扫描。
*
* @author huangchengxing
* @see TypeAnnotationScanner
* @see MethodAnnotationScanner
* @see MetaAnnotationScanner
* @see ElementAnnotationScanner
*/
public
class
GenericAnnotationScanner
implements
AnnotationScanner
{
/**
* 类型扫描器
*/
private
final
AnnotationScanner
typeScanner
;
/**
* 方法扫描器
*/
private
final
AnnotationScanner
methodScanner
;
/**
* 元注解扫描器
*/
private
final
AnnotationScanner
metaScanner
;
/**
* 普通元素扫描器
*/
private
final
AnnotationScanner
elementScanner
;
/**
* 通用注解扫描器支持扫描所有类型的{@link AnnotatedElement}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public
boolean
support
(
AnnotatedElement
annotatedEle
)
{
return
true
;
}
/**
* 构造一个通用注解扫描器
*
* @param enableScanMetaAnnotation 是否扫描注解上的元注解
* @param enableScanSupperClass 是否扫描父类
* @param enableScanSupperInterface 是否扫描父接口
*/
public
GenericAnnotationScanner
(
boolean
enableScanMetaAnnotation
,
boolean
enableScanSupperClass
,
boolean
enableScanSupperInterface
)
{
this
.
metaScanner
=
enableScanMetaAnnotation
?
new
MetaAnnotationScanner
()
:
new
EmptyAnnotationScanner
();
this
.
typeScanner
=
new
TypeAnnotationScanner
(
enableScanSupperClass
,
enableScanSupperInterface
,
a
->
true
,
Collections
.
emptySet
()
);
this
.
methodScanner
=
new
MethodAnnotationScanner
(
enableScanSupperClass
,
enableScanSupperInterface
,
a
->
true
,
Collections
.
emptySet
()
);
this
.
elementScanner
=
new
ElementAnnotationScanner
();
}
/**
* 扫描注解元素的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理
*
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
@Override
public
void
scan
(
BiConsumer
<
Integer
,
Annotation
>
consumer
,
AnnotatedElement
annotatedEle
,
Predicate
<
Annotation
>
filter
)
{
filter
=
ObjectUtil
.
defaultIfNull
(
filter
,
a
->
t
->
true
);
if
(
ObjectUtil
.
isNull
(
annotatedEle
))
{
return
;
}
// 注解元素是类
if
(
annotatedEle
instanceof
Class
)
{
scanElements
(
typeScanner
,
consumer
,
annotatedEle
,
filter
);
}
// 注解元素是方法
else
if
(
annotatedEle
instanceof
Method
)
{
scanElements
(
methodScanner
,
consumer
,
annotatedEle
,
filter
);
}
// 注解元素是其他类型
else
{
scanElements
(
elementScanner
,
consumer
,
annotatedEle
,
filter
);
}
}
/**
* 扫描注解类的层级结构(若存在),然后对获取到的注解和注解对应的层级索引进行处理
*
* @param scanner 使用的扫描器
* @param consumer 对获取到的注解和注解对应的层级索引的处理
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 注解过滤器,无法通过过滤器的注解不会被处理。该参数允许为空。
*/
private
void
scanElements
(
AnnotationScanner
scanner
,
BiConsumer
<
Integer
,
Annotation
>
consumer
,
AnnotatedElement
annotatedEle
,
Predicate
<
Annotation
>
filter
)
{
// 扫描类上注解
final
ListValueMap
<
Integer
,
Annotation
>
classAnnotations
=
new
ListValueMap
<>(
new
LinkedHashMap
<>());
scanner
.
scan
((
index
,
annotation
)
->
{
if
(
filter
.
test
(
annotation
))
{
classAnnotations
.
putValue
(
index
,
annotation
);
}
},
annotatedEle
,
filter
);
// 扫描元注解
classAnnotations
.
forEach
((
index
,
annotations
)
->
annotations
.
forEach
(
annotation
->
{
consumer
.
accept
(
index
,
annotation
);
metaScanner
.
scan
(
consumer
,
annotation
.
annotationType
(),
filter
);
})
);
}
}
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MetaAnnotationScanner.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.annotation.scanner
;
import
cn.hutool.core.annotation.AnnotationUtil
;
import
cn.hutool.core.collection.CollUtil
;
import
cn.hutool.core.util.ClassUtil
;
import
cn.hutool.core.util.ObjectUtil
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
java.util.*
;
import
java.util.function.BiConsumer
;
import
java.util.function.Predicate
;
import
java.util.stream.Collectors
;
import
java.util.stream.Stream
;
/**
* 扫描注解类上存在的注解,支持处理枚举实例或枚举类型
* 需要注意,当待解析是枚举类时,有可能与{@link TypeAnnotationScanner}冲突
*
* @author huangchengxing
* @see TypeAnnotationScanner
*/
public
class
MetaAnnotationScanner
implements
AnnotationScanner
{
/**
* 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
*/
private
final
boolean
includeSupperMetaAnnotation
;
/**
* 构造一个元注解扫描器
*
* @param includeSupperMetaAnnotation 获取当前注解的元注解后,是否继续递归扫描的元注解的元注解
*/
public
MetaAnnotationScanner
(
boolean
includeSupperMetaAnnotation
)
{
this
.
includeSupperMetaAnnotation
=
includeSupperMetaAnnotation
;
}
/**
* 构造一个元注解扫描器,默认在扫描当前注解上的元注解后,并继续递归扫描元注解
*/
public
MetaAnnotationScanner
()
{
this
(
true
);
}
/**
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Annotation}接口的子类{@link Class}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public
boolean
support
(
AnnotatedElement
annotatedEle
)
{
return
(
annotatedEle
instanceof
Class
&&
ClassUtil
.
isAssignable
(
Annotation
.
class
,
(
Class
<?>)
annotatedEle
));
}
/**
* 获取注解元素上的全部注解。调用该方法前,需要确保调用{@link #support(AnnotatedElement)}返回为true
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 注解
*/
@Override
public
List
<
Annotation
>
getAnnotations
(
AnnotatedElement
annotatedEle
)
{
final
List
<
Annotation
>
annotations
=
new
ArrayList
<>();
scan
(
(
index
,
annotation
)
->
annotations
.
add
(
annotation
),
annotatedEle
,
annotation
->
ObjectUtil
.
notEqual
(
annotation
,
annotatedEle
)
);
return
annotations
;
}
/**
* 按广度优先扫描指定注解上的元注解,对扫描到的注解与层级索引进行操作
*
* @param consumer 当前层级索引与操作
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @param filter 过滤器
*/
@SuppressWarnings
(
"unchecked"
)
@Override
public
void
scan
(
BiConsumer
<
Integer
,
Annotation
>
consumer
,
AnnotatedElement
annotatedEle
,
Predicate
<
Annotation
>
filter
)
{
filter
=
ObjectUtil
.
defaultIfNull
(
filter
,
a
->
t
->
true
);
Set
<
Class
<?
extends
Annotation
>>
accessed
=
new
HashSet
<>();
final
Deque
<
List
<
Class
<?
extends
Annotation
>>>
deque
=
CollUtil
.
newLinkedList
(
CollUtil
.
newArrayList
((
Class
<?
extends
Annotation
>)
annotatedEle
));
int
distance
=
0
;
do
{
final
List
<
Class
<?
extends
Annotation
>>
annotationTypes
=
deque
.
removeFirst
();
for
(
final
Class
<?
extends
Annotation
>
type
:
annotationTypes
)
{
final
List
<
Annotation
>
metaAnnotations
=
Stream
.
of
(
type
.
getAnnotations
())
.
filter
(
a
->
!
AnnotationUtil
.
isJdkMetaAnnotation
(
a
.
annotationType
()))
.
filter
(
filter
)
.
collect
(
Collectors
.
toList
());
for
(
final
Annotation
metaAnnotation
:
metaAnnotations
)
{
consumer
.
accept
(
distance
,
metaAnnotation
);
}
accessed
.
add
(
type
);
List
<
Class
<?
extends
Annotation
>>
next
=
metaAnnotations
.
stream
()
.
map
(
Annotation:
:
annotationType
)
.
filter
(
t
->
!
accessed
.
contains
(
t
))
.
collect
(
Collectors
.
toList
());
if
(
CollUtil
.
isNotEmpty
(
next
))
{
deque
.
addLast
(
next
);
}
}
distance
++;
}
while
(
includeSupperMetaAnnotation
&&
!
deque
.
isEmpty
());
}
}
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/MethodAnnotationScanner.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.annotation.scanner
;
import
cn.hutool.core.collection.CollUtil
;
import
cn.hutool.core.util.ArrayUtil
;
import
cn.hutool.core.util.ClassUtil
;
import
cn.hutool.core.util.StrUtil
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
java.lang.reflect.Method
;
import
java.util.Set
;
import
java.util.function.Predicate
;
import
java.util.stream.Stream
;
/**
* 扫描{@link Method}上的注解
*
* @author huangchengxing
*/
public
class
MethodAnnotationScanner
extends
AbstractTypeAnnotationScanner
<
MethodAnnotationScanner
>
implements
AnnotationScanner
{
/**
* 构造一个类注解扫描器,仅扫描该方法上直接声明的注解
*/
public
MethodAnnotationScanner
()
{
this
(
false
);
}
/**
* 构造一个类注解扫描器
*
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
*/
public
MethodAnnotationScanner
(
boolean
scanSameSignatureMethod
)
{
this
(
scanSameSignatureMethod
,
targetClass
->
true
,
CollUtil
.
newLinkedHashSet
());
}
/**
* 构造一个方法注解扫描器
*
* @param scanSameSignatureMethod 是否扫描类层级结构中具有相同方法签名的方法
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
public
MethodAnnotationScanner
(
boolean
scanSameSignatureMethod
,
Predicate
<
Class
<?>>
filter
,
Set
<
Class
<?>>
excludeTypes
)
{
super
(
scanSameSignatureMethod
,
scanSameSignatureMethod
,
filter
,
excludeTypes
);
}
/**
* 构造一个方法注解扫描器
*
* @param includeSuperClass 是否允许扫描父类中具有相同方法签名的方法
* @param includeInterfaces 是否允许扫描父接口中具有相同方法签名的方法
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
public
MethodAnnotationScanner
(
boolean
includeSuperClass
,
boolean
includeInterfaces
,
Predicate
<
Class
<?>>
filter
,
Set
<
Class
<?>>
excludeTypes
)
{
super
(
includeSuperClass
,
includeInterfaces
,
filter
,
excludeTypes
);
}
/**
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Method}时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return boolean 是否支持扫描该注解元素
*/
@Override
public
boolean
support
(
AnnotatedElement
annotatedEle
)
{
return
annotatedEle
instanceof
Method
;
}
/**
* 获取声明该方法的类
*
* @param annotatedElement 注解元素
* @return 要递归的类型
* @see Method#getDeclaringClass()
*/
@Override
protected
Class
<?>
getClassFormAnnotatedElement
(
AnnotatedElement
annotatedElement
)
{
return
((
Method
)
annotatedElement
).
getDeclaringClass
();
}
/**
* 若父类/父接口中方法具有相同的方法签名,则返回该方法上的注解
*
* @param source 原始方法
* @param index 类的层级索引
* @param targetClass 类
* @return 最终所需的目标注解
*/
@Override
protected
Annotation
[]
getAnnotationsFromTargetClass
(
AnnotatedElement
source
,
int
index
,
Class
<?>
targetClass
)
{
final
Method
sourceMethod
=
(
Method
)
source
;
return
Stream
.
of
(
ClassUtil
.
getDeclaredMethods
(
targetClass
))
.
filter
(
superMethod
->
!
superMethod
.
isBridge
())
.
filter
(
superMethod
->
hasSameSignature
(
sourceMethod
,
superMethod
))
.
map
(
AnnotatedElement:
:
getAnnotations
)
.
flatMap
(
Stream:
:
of
)
.
toArray
(
Annotation
[]::
new
);
}
/**
* 设置是否扫描类层级结构中具有相同方法签名的方法
*
* @param scanSuperMethodIfOverride 是否扫描类层级结构中具有相同方法签名的方法
* @return 当前实例
*/
public
MethodAnnotationScanner
setScanSameSignatureMethod
(
boolean
scanSuperMethodIfOverride
)
{
setIncludeInterfaces
(
scanSuperMethodIfOverride
);
setIncludeSuperClass
(
scanSuperMethodIfOverride
);
return
this
;
}
/**
* 该方法是否具备与扫描的方法相同的方法签名
*/
private
boolean
hasSameSignature
(
Method
sourceMethod
,
Method
superMethod
)
{
if
(
false
==
StrUtil
.
equals
(
sourceMethod
.
getName
(),
superMethod
.
getName
()))
{
return
false
;
}
final
Class
<?>[]
sourceParameterTypes
=
sourceMethod
.
getParameterTypes
();
final
Class
<?>[]
targetParameterTypes
=
superMethod
.
getParameterTypes
();
if
(
sourceParameterTypes
.
length
!=
targetParameterTypes
.
length
)
{
return
false
;
}
if
(!
ArrayUtil
.
containsAll
(
sourceParameterTypes
,
targetParameterTypes
))
{
return
false
;
}
return
ClassUtil
.
isAssignable
(
superMethod
.
getReturnType
(),
sourceMethod
.
getReturnType
());
}
}
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/TypeAnnotationScanner.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.annotation.scanner
;
import
cn.hutool.core.collection.CollUtil
;
import
java.lang.annotation.Annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
java.lang.reflect.Proxy
;
import
java.util.Set
;
import
java.util.function.Predicate
;
import
java.util.function.UnaryOperator
;
/**
* 扫描{@link Class}上的注解
*
* @author huangchengxing
*/
public
class
TypeAnnotationScanner
extends
AbstractTypeAnnotationScanner
<
TypeAnnotationScanner
>
implements
AnnotationScanner
{
/**
* 构造一个类注解扫描器
*
* @param includeSupperClass 是否允许扫描父类
* @param includeInterfaces 是否允许扫描父接口
* @param filter 过滤器
* @param excludeTypes 不包含的类型
*/
public
TypeAnnotationScanner
(
boolean
includeSupperClass
,
boolean
includeInterfaces
,
Predicate
<
Class
<?>>
filter
,
Set
<
Class
<?>>
excludeTypes
)
{
super
(
includeSupperClass
,
includeInterfaces
,
filter
,
excludeTypes
);
}
/**
* 构建一个类注解扫描器,默认允许扫描指定元素的父类以及父接口
*/
public
TypeAnnotationScanner
()
{
this
(
true
,
true
,
t
->
true
,
CollUtil
.
newLinkedHashSet
());
}
/**
* 判断是否支持扫描该注解元素,仅当注解元素是{@link Class}接时返回{@code true}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 是否支持扫描该注解元素
*/
@Override
public
boolean
support
(
AnnotatedElement
annotatedEle
)
{
return
annotatedEle
instanceof
Class
;
}
/**
* 将注解元素转为{@link Class}
*
* @param annotatedEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
* @return 要递归的类型
*/
@Override
protected
Class
<?>
getClassFormAnnotatedElement
(
AnnotatedElement
annotatedEle
)
{
return
(
Class
<?>)
annotatedEle
;
}
/**
* 获取{@link Class#getAnnotations()}
*
* @param source 最初的注解元素
* @param index 类的层级索引
* @param targetClass 类
* @return 类上直接声明的注解
*/
@Override
protected
Annotation
[]
getAnnotationsFromTargetClass
(
AnnotatedElement
source
,
int
index
,
Class
<?>
targetClass
)
{
return
targetClass
.
getAnnotations
();
}
/**
* 是否允许扫描父类
*
* @param includeSuperClass 是否允许扫描父类
* @return 当前实例
*/
@Override
public
TypeAnnotationScanner
setIncludeSuperClass
(
boolean
includeSuperClass
)
{
return
super
.
setIncludeSuperClass
(
includeSuperClass
);
}
/**
* 是否允许扫描父接口
*
* @param includeInterfaces 是否允许扫描父类
* @return 当前实例
*/
@Override
public
TypeAnnotationScanner
setIncludeInterfaces
(
boolean
includeInterfaces
)
{
return
super
.
setIncludeInterfaces
(
includeInterfaces
);
}
/**
* 若类型为jdk代理类,则尝试转换为原始被代理类
*/
public
static
class
JdkProxyClassConverter
implements
UnaryOperator
<
Class
<?>>
{
@Override
public
Class
<?>
apply
(
Class
<?>
sourceClass
)
{
return
Proxy
.
isProxyClass
(
sourceClass
)
?
apply
(
sourceClass
.
getSuperclass
())
:
sourceClass
;
}
}
}
hutool-core/src/main/java/cn/hutool/core/annotation/scanner/package-info.java
0 → 100644
View file @
45cda665
/**
* 注解包扫描封装
*
* @author looly
*
*/
package
cn.hutool.core.annotation.scanner
;
hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
import
cn.hutool.core.lang.Assert
;
import
cn.hutool.core.map.CaseInsensitiveMap
;
import
cn.hutool.core.util.BooleanUtil
;
import
cn.hutool.core.util.ModifierUtil
;
import
cn.hutool.core.util.ReflectUtil
;
import
cn.hutool.core.util.StrUtil
;
import
java.io.Serializable
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.util.Collection
;
import
java.util.LinkedHashMap
;
import
java.util.Map
;
/**
* Bean信息描述做为BeanInfo替代方案,此对象持有JavaBean中的setters和getters等相关信息描述<br>
* 查找Getter和Setter方法时会:
*
* <pre>
* 1. 忽略字段和方法名的大小写
* 2. Getter查找getXXX、isXXX、getIsXXX
* 3. Setter查找setXXX、setIsXXX
* 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
* </pre>
*
* @author looly
* @since 3.1.2
*/
public
class
BeanDesc
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* Bean类
*/
private
final
Class
<?>
beanClass
;
/**
* 属性Map
*/
private
final
Map
<
String
,
PropDesc
>
propMap
=
new
LinkedHashMap
<>();
/**
* 构造
*
* @param beanClass Bean类
*/
public
BeanDesc
(
Class
<?>
beanClass
)
{
Assert
.
notNull
(
beanClass
);
this
.
beanClass
=
beanClass
;
init
();
}
/**
* 获取Bean的全类名
*
* @return Bean的类名
*/
public
String
getName
()
{
return
this
.
beanClass
.
getName
();
}
/**
* 获取Bean的简单类名
*
* @return Bean的类名
*/
public
String
getSimpleName
()
{
return
this
.
beanClass
.
getSimpleName
();
}
/**
* 获取字段名-字段属性Map
*
* @param ignoreCase 是否忽略大小写,true为忽略,false不忽略
* @return 字段名-字段属性Map
*/
public
Map
<
String
,
PropDesc
>
getPropMap
(
boolean
ignoreCase
)
{
return
ignoreCase
?
new
CaseInsensitiveMap
<>(
1
,
this
.
propMap
)
:
this
.
propMap
;
}
/**
* 获取字段属性列表
*
* @return {@link PropDesc} 列表
*/
public
Collection
<
PropDesc
>
getProps
()
{
return
this
.
propMap
.
values
();
}
/**
* 获取属性,如果不存在返回null
*
* @param fieldName 字段名
* @return {@link PropDesc}
*/
public
PropDesc
getProp
(
String
fieldName
)
{
return
this
.
propMap
.
get
(
fieldName
);
}
/**
* 获得字段名对应的字段对象,如果不存在返回null
*
* @param fieldName 字段名
* @return 字段值
*/
public
Field
getField
(
String
fieldName
)
{
final
PropDesc
desc
=
this
.
propMap
.
get
(
fieldName
);
return
null
==
desc
?
null
:
desc
.
getField
();
}
/**
* 获取Getter方法,如果不存在返回null
*
* @param fieldName 字段名
* @return Getter方法
*/
public
Method
getGetter
(
String
fieldName
)
{
final
PropDesc
desc
=
this
.
propMap
.
get
(
fieldName
);
return
null
==
desc
?
null
:
desc
.
getGetter
();
}
/**
* 获取Setter方法,如果不存在返回null
*
* @param fieldName 字段名
* @return Setter方法
*/
public
Method
getSetter
(
String
fieldName
)
{
final
PropDesc
desc
=
this
.
propMap
.
get
(
fieldName
);
return
null
==
desc
?
null
:
desc
.
getSetter
();
}
// ------------------------------------------------------------------------------------------------------ Private method start
/**
* 初始化<br>
* 只有与属性关联的相关Getter和Setter方法才会被读取,无关的getXXX和setXXX都被忽略
*
* @return this
*/
private
BeanDesc
init
()
{
final
Method
[]
gettersAndSetters
=
ReflectUtil
.
getMethods
(
this
.
beanClass
,
ReflectUtil:
:
isGetterOrSetterIgnoreCase
);
PropDesc
prop
;
for
(
Field
field
:
ReflectUtil
.
getFields
(
this
.
beanClass
))
{
// 排除静态属性和对象子类
if
(
false
==
ModifierUtil
.
isStatic
(
field
)
&&
false
==
ReflectUtil
.
isOuterClassField
(
field
))
{
prop
=
createProp
(
field
,
gettersAndSetters
);
// 只有不存在时才放入,防止父类属性覆盖子类属性
this
.
propMap
.
putIfAbsent
(
prop
.
getFieldName
(),
prop
);
}
}
return
this
;
}
/**
* 根据字段创建属性描述<br>
* 查找Getter和Setter方法时会:
*
* <pre>
* 1. 忽略字段和方法名的大小写
* 2. Getter查找getXXX、isXXX、getIsXXX
* 3. Setter查找setXXX、setIsXXX
* 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
* </pre>
*
* @param field 字段
* @param methods 类中所有的方法
* @return {@link PropDesc}
* @since 4.0.2
*/
private
PropDesc
createProp
(
Field
field
,
Method
[]
methods
)
{
final
PropDesc
prop
=
findProp
(
field
,
methods
,
false
);
// 忽略大小写重新匹配一次
if
(
null
==
prop
.
getter
||
null
==
prop
.
setter
)
{
final
PropDesc
propIgnoreCase
=
findProp
(
field
,
methods
,
true
);
if
(
null
==
prop
.
getter
)
{
prop
.
getter
=
propIgnoreCase
.
getter
;
}
if
(
null
==
prop
.
setter
)
{
prop
.
setter
=
propIgnoreCase
.
setter
;
}
}
return
prop
;
}
/**
* 查找字段对应的Getter和Setter方法
*
* @param field 字段
* @param gettersOrSetters 类中所有的Getter或Setter方法
* @param ignoreCase 是否忽略大小写匹配
* @return PropDesc
*/
private
PropDesc
findProp
(
Field
field
,
Method
[]
gettersOrSetters
,
boolean
ignoreCase
)
{
final
String
fieldName
=
field
.
getName
();
final
Class
<?>
fieldType
=
field
.
getType
();
final
boolean
isBooleanField
=
BooleanUtil
.
isBoolean
(
fieldType
);
Method
getter
=
null
;
Method
setter
=
null
;
String
methodName
;
for
(
Method
method
:
gettersOrSetters
)
{
methodName
=
method
.
getName
();
if
(
method
.
getParameterCount
()
==
0
)
{
// 无参数,可能为Getter方法
if
(
isMatchGetter
(
methodName
,
fieldName
,
isBooleanField
,
ignoreCase
))
{
// 方法名与字段名匹配,则为Getter方法
getter
=
method
;
}
}
else
if
(
isMatchSetter
(
methodName
,
fieldName
,
isBooleanField
,
ignoreCase
))
{
// setter方法的参数类型和字段类型必须一致,或参数类型是字段类型的子类
if
(
fieldType
.
isAssignableFrom
(
method
.
getParameterTypes
()[
0
])){
setter
=
method
;
}
}
if
(
null
!=
getter
&&
null
!=
setter
)
{
// 如果Getter和Setter方法都找到了,不再继续寻找
break
;
}
}
return
new
PropDesc
(
field
,
getter
,
setter
);
}
/**
* 方法是否为Getter方法<br>
* 匹配规则如下(忽略大小写):
*
* <pre>
* 字段名 -》 方法名
* isName -》 isName
* isName -》 isIsName
* isName -》 getIsName
* name -》 isName
* name -》 getName
* </pre>
*
* @param methodName 方法名
* @param fieldName 字段名
* @param isBooleanField 是否为Boolean类型字段
* @param ignoreCase 匹配是否忽略大小写
* @return 是否匹配
*/
private
boolean
isMatchGetter
(
String
methodName
,
String
fieldName
,
boolean
isBooleanField
,
boolean
ignoreCase
)
{
final
String
handledFieldName
;
if
(
ignoreCase
)
{
// 全部转为小写,忽略大小写比较
methodName
=
methodName
.
toLowerCase
();
handledFieldName
=
fieldName
.
toLowerCase
();
fieldName
=
handledFieldName
;
}
else
{
handledFieldName
=
StrUtil
.
upperFirst
(
fieldName
);
}
// 针对Boolean类型特殊检查
if
(
isBooleanField
)
{
if
(
fieldName
.
startsWith
(
"is"
))
{
// 字段已经是is开头
if
(
methodName
.
equals
(
fieldName
)
// isName -》 isName
||
(
"get"
+
handledFieldName
).
equals
(
methodName
)
// isName -》 getIsName
||
(
"is"
+
handledFieldName
).
equals
(
methodName
)
// isName -》 isIsName
)
{
return
true
;
}
}
else
if
((
"is"
+
handledFieldName
).
equals
(
methodName
))
{
// 字段非is开头, name -》 isName
return
true
;
}
}
// 包括boolean的任何类型只有一种匹配情况:name -》 getName
return
(
"get"
+
handledFieldName
).
equals
(
methodName
);
}
/**
* 方法是否为Setter方法<br>
* 匹配规则如下(忽略大小写):
*
* <pre>
* 字段名 -》 方法名
* isName -》 setName
* isName -》 setIsName
* name -》 setName
* </pre>
*
* @param methodName 方法名
* @param fieldName 字段名
* @param isBooleanField 是否为Boolean类型字段
* @param ignoreCase 匹配是否忽略大小写
* @return 是否匹配
*/
private
boolean
isMatchSetter
(
String
methodName
,
String
fieldName
,
boolean
isBooleanField
,
boolean
ignoreCase
)
{
final
String
handledFieldName
;
if
(
ignoreCase
)
{
// 全部转为小写,忽略大小写比较
methodName
=
methodName
.
toLowerCase
();
handledFieldName
=
fieldName
.
toLowerCase
();
fieldName
=
handledFieldName
;
}
else
{
handledFieldName
=
StrUtil
.
upperFirst
(
fieldName
);
}
// 非标准Setter方法跳过
if
(
false
==
methodName
.
startsWith
(
"set"
))
{
return
false
;
}
// 针对Boolean类型特殊检查
if
(
isBooleanField
&&
fieldName
.
startsWith
(
"is"
))
{
// 字段是is开头
if
((
"set"
+
StrUtil
.
removePrefix
(
fieldName
,
"is"
)).
equals
(
methodName
)
// isName -》 setName
||
(
"set"
+
handledFieldName
).
equals
(
methodName
)
// isName -》 setIsName
)
{
return
true
;
}
}
// 包括boolean的任何类型只有一种匹配情况:name -》 setName
return
(
"set"
+
handledFieldName
).
equals
(
methodName
);
}
// ------------------------------------------------------------------------------------------------------ Private method end
}
hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
import
cn.hutool.core.lang.func.Func0
;
import
cn.hutool.core.map.WeakConcurrentMap
;
/**
* Bean属性缓存<br>
* 缓存用于防止多次反射造成的性能问题
*
* @author Looly
*/
public
enum
BeanDescCache
{
INSTANCE
;
private
final
WeakConcurrentMap
<
Class
<?>,
BeanDesc
>
bdCache
=
new
WeakConcurrentMap
<>();
/**
* 获得属性名和{@link BeanDesc}Map映射
*
* @param beanClass Bean的类
* @param supplier 对象不存在时创建对象的函数
* @return 属性名和{@link BeanDesc}映射
* @since 5.4.2
*/
public
BeanDesc
getBeanDesc
(
Class
<?>
beanClass
,
Func0
<
BeanDesc
>
supplier
)
{
return
bdCache
.
computeIfAbsent
(
beanClass
,
(
key
)->
supplier
.
callWithRuntimeException
());
}
/**
* 清空全局的Bean属性缓存
*
* @since 5.7.21
*/
public
void
clear
()
{
this
.
bdCache
.
clear
();
}
}
hutool-core/src/main/java/cn/hutool/core/bean/BeanException.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
import
cn.hutool.core.exceptions.ExceptionUtil
;
import
cn.hutool.core.util.StrUtil
;
/**
* Bean异常
* @author xiaoleilu
*/
public
class
BeanException
extends
RuntimeException
{
private
static
final
long
serialVersionUID
=
-
8096998667745023423L
;
public
BeanException
(
Throwable
e
)
{
super
(
ExceptionUtil
.
getMessage
(
e
),
e
);
}
public
BeanException
(
String
message
)
{
super
(
message
);
}
public
BeanException
(
String
messageTemplate
,
Object
...
params
)
{
super
(
StrUtil
.
format
(
messageTemplate
,
params
));
}
public
BeanException
(
String
message
,
Throwable
throwable
)
{
super
(
message
,
throwable
);
}
public
BeanException
(
Throwable
throwable
,
String
messageTemplate
,
Object
...
params
)
{
super
(
StrUtil
.
format
(
messageTemplate
,
params
),
throwable
);
}
}
hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
import
cn.hutool.core.lang.func.Func0
;
import
cn.hutool.core.map.ReferenceConcurrentMap
;
import
cn.hutool.core.map.WeakConcurrentMap
;
import
java.beans.PropertyDescriptor
;
import
java.util.Map
;
/**
* Bean属性缓存<br>
* 缓存用于防止多次反射造成的性能问题
*
* @author Looly
*/
public
enum
BeanInfoCache
{
INSTANCE
;
private
final
WeakConcurrentMap
<
Class
<?>,
Map
<
String
,
PropertyDescriptor
>>
pdCache
=
new
WeakConcurrentMap
<>();
private
final
WeakConcurrentMap
<
Class
<?>,
Map
<
String
,
PropertyDescriptor
>>
ignoreCasePdCache
=
new
WeakConcurrentMap
<>();
/**
* 获得属性名和{@link PropertyDescriptor}Map映射
*
* @param beanClass Bean的类
* @param ignoreCase 是否忽略大小写
* @return 属性名和{@link PropertyDescriptor}Map映射
*/
public
Map
<
String
,
PropertyDescriptor
>
getPropertyDescriptorMap
(
Class
<?>
beanClass
,
boolean
ignoreCase
)
{
return
getCache
(
ignoreCase
).
get
(
beanClass
);
}
/**
* 获得属性名和{@link PropertyDescriptor}Map映射
*
* @param beanClass Bean的类
* @param ignoreCase 是否忽略大小写
* @param supplier 缓存对象产生函数
* @return 属性名和{@link PropertyDescriptor}Map映射
* @since 5.4.1
*/
public
Map
<
String
,
PropertyDescriptor
>
getPropertyDescriptorMap
(
Class
<?>
beanClass
,
boolean
ignoreCase
,
Func0
<
Map
<
String
,
PropertyDescriptor
>>
supplier
)
{
return
getCache
(
ignoreCase
).
computeIfAbsent
(
beanClass
,
(
key
)->
supplier
.
callWithRuntimeException
());
}
/**
* 加入缓存
*
* @param beanClass Bean的类
* @param fieldNamePropertyDescriptorMap 属性名和{@link PropertyDescriptor}Map映射
* @param ignoreCase 是否忽略大小写
*/
public
void
putPropertyDescriptorMap
(
Class
<?>
beanClass
,
Map
<
String
,
PropertyDescriptor
>
fieldNamePropertyDescriptorMap
,
boolean
ignoreCase
)
{
getCache
(
ignoreCase
).
put
(
beanClass
,
fieldNamePropertyDescriptorMap
);
}
/**
* 清空缓存
*
* @since 5.7.21
*/
public
void
clear
()
{
this
.
pdCache
.
clear
();
this
.
ignoreCasePdCache
.
clear
();
}
/**
* 根据是否忽略字段名的大小写,返回不用Cache对象
*
* @param ignoreCase 是否忽略大小写
* @return {@link ReferenceConcurrentMap}
* @since 5.4.1
*/
private
ReferenceConcurrentMap
<
Class
<?>,
Map
<
String
,
PropertyDescriptor
>>
getCache
(
boolean
ignoreCase
)
{
return
ignoreCase
?
ignoreCasePdCache
:
pdCache
;
}
}
hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
import
cn.hutool.core.collection.CollUtil
;
import
cn.hutool.core.collection.ListUtil
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.map.MapUtil
;
import
cn.hutool.core.util.ArrayUtil
;
import
cn.hutool.core.util.CharUtil
;
import
cn.hutool.core.util.NumberUtil
;
import
cn.hutool.core.util.StrUtil
;
import
java.io.Serializable
;
import
java.util.*
;
/**
* Bean路径表达式,用于获取多层嵌套Bean中的字段值或Bean对象<br>
* 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
* <ol>
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
* </ol>
* <p>
* 表达式栗子:
*
* <pre>
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
* </pre>
*
* @author Looly
* @since 4.0.6
*/
public
class
BeanPath
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 表达式边界符号数组
*/
private
static
final
char
[]
EXP_CHARS
=
{
CharUtil
.
DOT
,
CharUtil
.
BRACKET_START
,
CharUtil
.
BRACKET_END
};
private
boolean
isStartWith
=
false
;
protected
List
<
String
>
patternParts
;
/**
* 解析Bean路径表达式为Bean模式<br>
* Bean表达式,用于获取多层嵌套Bean中的字段值或Bean对象<br>
* 根据给定的表达式,查找Bean中对应的属性值对象。 表达式分为两种:
* <ol>
* <li>.表达式,可以获取Bean对象中的属性(字段)值或者Map中key对应的值</li>
* <li>[]表达式,可以获取集合等对象中对应index的值</li>
* </ol>
* <p>
* 表达式栗子:
*
* <pre>
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
* </pre>
*
* @param expression 表达式
* @return BeanPath
*/
public
static
BeanPath
create
(
final
String
expression
)
{
return
new
BeanPath
(
expression
);
}
/**
* 构造
*
* @param expression 表达式
*/
public
BeanPath
(
final
String
expression
)
{
init
(
expression
);
}
/**
* 获取表达式解析后的分段列表
*
* @return 表达式分段列表
*/
public
List
<
String
>
getPatternParts
()
{
return
this
.
patternParts
;
}
/**
* 获取Bean中对应表达式的值
*
* @param bean Bean对象或Map或List等
* @return 值,如果对应值不存在,则返回null
*/
public
Object
get
(
final
Object
bean
)
{
return
get
(
this
.
patternParts
,
bean
,
false
);
}
/**
* 设置表达式指定位置(或filed对应)的值<br>
* 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值<br>
* 注意:
*
* <pre>
* 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
* 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
* </pre>
*
* @param bean Bean、Map或List
* @param value 值
*/
public
void
set
(
final
Object
bean
,
final
Object
value
)
{
set
(
bean
,
this
.
patternParts
,
lastIsNumber
(
this
.
patternParts
),
value
);
}
@Override
public
String
toString
()
{
return
this
.
patternParts
.
toString
();
}
//region Private Methods
/**
* 设置表达式指定位置(或filed对应)的值<br>
* 若表达式指向一个List则设置其坐标对应位置的值,若指向Map则put对应key的值,Bean则设置字段的值<br>
* 注意:
*
* <pre>
* 1. 如果为List,如果下标不大于List长度,则替换原有值,否则追加值
* 2. 如果为数组,如果下标不大于数组长度,则替换原有值,否则追加值
* </pre>
*
* @param bean Bean、Map或List
* @param patternParts 表达式块列表
* @param nextNumberPart 下一个值是否
* @param value 值
*/
private
void
set
(
Object
bean
,
List
<
String
>
patternParts
,
boolean
nextNumberPart
,
Object
value
)
{
Object
subBean
=
this
.
get
(
patternParts
,
bean
,
true
);
if
(
null
==
subBean
)
{
// 当前节点是空,则先创建父节点
final
List
<
String
>
parentParts
=
getParentParts
(
patternParts
);
this
.
set
(
bean
,
parentParts
,
lastIsNumber
(
parentParts
),
nextNumberPart
?
new
ArrayList
<>()
:
new
HashMap
<>());
//set中有可能做过转换,因此此处重新获取bean
subBean
=
this
.
get
(
patternParts
,
bean
,
true
);
}
final
Object
newSubBean
=
BeanUtil
.
setFieldValue
(
subBean
,
patternParts
.
get
(
patternParts
.
size
()
-
1
),
value
);
if
(
newSubBean
!=
subBean
){
// 对象变更,重新加入
this
.
set
(
bean
,
getParentParts
(
patternParts
),
nextNumberPart
,
newSubBean
);
}
}
/**
* 判断path列表中末尾的标记是否为数字
*
* @param patternParts path列表
* @return 是否为数字
*/
private
static
boolean
lastIsNumber
(
List
<
String
>
patternParts
)
{
return
NumberUtil
.
isInteger
(
patternParts
.
get
(
patternParts
.
size
()
-
1
));
}
/**
* 获取父级路径列表
*
* @param patternParts 路径列表
* @return 父级路径列表
*/
private
static
List
<
String
>
getParentParts
(
List
<
String
>
patternParts
)
{
return
patternParts
.
subList
(
0
,
patternParts
.
size
()
-
1
);
}
/**
* 获取Bean中对应表达式的值
*
* @param patternParts 表达式分段列表
* @param bean Bean对象或Map或List等
* @param ignoreLast 是否忽略最后一个值,忽略最后一个值则用于set,否则用于read
* @return 值,如果对应值不存在,则返回null
*/
private
Object
get
(
final
List
<
String
>
patternParts
,
final
Object
bean
,
final
boolean
ignoreLast
)
{
int
length
=
patternParts
.
size
();
if
(
ignoreLast
)
{
length
--;
}
Object
subBean
=
bean
;
boolean
isFirst
=
true
;
String
patternPart
;
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
patternPart
=
patternParts
.
get
(
i
);
subBean
=
getFieldValue
(
subBean
,
patternPart
);
if
(
null
==
subBean
)
{
// 支持表达式的第一个对象为Bean本身(若用户定义表达式$开头,则不做此操作)
if
(
isFirst
&&
false
==
this
.
isStartWith
&&
BeanUtil
.
isMatchName
(
bean
,
patternPart
,
true
))
{
subBean
=
bean
;
isFirst
=
false
;
}
else
{
return
null
;
}
}
}
return
subBean
;
}
@SuppressWarnings
(
"unchecked"
)
private
static
Object
getFieldValue
(
final
Object
bean
,
final
String
expression
)
{
if
(
StrUtil
.
isBlank
(
expression
))
{
return
null
;
}
if
(
StrUtil
.
contains
(
expression
,
':'
))
{
// [start:end:step] 模式
final
List
<
String
>
parts
=
StrUtil
.
splitTrim
(
expression
,
':'
);
final
int
start
=
Integer
.
parseInt
(
parts
.
get
(
0
));
final
int
end
=
Integer
.
parseInt
(
parts
.
get
(
1
));
int
step
=
1
;
if
(
3
==
parts
.
size
())
{
step
=
Integer
.
parseInt
(
parts
.
get
(
2
));
}
if
(
bean
instanceof
Collection
)
{
return
CollUtil
.
sub
((
Collection
<?>)
bean
,
start
,
end
,
step
);
}
else
if
(
ArrayUtil
.
isArray
(
bean
))
{
return
ArrayUtil
.
sub
(
bean
,
start
,
end
,
step
);
}
}
else
if
(
StrUtil
.
contains
(
expression
,
','
))
{
// [num0,num1,num2...]模式或者['key0','key1']模式
final
List
<
String
>
keys
=
StrUtil
.
splitTrim
(
expression
,
','
);
if
(
bean
instanceof
Collection
)
{
return
CollUtil
.
getAny
((
Collection
<?>)
bean
,
Convert
.
convert
(
int
[].
class
,
keys
));
}
else
if
(
ArrayUtil
.
isArray
(
bean
))
{
return
ArrayUtil
.
getAny
(
bean
,
Convert
.
convert
(
int
[].
class
,
keys
));
}
else
{
final
String
[]
unWrappedKeys
=
new
String
[
keys
.
size
()];
for
(
int
i
=
0
;
i
<
unWrappedKeys
.
length
;
i
++)
{
unWrappedKeys
[
i
]
=
StrUtil
.
unWrap
(
keys
.
get
(
i
),
'\''
);
}
if
(
bean
instanceof
Map
)
{
// 只支持String为key的Map
return
MapUtil
.
getAny
((
Map
<
String
,
?>)
bean
,
unWrappedKeys
);
}
else
{
final
Map
<
String
,
Object
>
map
=
BeanUtil
.
beanToMap
(
bean
);
return
MapUtil
.
getAny
(
map
,
unWrappedKeys
);
}
}
}
else
{
// 数字或普通字符串
return
BeanUtil
.
getFieldValue
(
bean
,
expression
);
}
return
null
;
}
/**
* 初始化
*
* @param expression 表达式
*/
private
void
init
(
final
String
expression
)
{
final
List
<
String
>
localPatternParts
=
new
ArrayList
<>();
final
int
length
=
expression
.
length
();
final
StringBuilder
builder
=
new
StringBuilder
();
char
c
;
boolean
isNumStart
=
false
;
// 下标标识符开始
boolean
isInWrap
=
false
;
//标识是否在引号内
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
c
=
expression
.
charAt
(
i
);
if
(
0
==
i
&&
'$'
==
c
)
{
// 忽略开头的$符,表示当前对象
isStartWith
=
true
;
continue
;
}
if
(
'\''
==
c
)
{
// 结束
isInWrap
=
(
false
==
isInWrap
);
continue
;
}
if
(
false
==
isInWrap
&&
ArrayUtil
.
contains
(
EXP_CHARS
,
c
))
{
// 处理边界符号
if
(
CharUtil
.
BRACKET_END
==
c
)
{
// 中括号(数字下标)结束
if
(
false
==
isNumStart
)
{
throw
new
IllegalArgumentException
(
StrUtil
.
format
(
"Bad expression '{}':{}, we find ']' but no '[' !"
,
expression
,
i
));
}
isNumStart
=
false
;
// 中括号结束加入下标
}
else
{
if
(
isNumStart
)
{
// 非结束中括号情况下发现起始中括号报错(中括号未关闭)
throw
new
IllegalArgumentException
(
StrUtil
.
format
(
"Bad expression '{}':{}, we find '[' but no ']' !"
,
expression
,
i
));
}
else
if
(
CharUtil
.
BRACKET_START
==
c
)
{
// 数字下标开始
isNumStart
=
true
;
}
// 每一个边界符之前的表达式是一个完整的KEY,开始处理KEY
}
if
(
builder
.
length
()
>
0
)
{
localPatternParts
.
add
(
builder
.
toString
());
}
builder
.
setLength
(
0
);
}
else
{
// 非边界符号,追加字符
builder
.
append
(
c
);
}
}
// 末尾边界符检查
if
(
isNumStart
)
{
throw
new
IllegalArgumentException
(
StrUtil
.
format
(
"Bad expression '{}':{}, we find '[' but no ']' !"
,
expression
,
length
-
1
));
}
else
{
if
(
builder
.
length
()
>
0
)
{
localPatternParts
.
add
(
builder
.
toString
());
}
}
// 不可变List
this
.
patternParts
=
ListUtil
.
unmodifiable
(
localPatternParts
);
}
//endregion
}
hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
import
cn.hutool.core.bean.copier.BeanCopier
;
import
cn.hutool.core.bean.copier.CopyOptions
;
import
cn.hutool.core.bean.copier.ValueProvider
;
import
cn.hutool.core.collection.CollUtil
;
import
cn.hutool.core.collection.ListUtil
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.lang.Editor
;
import
cn.hutool.core.map.CaseInsensitiveMap
;
import
cn.hutool.core.map.MapUtil
;
import
cn.hutool.core.util.*
;
import
java.beans.*
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.util.*
;
import
java.util.function.Consumer
;
import
java.util.function.Supplier
;
import
java.util.stream.Collectors
;
/**
* Bean工具类
*
* <p>
* 把一个拥有对属性进行set和get方法的类,我们就可以称之为JavaBean。
* </p>
*
* @author Looly
* @since 3.1.2
*/
public
class
BeanUtil
{
/**
* 判断是否为可读的Bean对象,判定方法是:
*
* <pre>
* 1、是否存在只有无参数的getXXX方法或者isXXX方法
* 2、是否存在public类型的字段
* </pre>
*
* @param clazz 待测试类
* @return 是否为可读的Bean对象
* @see #hasGetter(Class)
* @see #hasPublicField(Class)
*/
public
static
boolean
isReadableBean
(
Class
<?>
clazz
)
{
return
hasGetter
(
clazz
)
||
hasPublicField
(
clazz
);
}
/**
* 判断是否为Bean对象,判定方法是:
*
* <pre>
* 1、是否存在只有一个参数的setXXX方法
* 2、是否存在public类型的字段
* </pre>
*
* @param clazz 待测试类
* @return 是否为Bean对象
* @see #hasSetter(Class)
* @see #hasPublicField(Class)
*/
public
static
boolean
isBean
(
Class
<?>
clazz
)
{
return
hasSetter
(
clazz
)
||
hasPublicField
(
clazz
);
}
/**
* 判断是否有Setter方法<br>
* 判定方法是否存在只有一个参数的setXXX方法
*
* @param clazz 待测试类
* @return 是否为Bean对象
* @since 4.2.2
*/
public
static
boolean
hasSetter
(
Class
<?>
clazz
)
{
if
(
ClassUtil
.
isNormalClass
(
clazz
))
{
for
(
Method
method
:
clazz
.
getMethods
())
{
if
(
method
.
getParameterCount
()
==
1
&&
method
.
getName
().
startsWith
(
"set"
))
{
// 检测包含标准的setXXX方法即视为标准的JavaBean
return
true
;
}
}
}
return
false
;
}
/**
* 判断是否为Bean对象<br>
* 判定方法是否存在只有无参数的getXXX方法或者isXXX方法
*
* @param clazz 待测试类
* @return 是否为Bean对象
* @since 4.2.2
*/
public
static
boolean
hasGetter
(
Class
<?>
clazz
)
{
if
(
ClassUtil
.
isNormalClass
(
clazz
))
{
for
(
Method
method
:
clazz
.
getMethods
())
{
if
(
method
.
getParameterCount
()
==
0
)
{
final
String
name
=
method
.
getName
();
if
(
name
.
startsWith
(
"get"
)
||
name
.
startsWith
(
"is"
))
{
if
(
false
==
"getClass"
.
equals
(
name
))
{
return
true
;
}
}
}
}
}
return
false
;
}
/**
* 指定类中是否有public类型字段(static字段除外)
*
* @param clazz 待测试类
* @return 是否有public类型字段
* @since 5.1.0
*/
public
static
boolean
hasPublicField
(
Class
<?>
clazz
)
{
if
(
ClassUtil
.
isNormalClass
(
clazz
))
{
for
(
Field
field
:
clazz
.
getFields
())
{
if
(
ModifierUtil
.
isPublic
(
field
)
&&
false
==
ModifierUtil
.
isStatic
(
field
))
{
//非static的public字段
return
true
;
}
}
}
return
false
;
}
/**
* 创建动态Bean
*
* @param bean 普通Bean或Map
* @return {@link DynaBean}
* @since 3.0.7
*/
public
static
DynaBean
createDynaBean
(
Object
bean
)
{
return
new
DynaBean
(
bean
);
}
/**
* 查找类型转换器 {@link PropertyEditor}
*
* @param type 需要转换的目标类型
* @return {@link PropertyEditor}
*/
public
static
PropertyEditor
findEditor
(
Class
<?>
type
)
{
return
PropertyEditorManager
.
findEditor
(
type
);
}
/**
* 获取{@link BeanDesc} Bean描述信息
*
* @param clazz Bean类
* @return {@link BeanDesc}
* @since 3.1.2
*/
public
static
BeanDesc
getBeanDesc
(
Class
<?>
clazz
)
{
return
BeanDescCache
.
INSTANCE
.
getBeanDesc
(
clazz
,
()
->
new
BeanDesc
(
clazz
));
}
/**
* 遍历Bean的属性
*
* @param clazz Bean类
* @param action 每个元素的处理类
* @since 5.4.2
*/
public
static
void
descForEach
(
Class
<?>
clazz
,
Consumer
<?
super
PropDesc
>
action
)
{
getBeanDesc
(
clazz
).
getProps
().
forEach
(
action
);
}
// --------------------------------------------------------------------------------------------------------- PropertyDescriptor
/**
* 获得Bean字段描述数组
*
* @param clazz Bean类
* @return 字段描述数组
* @throws BeanException 获取属性异常
*/
public
static
PropertyDescriptor
[]
getPropertyDescriptors
(
Class
<?>
clazz
)
throws
BeanException
{
BeanInfo
beanInfo
;
try
{
beanInfo
=
Introspector
.
getBeanInfo
(
clazz
);
}
catch
(
IntrospectionException
e
)
{
throw
new
BeanException
(
e
);
}
return
ArrayUtil
.
filter
(
beanInfo
.
getPropertyDescriptors
(),
t
->
{
// 过滤掉getClass方法
return
false
==
"class"
.
equals
(
t
.
getName
());
});
}
/**
* 获得字段名和字段描述Map,获得的结果会缓存在 {@link BeanInfoCache}中
*
* @param clazz Bean类
* @param ignoreCase 是否忽略大小写
* @return 字段名和字段描述Map
* @throws BeanException 获取属性异常
*/
public
static
Map
<
String
,
PropertyDescriptor
>
getPropertyDescriptorMap
(
Class
<?>
clazz
,
boolean
ignoreCase
)
throws
BeanException
{
return
BeanInfoCache
.
INSTANCE
.
getPropertyDescriptorMap
(
clazz
,
ignoreCase
,
()
->
internalGetPropertyDescriptorMap
(
clazz
,
ignoreCase
));
}
/**
* 获得字段名和字段描述Map。内部使用,直接获取Bean类的PropertyDescriptor
*
* @param clazz Bean类
* @param ignoreCase 是否忽略大小写
* @return 字段名和字段描述Map
* @throws BeanException 获取属性异常
*/
private
static
Map
<
String
,
PropertyDescriptor
>
internalGetPropertyDescriptorMap
(
Class
<?>
clazz
,
boolean
ignoreCase
)
throws
BeanException
{
final
PropertyDescriptor
[]
propertyDescriptors
=
getPropertyDescriptors
(
clazz
);
final
Map
<
String
,
PropertyDescriptor
>
map
=
ignoreCase
?
new
CaseInsensitiveMap
<>(
propertyDescriptors
.
length
,
1
f
)
:
new
HashMap
<>(
propertyDescriptors
.
length
,
1
);
for
(
PropertyDescriptor
propertyDescriptor
:
propertyDescriptors
)
{
map
.
put
(
propertyDescriptor
.
getName
(),
propertyDescriptor
);
}
return
map
;
}
/**
* 获得Bean类属性描述,大小写敏感
*
* @param clazz Bean类
* @param fieldName 字段名
* @return PropertyDescriptor
* @throws BeanException 获取属性异常
*/
public
static
PropertyDescriptor
getPropertyDescriptor
(
Class
<?>
clazz
,
final
String
fieldName
)
throws
BeanException
{
return
getPropertyDescriptor
(
clazz
,
fieldName
,
false
);
}
/**
* 获得Bean类属性描述
*
* @param clazz Bean类
* @param fieldName 字段名
* @param ignoreCase 是否忽略大小写
* @return PropertyDescriptor
* @throws BeanException 获取属性异常
*/
public
static
PropertyDescriptor
getPropertyDescriptor
(
Class
<?>
clazz
,
final
String
fieldName
,
boolean
ignoreCase
)
throws
BeanException
{
final
Map
<
String
,
PropertyDescriptor
>
map
=
getPropertyDescriptorMap
(
clazz
,
ignoreCase
);
return
(
null
==
map
)
?
null
:
map
.
get
(
fieldName
);
}
/**
* 获得字段值,通过反射直接获得字段值,并不调用getXXX方法<br>
* 对象同样支持Map类型,fieldNameOrIndex即为key
*
* <ul>
* <li>Map: fieldNameOrIndex需为key,获取对应value</li>
* <li>Collection: fieldNameOrIndex当为数字,返回index对应值,非数字遍历集合返回子bean对应name值</li>
* <li>Array: fieldNameOrIndex当为数字,返回index对应值,非数字遍历数组返回子bean对应name值</li>
* </ul>
*
* @param bean Bean对象
* @param fieldNameOrIndex 字段名或序号,序号支持负数
* @return 字段值
*/
public
static
Object
getFieldValue
(
Object
bean
,
String
fieldNameOrIndex
)
{
if
(
null
==
bean
||
null
==
fieldNameOrIndex
)
{
return
null
;
}
if
(
bean
instanceof
Map
)
{
return
((
Map
<?,
?>)
bean
).
get
(
fieldNameOrIndex
);
}
else
if
(
bean
instanceof
Collection
)
{
try
{
return
CollUtil
.
get
((
Collection
<?>)
bean
,
Integer
.
parseInt
(
fieldNameOrIndex
));
}
catch
(
NumberFormatException
e
)
{
// 非数字,see pr#254@Gitee
return
CollUtil
.
map
((
Collection
<?>)
bean
,
(
beanEle
)
->
getFieldValue
(
beanEle
,
fieldNameOrIndex
),
false
);
}
}
else
if
(
ArrayUtil
.
isArray
(
bean
))
{
try
{
return
ArrayUtil
.
get
(
bean
,
Integer
.
parseInt
(
fieldNameOrIndex
));
}
catch
(
NumberFormatException
e
)
{
// 非数字,see pr#254@Gitee
return
ArrayUtil
.
map
(
bean
,
Object
.
class
,
(
beanEle
)
->
getFieldValue
(
beanEle
,
fieldNameOrIndex
));
}
}
else
{
// 普通Bean对象
return
ReflectUtil
.
getFieldValue
(
bean
,
fieldNameOrIndex
);
}
}
/**
* 设置字段值,通过反射设置字段值,并不调用setXXX方法<br>
* 对象同样支持Map类型,fieldNameOrIndex即为key,支持:
* <ul>
* <li>Map</li>
* <li>List</li>
* <li>Bean</li>
* </ul>
*
* @param bean Bean
* @param fieldNameOrIndex 字段名或序号,序号支持负数
* @param value 值
* @return bean,当为数组时,返回一个新的数组
*/
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
public
static
Object
setFieldValue
(
Object
bean
,
String
fieldNameOrIndex
,
Object
value
)
{
if
(
bean
instanceof
Map
)
{
((
Map
)
bean
).
put
(
fieldNameOrIndex
,
value
);
}
else
if
(
bean
instanceof
List
)
{
ListUtil
.
setOrPadding
((
List
)
bean
,
Convert
.
toInt
(
fieldNameOrIndex
),
value
);
}
else
if
(
ArrayUtil
.
isArray
(
bean
))
{
// issue#3008,追加产生新数组,此处返回新数组
return
ArrayUtil
.
setOrAppend
(
bean
,
Convert
.
toInt
(
fieldNameOrIndex
),
value
);
}
else
{
// 普通Bean对象
ReflectUtil
.
setFieldValue
(
bean
,
fieldNameOrIndex
,
value
);
}
return
bean
;
}
/**
* 解析Bean中的属性值
*
* @param <T> 属性值类型
* @param bean Bean对象,支持Map、List、Collection、Array
* @param expression 表达式,例如:person.friend[5].name
* @return Bean属性值,bean为{@code null}或者express为空,返回{@code null}
* @see BeanPath#get(Object)
* @since 3.0.7
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
T
>
T
getProperty
(
Object
bean
,
String
expression
)
{
if
(
null
==
bean
||
StrUtil
.
isBlank
(
expression
))
{
return
null
;
}
return
(
T
)
BeanPath
.
create
(
expression
).
get
(
bean
);
}
/**
* 解析Bean中的属性值
*
* @param bean Bean对象,支持Map、List、Collection、Array
* @param expression 表达式,例如:person.friend[5].name
* @param value 属性值
* @see BeanPath#get(Object)
* @since 4.0.6
*/
public
static
void
setProperty
(
Object
bean
,
String
expression
,
Object
value
)
{
BeanPath
.
create
(
expression
).
set
(
bean
,
value
);
}
// --------------------------------------------------------------------------------------------- mapToBean
/**
* Map转换为Bean对象
*
* @param <T> Bean类型
* @param map {@link Map}
* @param beanClass Bean Class
* @param isIgnoreError 是否忽略注入错误
* @return Bean
* @deprecated 请使用 {@link #toBean(Object, Class)} 或 {@link #toBeanIgnoreError(Object, Class)}
*/
@Deprecated
public
static
<
T
>
T
mapToBean
(
Map
<?,
?>
map
,
Class
<
T
>
beanClass
,
boolean
isIgnoreError
)
{
return
fillBeanWithMap
(
map
,
ReflectUtil
.
newInstanceIfPossible
(
beanClass
),
isIgnoreError
);
}
/**
* Map转换为Bean对象<br>
* 忽略大小写
*
* @param <T> Bean类型
* @param map Map
* @param beanClass Bean Class
* @param isIgnoreError 是否忽略注入错误
* @return Bean
* @deprecated 请使用 {@link #toBeanIgnoreCase(Object, Class, boolean)}
*/
@Deprecated
public
static
<
T
>
T
mapToBeanIgnoreCase
(
Map
<?,
?>
map
,
Class
<
T
>
beanClass
,
boolean
isIgnoreError
)
{
return
fillBeanWithMapIgnoreCase
(
map
,
ReflectUtil
.
newInstanceIfPossible
(
beanClass
),
isIgnoreError
);
}
/**
* Map转换为Bean对象
*
* @param <T> Bean类型
* @param map {@link Map}
* @param beanClass Bean Class
* @param copyOptions 转Bean选项
* @return Bean
* @deprecated 请使用 {@link #toBean(Object, Class, CopyOptions)}
*/
@Deprecated
public
static
<
T
>
T
mapToBean
(
Map
<?,
?>
map
,
Class
<
T
>
beanClass
,
CopyOptions
copyOptions
)
{
return
fillBeanWithMap
(
map
,
ReflectUtil
.
newInstanceIfPossible
(
beanClass
),
copyOptions
);
}
/**
* Map转换为Bean对象
*
* @param <T> Bean类型
* @param map {@link Map}
* @param beanClass Bean Class
* @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格
* @param copyOptions 转Bean选项
* @return Bean
*/
public
static
<
T
>
T
mapToBean
(
Map
<?,
?>
map
,
Class
<
T
>
beanClass
,
boolean
isToCamelCase
,
CopyOptions
copyOptions
)
{
return
fillBeanWithMap
(
map
,
ReflectUtil
.
newInstanceIfPossible
(
beanClass
),
isToCamelCase
,
copyOptions
);
}
// --------------------------------------------------------------------------------------------- fillBeanWithMap
/**
* 使用Map填充Bean对象
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public
static
<
T
>
T
fillBeanWithMap
(
Map
<?,
?>
map
,
T
bean
,
boolean
isIgnoreError
)
{
return
fillBeanWithMap
(
map
,
bean
,
false
,
isIgnoreError
);
}
/**
* 使用Map填充Bean对象,可配置将下划线转换为驼峰
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param isToCamelCase 是否将下划线模式转换为驼峰模式
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public
static
<
T
>
T
fillBeanWithMap
(
Map
<?,
?>
map
,
T
bean
,
boolean
isToCamelCase
,
boolean
isIgnoreError
)
{
return
fillBeanWithMap
(
map
,
bean
,
isToCamelCase
,
CopyOptions
.
create
().
setIgnoreError
(
isIgnoreError
));
}
/**
* 使用Map填充Bean对象,忽略大小写
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param isIgnoreError 是否忽略注入错误
* @return Bean
*/
public
static
<
T
>
T
fillBeanWithMapIgnoreCase
(
Map
<?,
?>
map
,
T
bean
,
boolean
isIgnoreError
)
{
return
fillBeanWithMap
(
map
,
bean
,
CopyOptions
.
create
().
setIgnoreCase
(
true
).
setIgnoreError
(
isIgnoreError
));
}
/**
* 使用Map填充Bean对象
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param copyOptions 属性复制选项 {@link CopyOptions}
* @return Bean
*/
public
static
<
T
>
T
fillBeanWithMap
(
Map
<?,
?>
map
,
T
bean
,
CopyOptions
copyOptions
)
{
return
fillBeanWithMap
(
map
,
bean
,
false
,
copyOptions
);
}
/**
* 使用Map填充Bean对象
*
* @param <T> Bean类型
* @param map Map
* @param bean Bean
* @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格
* @param copyOptions 属性复制选项 {@link CopyOptions}
* @return Bean
* @since 3.3.1
*/
public
static
<
T
>
T
fillBeanWithMap
(
Map
<?,
?>
map
,
T
bean
,
boolean
isToCamelCase
,
CopyOptions
copyOptions
)
{
if
(
MapUtil
.
isEmpty
(
map
))
{
return
bean
;
}
if
(
isToCamelCase
)
{
map
=
MapUtil
.
toCamelCaseMap
(
map
);
}
copyProperties
(
map
,
bean
,
copyOptions
);
return
bean
;
}
// --------------------------------------------------------------------------------------------- fillBean
/**
* 对象或Map转Bean
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param clazz 目标的Bean类型
* @return Bean对象
* @since 4.1.20
*/
public
static
<
T
>
T
toBean
(
Object
source
,
Class
<
T
>
clazz
)
{
return
toBean
(
source
,
clazz
,
null
);
}
/**
* 对象或Map转Bean,忽略字段转换时发生的异常
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param clazz 目标的Bean类型
* @return Bean对象
* @since 5.4.0
*/
public
static
<
T
>
T
toBeanIgnoreError
(
Object
source
,
Class
<
T
>
clazz
)
{
return
toBean
(
source
,
clazz
,
CopyOptions
.
create
().
setIgnoreError
(
true
));
}
/**
* 对象或Map转Bean,忽略字段转换时发生的异常
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param clazz 目标的Bean类型
* @param ignoreError 是否忽略注入错误
* @return Bean对象
* @since 5.4.0
*/
public
static
<
T
>
T
toBeanIgnoreCase
(
Object
source
,
Class
<
T
>
clazz
,
boolean
ignoreError
)
{
return
toBean
(
source
,
clazz
,
CopyOptions
.
create
()
.
setIgnoreCase
(
true
)
.
setIgnoreError
(
ignoreError
));
}
/**
* 对象或Map转Bean
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param clazz 目标的Bean类型
* @param options 属性拷贝选项
* @return Bean对象
* @since 5.2.4
*/
public
static
<
T
>
T
toBean
(
Object
source
,
Class
<
T
>
clazz
,
CopyOptions
options
)
{
return
toBean
(
source
,
()
->
ReflectUtil
.
newInstanceIfPossible
(
clazz
),
options
);
}
/**
* 对象或Map转Bean
*
* @param <T> 转换的Bean类型
* @param source Bean对象或Map
* @param targetSupplier 目标的Bean创建器
* @param options 属性拷贝选项
* @return Bean对象
* @since 5.8.0
*/
public
static
<
T
>
T
toBean
(
Object
source
,
Supplier
<
T
>
targetSupplier
,
CopyOptions
options
)
{
if
(
null
==
source
||
null
==
targetSupplier
)
{
return
null
;
}
final
T
target
=
targetSupplier
.
get
();
copyProperties
(
source
,
target
,
options
);
return
target
;
}
/**
* ServletRequest 参数转Bean
*
* @param <T> Bean类型
* @param beanClass Bean Class
* @param valueProvider 值提供者
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
* @return Bean
*/
public
static
<
T
>
T
toBean
(
Class
<
T
>
beanClass
,
ValueProvider
<
String
>
valueProvider
,
CopyOptions
copyOptions
)
{
if
(
null
==
beanClass
||
null
==
valueProvider
)
{
return
null
;
}
return
fillBean
(
ReflectUtil
.
newInstanceIfPossible
(
beanClass
),
valueProvider
,
copyOptions
);
}
/**
* 填充Bean的核心方法
*
* @param <T> Bean类型
* @param bean Bean
* @param valueProvider 值提供者
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
* @return Bean
*/
public
static
<
T
>
T
fillBean
(
T
bean
,
ValueProvider
<
String
>
valueProvider
,
CopyOptions
copyOptions
)
{
if
(
null
==
valueProvider
)
{
return
bean
;
}
return
BeanCopier
.
create
(
valueProvider
,
bean
,
copyOptions
).
copy
();
}
// --------------------------------------------------------------------------------------------- beanToMap
/**
* 将bean的部分属性转换成map<br>
* 可选拷贝哪些属性值,默认是不忽略值为{@code null}的值的。
*
* @param bean bean
* @param properties 需要拷贝的属性值,{@code null}或空表示拷贝所有值
* @return Map
* @since 5.8.0
*/
public
static
Map
<
String
,
Object
>
beanToMap
(
Object
bean
,
String
...
properties
)
{
int
mapSize
=
16
;
Editor
<
String
>
keyEditor
=
null
;
if
(
ArrayUtil
.
isNotEmpty
(
properties
))
{
mapSize
=
properties
.
length
;
final
Set
<
String
>
propertiesSet
=
CollUtil
.
set
(
false
,
properties
);
keyEditor
=
property
->
propertiesSet
.
contains
(
property
)
?
property
:
null
;
}
// 指明了要复制的属性 所以不忽略null值
return
beanToMap
(
bean
,
new
LinkedHashMap
<>(
mapSize
,
1
),
false
,
keyEditor
);
}
/**
* 对象转Map
*
* @param bean bean对象
* @param isToUnderlineCase 是否转换为下划线模式
* @param ignoreNullValue 是否忽略值为空的字段
* @return Map
*/
public
static
Map
<
String
,
Object
>
beanToMap
(
Object
bean
,
boolean
isToUnderlineCase
,
boolean
ignoreNullValue
)
{
if
(
null
==
bean
)
{
return
null
;
}
return
beanToMap
(
bean
,
new
LinkedHashMap
<>(),
isToUnderlineCase
,
ignoreNullValue
);
}
/**
* 对象转Map
*
* @param bean bean对象
* @param targetMap 目标的Map
* @param isToUnderlineCase 是否转换为下划线模式
* @param ignoreNullValue 是否忽略值为空的字段
* @return Map
* @since 3.2.3
*/
public
static
Map
<
String
,
Object
>
beanToMap
(
Object
bean
,
Map
<
String
,
Object
>
targetMap
,
final
boolean
isToUnderlineCase
,
boolean
ignoreNullValue
)
{
if
(
null
==
bean
)
{
return
null
;
}
return
beanToMap
(
bean
,
targetMap
,
ignoreNullValue
,
key
->
isToUnderlineCase
?
StrUtil
.
toUnderlineCase
(
key
)
:
key
);
}
/**
* 对象转Map<br>
* 通过实现{@link Editor} 可以自定义字段值,如果这个Editor返回null则忽略这个字段,以便实现:
*
* <pre>
* 1. 字段筛选,可以去除不需要的字段
* 2. 字段变换,例如实现驼峰转下划线
* 3. 自定义字段前缀或后缀等等
* </pre>
*
* @param bean bean对象
* @param targetMap 目标的Map
* @param ignoreNullValue 是否忽略值为空的字段
* @param keyEditor 属性字段(Map的key)编辑器,用于筛选、编辑key,如果这个Editor返回null则忽略这个字段
* @return Map
* @since 4.0.5
*/
public
static
Map
<
String
,
Object
>
beanToMap
(
Object
bean
,
Map
<
String
,
Object
>
targetMap
,
boolean
ignoreNullValue
,
Editor
<
String
>
keyEditor
)
{
if
(
null
==
bean
)
{
return
null
;
}
return
BeanCopier
.
create
(
bean
,
targetMap
,
CopyOptions
.
create
()
.
setIgnoreNullValue
(
ignoreNullValue
)
.
setFieldNameEditor
(
keyEditor
)
).
copy
();
}
/**
* 对象转Map<br>
* 通过自定义{@link CopyOptions} 完成抓换选项,以便实现:
*
* <pre>
* 1. 字段筛选,可以去除不需要的字段
* 2. 字段变换,例如实现驼峰转下划线
* 3. 自定义字段前缀或后缀等等
* 4. 字段值处理
* ...
* </pre>
*
* @param bean bean对象
* @param targetMap 目标的Map
* @param copyOptions 拷贝选项
* @return Map
* @since 5.7.15
*/
public
static
Map
<
String
,
Object
>
beanToMap
(
Object
bean
,
Map
<
String
,
Object
>
targetMap
,
CopyOptions
copyOptions
)
{
if
(
null
==
bean
)
{
return
null
;
}
return
BeanCopier
.
create
(
bean
,
targetMap
,
copyOptions
).
copy
();
}
// --------------------------------------------------------------------------------------------- copyProperties
/**
* 按照Bean对象属性创建对应的Class对象,并忽略某些属性
*
* @param <T> 对象类型
* @param source 源Bean对象
* @param tClass 目标Class
* @param ignoreProperties 不拷贝的的属性列表
* @return 目标对象
*/
public
static
<
T
>
T
copyProperties
(
Object
source
,
Class
<
T
>
tClass
,
String
...
ignoreProperties
)
{
if
(
null
==
source
)
{
return
null
;
}
T
target
=
ReflectUtil
.
newInstanceIfPossible
(
tClass
);
copyProperties
(
source
,
target
,
CopyOptions
.
create
().
setIgnoreProperties
(
ignoreProperties
));
return
target
;
}
/**
* 复制Bean对象属性<br>
* 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param ignoreProperties 不拷贝的的属性列表
*/
public
static
void
copyProperties
(
Object
source
,
Object
target
,
String
...
ignoreProperties
)
{
copyProperties
(
source
,
target
,
CopyOptions
.
create
().
setIgnoreProperties
(
ignoreProperties
));
}
/**
* 复制Bean对象属性<br>
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param ignoreCase 是否忽略大小写
*/
public
static
void
copyProperties
(
Object
source
,
Object
target
,
boolean
ignoreCase
)
{
BeanCopier
.
create
(
source
,
target
,
CopyOptions
.
create
().
setIgnoreCase
(
ignoreCase
)).
copy
();
}
/**
* 复制Bean对象属性<br>
* 限制类用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
*
* @param source 源Bean对象
* @param target 目标Bean对象
* @param copyOptions 拷贝选项,见 {@link CopyOptions}
*/
public
static
void
copyProperties
(
Object
source
,
Object
target
,
CopyOptions
copyOptions
)
{
if
(
null
==
source
)
{
return
;
}
BeanCopier
.
create
(
source
,
target
,
ObjectUtil
.
defaultIfNull
(
copyOptions
,
CopyOptions:
:
create
)).
copy
();
}
/**
* 复制集合中的Bean属性<br>
* 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。
*
* @param collection 原Bean集合
* @param targetType 目标Bean类型
* @param copyOptions 拷贝选项
* @param <T> Bean类型
* @return 复制后的List
* @since 5.6.4
*/
public
static
<
T
>
List
<
T
>
copyToList
(
Collection
<?>
collection
,
Class
<
T
>
targetType
,
CopyOptions
copyOptions
)
{
if
(
null
==
collection
)
{
return
null
;
}
if
(
collection
.
isEmpty
())
{
return
new
ArrayList
<>(
0
);
}
// issue#3091
if
(
ClassUtil
.
isBasicType
(
targetType
)
||
String
.
class
==
targetType
){
return
Convert
.
toList
(
targetType
,
collection
);
}
return
collection
.
stream
().
map
((
source
)
->
{
final
T
target
=
ReflectUtil
.
newInstanceIfPossible
(
targetType
);
copyProperties
(
source
,
target
,
copyOptions
);
return
target
;
}).
collect
(
Collectors
.
toList
());
}
/**
* 复制集合中的Bean属性<br>
* 此方法遍历集合中每个Bean,复制其属性后加入一个新的{@link List}中。
*
* @param collection 原Bean集合
* @param targetType 目标Bean类型
* @param <T> Bean类型
* @return 复制后的List
* @since 5.6.6
*/
public
static
<
T
>
List
<
T
>
copyToList
(
Collection
<?>
collection
,
Class
<
T
>
targetType
)
{
return
copyToList
(
collection
,
targetType
,
CopyOptions
.
create
());
}
/**
* 给定的Bean的类名是否匹配指定类名字符串<br>
* 如果isSimple为{@code true},则只匹配类名而忽略包名,例如:cn.hutool.TestEntity只匹配TestEntity<br>
* 如果isSimple为{@code false},则匹配包括包名的全类名,例如:cn.hutool.TestEntity匹配cn.hutool.TestEntity
*
* @param bean Bean
* @param beanClassName Bean的类名
* @param isSimple 是否只匹配类名而忽略包名,true表示忽略包名
* @return 是否匹配
* @since 4.0.6
*/
public
static
boolean
isMatchName
(
Object
bean
,
String
beanClassName
,
boolean
isSimple
)
{
if
(
null
==
bean
||
StrUtil
.
isBlank
(
beanClassName
))
{
return
false
;
}
return
ClassUtil
.
getClassName
(
bean
,
isSimple
).
equals
(
isSimple
?
StrUtil
.
upperFirst
(
beanClassName
)
:
beanClassName
);
}
/**
* 编辑Bean的字段,static字段不会处理<br>
* 例如需要对指定的字段做判空操作、null转""操作等等。
*
* @param bean bean
* @param editor 编辑器函数
* @param <T> 被编辑的Bean类型
* @return bean
* @since 5.6.4
*/
public
static
<
T
>
T
edit
(
T
bean
,
Editor
<
Field
>
editor
)
{
if
(
bean
==
null
)
{
return
null
;
}
final
Field
[]
fields
=
ReflectUtil
.
getFields
(
bean
.
getClass
());
for
(
Field
field
:
fields
)
{
if
(
ModifierUtil
.
isStatic
(
field
))
{
continue
;
}
editor
.
edit
(
field
);
}
return
bean
;
}
/**
* 把Bean里面的String属性做trim操作。此方法直接对传入的Bean做修改。
* <p>
* 通常bean直接用来绑定页面的input,用户的输入可能首尾存在空格,通常保存数据库前需要把首尾空格去掉
*
* @param <T> Bean类型
* @param bean Bean对象
* @param ignoreFields 不需要trim的Field名称列表(不区分大小写)
* @return 处理后的Bean对象
*/
public
static
<
T
>
T
trimStrFields
(
T
bean
,
String
...
ignoreFields
)
{
return
edit
(
bean
,
(
field
)
->
{
if
(
ignoreFields
!=
null
&&
ArrayUtil
.
containsIgnoreCase
(
ignoreFields
,
field
.
getName
()))
{
// 不处理忽略的Fields
return
field
;
}
if
(
String
.
class
.
equals
(
field
.
getType
()))
{
// 只有String的Field才处理
final
String
val
=
(
String
)
ReflectUtil
.
getFieldValue
(
bean
,
field
);
if
(
null
!=
val
)
{
final
String
trimVal
=
StrUtil
.
trim
(
val
);
if
(
false
==
val
.
equals
(
trimVal
))
{
// Field Value不为null,且首尾有空格才处理
ReflectUtil
.
setFieldValue
(
bean
,
field
,
trimVal
);
}
}
}
return
field
;
});
}
/**
* 判断Bean是否为非空对象,非空对象表示本身不为{@code null}或者含有非{@code null}属性的对象
*
* @param bean Bean对象
* @param ignoreFieldNames 忽略检查的字段名
* @return 是否为非空,{@code true} - 非空 / {@code false} - 空
* @since 5.0.7
*/
public
static
boolean
isNotEmpty
(
Object
bean
,
String
...
ignoreFieldNames
)
{
return
false
==
isEmpty
(
bean
,
ignoreFieldNames
);
}
/**
* 判断Bean是否为空对象,空对象表示本身为{@code null}或者所有属性都为{@code null}<br>
* 此方法不判断static属性
*
* @param bean Bean对象
* @param ignoreFieldNames 忽略检查的字段名
* @return 是否为空,{@code true} - 空 / {@code false} - 非空
* @since 4.1.10
*/
public
static
boolean
isEmpty
(
Object
bean
,
String
...
ignoreFieldNames
)
{
if
(
null
!=
bean
)
{
for
(
Field
field
:
ReflectUtil
.
getFields
(
bean
.
getClass
()))
{
if
(
ModifierUtil
.
isStatic
(
field
))
{
continue
;
}
if
((
false
==
ArrayUtil
.
contains
(
ignoreFieldNames
,
field
.
getName
()))
&&
null
!=
ReflectUtil
.
getFieldValue
(
bean
,
field
))
{
return
false
;
}
}
}
return
true
;
}
/**
* 判断Bean是否包含值为{@code null}的属性<br>
* 对象本身为{@code null}也返回true
*
* @param bean Bean对象
* @param ignoreFieldNames 忽略检查的字段名
* @return 是否包含值为<code>null</code>的属性,{@code true} - 包含 / {@code false} - 不包含
* @since 4.1.10
*/
public
static
boolean
hasNullField
(
Object
bean
,
String
...
ignoreFieldNames
)
{
if
(
null
==
bean
)
{
return
true
;
}
for
(
Field
field
:
ReflectUtil
.
getFields
(
bean
.
getClass
()))
{
if
(
ModifierUtil
.
isStatic
(
field
))
{
continue
;
}
if
((
false
==
ArrayUtil
.
contains
(
ignoreFieldNames
,
field
.
getName
()))
&&
null
==
ReflectUtil
.
getFieldValue
(
bean
,
field
))
{
return
true
;
}
}
return
false
;
}
/**
* 获取Getter或Setter方法名对应的字段名称,规则如下:
* <ul>
* <li>getXxxx获取为xxxx,如getName得到name。</li>
* <li>setXxxx获取为xxxx,如setName得到name。</li>
* <li>isXxxx获取为xxxx,如isName得到name。</li>
* <li>其它不满足规则的方法名抛出{@link IllegalArgumentException}</li>
* </ul>
*
* @param getterOrSetterName Getter或Setter方法名
* @return 字段名称
* @throws IllegalArgumentException 非Getter或Setter方法
* @since 5.7.23
*/
public
static
String
getFieldName
(
String
getterOrSetterName
)
{
if
(
getterOrSetterName
.
startsWith
(
"get"
)
||
getterOrSetterName
.
startsWith
(
"set"
))
{
return
StrUtil
.
removePreAndLowerFirst
(
getterOrSetterName
,
3
);
}
else
if
(
getterOrSetterName
.
startsWith
(
"is"
))
{
return
StrUtil
.
removePreAndLowerFirst
(
getterOrSetterName
,
2
);
}
else
{
throw
new
IllegalArgumentException
(
"Invalid Getter or Setter name: "
+
getterOrSetterName
);
}
}
/**
* 判断source与target的所有公共字段的值是否相同
*
* @param source 待检测对象1
* @param target 待检测对象2
* @param ignoreProperties 不需要检测的字段
* @return 判断结果,如果为true则证明所有字段的值都相同
* @author Takak11
* @since 5.8.4
*/
public
static
boolean
isCommonFieldsEqual
(
Object
source
,
Object
target
,
String
...
ignoreProperties
)
{
if
(
null
==
source
&&
null
==
target
)
{
return
true
;
}
if
(
null
==
source
||
null
==
target
)
{
return
false
;
}
Map
<
String
,
Object
>
sourceFieldsMap
=
BeanUtil
.
beanToMap
(
source
);
Map
<
String
,
Object
>
targetFieldsMap
=
BeanUtil
.
beanToMap
(
target
);
Set
<
String
>
sourceFields
=
sourceFieldsMap
.
keySet
();
sourceFields
.
removeAll
(
Arrays
.
asList
(
ignoreProperties
));
for
(
String
field
:
sourceFields
)
{
if
(
ObjectUtil
.
notEqual
(
sourceFieldsMap
.
get
(
field
),
targetFieldsMap
.
get
(
field
)))
{
return
false
;
}
}
return
true
;
}
}
hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
import
cn.hutool.core.clone.CloneSupport
;
import
cn.hutool.core.lang.Assert
;
import
cn.hutool.core.util.ClassUtil
;
import
cn.hutool.core.util.ReflectUtil
;
import
java.io.Serializable
;
import
java.util.Map
;
/**
* 动态Bean,通过反射对Bean的相关方法做操作<br>
* 支持Map和普通Bean
*
* @author Looly
* @since 3.0.7
*/
public
class
DynaBean
extends
CloneSupport
<
DynaBean
>
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
final
Class
<?>
beanClass
;
private
final
Object
bean
;
/**
* 创建一个DynaBean
*
* @param bean 普通Bean
* @return DynaBean
*/
public
static
DynaBean
create
(
Object
bean
)
{
return
new
DynaBean
(
bean
);
}
/**
* 创建一个DynaBean
*
* @param beanClass Bean类
* @return DynaBean
*/
public
static
DynaBean
create
(
Class
<?>
beanClass
)
{
return
new
DynaBean
(
beanClass
);
}
/**
* 创建一个DynaBean
*
* @param beanClass Bean类
* @param params 构造Bean所需要的参数
* @return DynaBean
*/
public
static
DynaBean
create
(
Class
<?>
beanClass
,
Object
...
params
)
{
return
new
DynaBean
(
beanClass
,
params
);
}
//------------------------------------------------------------------------ Constructor start
/**
* 构造
*
* @param beanClass Bean类
* @param params 构造Bean所需要的参数
*/
public
DynaBean
(
Class
<?>
beanClass
,
Object
...
params
)
{
this
(
ReflectUtil
.
newInstance
(
beanClass
,
params
));
}
/**
* 构造
*
* @param beanClass Bean类
*/
public
DynaBean
(
Class
<?>
beanClass
)
{
this
(
ReflectUtil
.
newInstance
(
beanClass
));
}
/**
* 构造
*
* @param bean 原始Bean
*/
public
DynaBean
(
Object
bean
)
{
Assert
.
notNull
(
bean
);
if
(
bean
instanceof
DynaBean
)
{
bean
=
((
DynaBean
)
bean
).
getBean
();
}
this
.
bean
=
bean
;
this
.
beanClass
=
ClassUtil
.
getClass
(
bean
);
}
//------------------------------------------------------------------------ Constructor end
/**
* 获得字段对应值
*
* @param <T> 属性值类型
* @param fieldName 字段名
* @return 字段值
* @throws BeanException 反射获取属性值或字段值导致的异常
*/
@SuppressWarnings
(
"unchecked"
)
public
<
T
>
T
get
(
String
fieldName
)
throws
BeanException
{
if
(
Map
.
class
.
isAssignableFrom
(
beanClass
))
{
return
(
T
)
((
Map
<?,
?>)
bean
).
get
(
fieldName
);
}
else
{
final
PropDesc
prop
=
BeanUtil
.
getBeanDesc
(
beanClass
).
getProp
(
fieldName
);
if
(
null
==
prop
)
{
throw
new
BeanException
(
"No public field or get method for {}"
,
fieldName
);
}
return
(
T
)
prop
.
getValue
(
bean
);
}
}
/**
* 检查是否有指定名称的bean属性
*
* @param fieldName 字段名
* @return 是否有bean属性
* @since 5.4.2
*/
public
boolean
containsProp
(
String
fieldName
)
{
if
(
Map
.
class
.
isAssignableFrom
(
beanClass
))
{
return
((
Map
<?,
?>)
bean
).
containsKey
(
fieldName
);
}
else
{
return
null
!=
BeanUtil
.
getBeanDesc
(
beanClass
).
getProp
(
fieldName
);
}
}
/**
* 获得字段对应值,获取异常返回{@code null}
*
* @param <T> 属性值类型
* @param fieldName 字段名
* @return 字段值
* @since 3.1.1
*/
public
<
T
>
T
safeGet
(
String
fieldName
)
{
try
{
return
get
(
fieldName
);
}
catch
(
Exception
e
)
{
return
null
;
}
}
/**
* 设置字段值
*
* @param fieldName 字段名
* @param value 字段值
* @throws BeanException 反射获取属性值或字段值导致的异常
*/
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
public
void
set
(
String
fieldName
,
Object
value
)
throws
BeanException
{
if
(
Map
.
class
.
isAssignableFrom
(
beanClass
))
{
((
Map
)
bean
).
put
(
fieldName
,
value
);
}
else
{
final
PropDesc
prop
=
BeanUtil
.
getBeanDesc
(
beanClass
).
getProp
(
fieldName
);
if
(
null
==
prop
)
{
throw
new
BeanException
(
"No public field or set method for {}"
,
fieldName
);
}
prop
.
setValue
(
bean
,
value
);
}
}
/**
* 执行原始Bean中的方法
*
* @param methodName 方法名
* @param params 参数
* @return 执行结果,可能为null
*/
public
Object
invoke
(
String
methodName
,
Object
...
params
)
{
return
ReflectUtil
.
invoke
(
this
.
bean
,
methodName
,
params
);
}
/**
* 获得原始Bean
*
* @param <T> Bean类型
* @return bean
*/
@SuppressWarnings
(
"unchecked"
)
public
<
T
>
T
getBean
()
{
return
(
T
)
this
.
bean
;
}
/**
* 获得Bean的类型
*
* @param <T> Bean类型
* @return Bean类型
*/
@SuppressWarnings
(
"unchecked"
)
public
<
T
>
Class
<
T
>
getBeanClass
()
{
return
(
Class
<
T
>)
this
.
beanClass
;
}
@Override
public
int
hashCode
()
{
final
int
prime
=
31
;
int
result
=
1
;
result
=
prime
*
result
+
((
bean
==
null
)
?
0
:
bean
.
hashCode
());
return
result
;
}
@Override
public
boolean
equals
(
Object
obj
)
{
if
(
this
==
obj
)
{
return
true
;
}
if
(
obj
==
null
)
{
return
false
;
}
if
(
getClass
()
!=
obj
.
getClass
())
{
return
false
;
}
final
DynaBean
other
=
(
DynaBean
)
obj
;
if
(
bean
==
null
)
{
return
other
.
bean
==
null
;
}
else
return
bean
.
equals
(
other
.
bean
);
}
@Override
public
String
toString
()
{
return
this
.
bean
.
toString
();
}
}
hutool-core/src/main/java/cn/hutool/core/bean/NullWrapperBean.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
/**
* 为了解决反射过程中,需要传递null参数,但是会丢失参数类型而设立的包装类
*
* @param <T> Null值对应的类型
* @author Lillls
* @since 5.5.0
*/
public
class
NullWrapperBean
<
T
>
{
private
final
Class
<
T
>
clazz
;
/**
* @param clazz null的类型
*/
public
NullWrapperBean
(
Class
<
T
>
clazz
)
{
this
.
clazz
=
clazz
;
}
/**
* 获取null值对应的类型
*
* @return 类型
*/
public
Class
<
T
>
getWrappedClass
()
{
return
clazz
;
}
}
hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean
;
import
cn.hutool.core.annotation.AnnotationUtil
;
import
cn.hutool.core.annotation.PropIgnore
;
import
cn.hutool.core.convert.Convert
;
import
cn.hutool.core.util.ClassUtil
;
import
cn.hutool.core.util.ModifierUtil
;
import
cn.hutool.core.util.ReflectUtil
;
import
cn.hutool.core.util.TypeUtil
;
import
java.beans.Transient
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Type
;
/**
* 属性描述,包括了字段、getter、setter和相应的方法执行
*
* @author looly
*/
public
class
PropDesc
{
/**
* 字段
*/
final
Field
field
;
/**
* Getter方法
*/
protected
Method
getter
;
/**
* Setter方法
*/
protected
Method
setter
;
/**
* 构造<br>
* Getter和Setter方法设置为默认可访问
*
* @param field 字段
* @param getter get方法
* @param setter set方法
*/
public
PropDesc
(
Field
field
,
Method
getter
,
Method
setter
)
{
this
.
field
=
field
;
this
.
getter
=
ClassUtil
.
setAccessible
(
getter
);
this
.
setter
=
ClassUtil
.
setAccessible
(
setter
);
}
/**
* 获取字段名,如果存在Alias注解,读取注解的值作为名称
*
* @return 字段名
*/
public
String
getFieldName
()
{
return
ReflectUtil
.
getFieldName
(
this
.
field
);
}
/**
* 获取字段名称
*
* @return 字段名
* @since 5.1.6
*/
public
String
getRawFieldName
()
{
return
null
==
this
.
field
?
null
:
this
.
field
.
getName
();
}
/**
* 获取字段
*
* @return 字段
*/
public
Field
getField
()
{
return
this
.
field
;
}
/**
* 获得字段类型<br>
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
*
* @return 字段类型
*/
public
Type
getFieldType
()
{
if
(
null
!=
this
.
field
)
{
return
TypeUtil
.
getType
(
this
.
field
);
}
return
findPropType
(
getter
,
setter
);
}
/**
* 获得字段类型<br>
* 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
*
* @return 字段类型
*/
public
Class
<?>
getFieldClass
()
{
if
(
null
!=
this
.
field
)
{
return
TypeUtil
.
getClass
(
this
.
field
);
}
return
findPropClass
(
getter
,
setter
);
}
/**
* 获取Getter方法,可能为{@code null}
*
* @return Getter方法
*/
public
Method
getGetter
()
{
return
this
.
getter
;
}
/**
* 获取Setter方法,可能为{@code null}
*
* @return {@link Method}Setter 方法对象
*/
public
Method
getSetter
()
{
return
this
.
setter
;
}
/**
* 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
*
* @param checkTransient 是否检查Transient关键字或注解
* @return 是否可读
* @since 5.4.2
*/
public
boolean
isReadable
(
boolean
checkTransient
)
{
// 检查是否有getter方法或是否为public修饰
if
(
null
==
this
.
getter
&&
false
==
ModifierUtil
.
isPublic
(
this
.
field
))
{
return
false
;
}
// 检查transient关键字和@Transient注解
if
(
checkTransient
&&
isTransientForGet
())
{
return
false
;
}
// 检查@PropIgnore注解
return
false
==
isIgnoreGet
();
}
/**
* 获取属性值<br>
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值<br>
* 此方法不检查任何注解,使用前需调用 {@link #isReadable(boolean)} 检查是否可读
*
* @param bean Bean对象
* @return 字段值
* @since 4.0.5
*/
public
Object
getValue
(
Object
bean
)
{
if
(
null
!=
this
.
getter
)
{
return
ReflectUtil
.
invoke
(
bean
,
this
.
getter
);
}
else
if
(
ModifierUtil
.
isPublic
(
this
.
field
))
{
return
ReflectUtil
.
getFieldValue
(
bean
,
this
.
field
);
}
return
null
;
}
/**
* 获取属性值,自动转换属性值类型<br>
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
*
* @param bean Bean对象
* @param targetType 返回属性值需要转换的类型,null表示不转换
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
* @return this
* @since 5.4.2
*/
public
Object
getValue
(
Object
bean
,
Type
targetType
,
boolean
ignoreError
)
{
Object
result
=
null
;
try
{
result
=
getValue
(
bean
);
}
catch
(
Exception
e
)
{
if
(
false
==
ignoreError
)
{
throw
new
BeanException
(
e
,
"Get value of [{}] error!"
,
getFieldName
());
}
}
if
(
null
!=
result
&&
null
!=
targetType
)
{
// 尝试将结果转换为目标类型,如果转换失败,返回null,即跳过此属性值。
// 来自:issues#I41WKP@Gitee,当忽略错误情况下,目标类型转换失败应返回null
// 如果返回原值,在集合注入时会成功,但是集合取值时会报类型转换错误
return
Convert
.
convertWithCheck
(
targetType
,
result
,
null
,
ignoreError
);
}
return
result
;
}
/**
* 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
*
* @param checkTransient 是否检查Transient关键字或注解
* @return 是否可读
* @since 5.4.2
*/
public
boolean
isWritable
(
boolean
checkTransient
)
{
// 检查是否有getter方法或是否为public修饰
if
(
null
==
this
.
setter
&&
false
==
ModifierUtil
.
isPublic
(
this
.
field
))
{
return
false
;
}
// 检查transient关键字和@Transient注解
if
(
checkTransient
&&
isTransientForSet
())
{
return
false
;
}
// 检查@PropIgnore注解
return
false
==
isIgnoreSet
();
}
/**
* 设置Bean的字段值<br>
* 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值<br>
* 此方法不检查任何注解,使用前需调用 {@link #isWritable(boolean)} 检查是否可写
*
* @param bean Bean对象
* @param value 值,必须与字段值类型匹配
* @return this
* @since 4.0.5
*/
public
PropDesc
setValue
(
Object
bean
,
Object
value
)
{
if
(
null
!=
this
.
setter
)
{
ReflectUtil
.
invoke
(
bean
,
this
.
setter
,
value
);
}
else
if
(
ModifierUtil
.
isPublic
(
this
.
field
))
{
ReflectUtil
.
setFieldValue
(
bean
,
this
.
field
,
value
);
}
return
this
;
}
/**
* 设置属性值,可以自动转换字段类型为目标类型
*
* @param bean Bean对象
* @param value 属性值,可以为任意类型
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
* @return this
* @since 5.4.2
*/
public
PropDesc
setValue
(
Object
bean
,
Object
value
,
boolean
ignoreNull
,
boolean
ignoreError
)
{
return
setValue
(
bean
,
value
,
ignoreNull
,
ignoreError
,
true
);
}
/**
* 设置属性值,可以自动转换字段类型为目标类型
*
* @param bean Bean对象
* @param value 属性值,可以为任意类型
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
* @param override 是否覆盖目标值,如果不覆盖,会先读取bean的值,{@code null}则写,否则忽略。如果覆盖,则不判断直接写
* @return this
* @since 5.7.17
*/
public
PropDesc
setValue
(
Object
bean
,
Object
value
,
boolean
ignoreNull
,
boolean
ignoreError
,
boolean
override
)
{
if
(
null
==
value
&&
ignoreNull
)
{
return
this
;
}
// issue#I4JQ1N@Gitee
// 非覆盖模式下,如果目标值存在,则跳过
if
(
false
==
override
&&
null
!=
getValue
(
bean
))
{
return
this
;
}
// 当类型不匹配的时候,执行默认转换
if
(
null
!=
value
)
{
final
Class
<?>
propClass
=
getFieldClass
();
if
(
false
==
propClass
.
isInstance
(
value
))
{
value
=
Convert
.
convertWithCheck
(
propClass
,
value
,
null
,
ignoreError
);
}
}
// 属性赋值
if
(
null
!=
value
||
false
==
ignoreNull
)
{
try
{
this
.
setValue
(
bean
,
value
);
}
catch
(
Exception
e
)
{
if
(
false
==
ignoreError
)
{
throw
new
BeanException
(
e
,
"Set value of [{}] error!"
,
getFieldName
());
}
// 忽略注入失败
}
}
return
this
;
}
//------------------------------------------------------------------------------------ Private method start
/**
* 通过Getter和Setter方法中找到属性类型
*
* @param getter Getter方法
* @param setter Setter方法
* @return {@link Type}
*/
private
Type
findPropType
(
Method
getter
,
Method
setter
)
{
Type
type
=
null
;
if
(
null
!=
getter
)
{
type
=
TypeUtil
.
getReturnType
(
getter
);
}
if
(
null
==
type
&&
null
!=
setter
)
{
type
=
TypeUtil
.
getParamType
(
setter
,
0
);
}
return
type
;
}
/**
* 通过Getter和Setter方法中找到属性类型
*
* @param getter Getter方法
* @param setter Setter方法
* @return {@link Type}
*/
private
Class
<?>
findPropClass
(
Method
getter
,
Method
setter
)
{
Class
<?>
type
=
null
;
if
(
null
!=
getter
)
{
type
=
TypeUtil
.
getReturnClass
(
getter
);
}
if
(
null
==
type
&&
null
!=
setter
)
{
type
=
TypeUtil
.
getFirstParamClass
(
setter
);
}
return
type
;
}
/**
* 检查字段是否被忽略写,通过{@link PropIgnore} 注解完成,规则为:
* <pre>
* 1. 在字段上有{@link PropIgnore} 注解
* 2. 在setXXX方法上有{@link PropIgnore} 注解
* </pre>
*
* @return 是否忽略写
* @since 5.4.2
*/
private
boolean
isIgnoreSet
()
{
return
AnnotationUtil
.
hasAnnotation
(
this
.
field
,
PropIgnore
.
class
)
||
AnnotationUtil
.
hasAnnotation
(
this
.
setter
,
PropIgnore
.
class
);
}
/**
* 检查字段是否被忽略读,通过{@link PropIgnore} 注解完成,规则为:
* <pre>
* 1. 在字段上有{@link PropIgnore} 注解
* 2. 在getXXX方法上有{@link PropIgnore} 注解
* </pre>
*
* @return 是否忽略读
* @since 5.4.2
*/
private
boolean
isIgnoreGet
()
{
return
AnnotationUtil
.
hasAnnotation
(
this
.
field
,
PropIgnore
.
class
)
||
AnnotationUtil
.
hasAnnotation
(
this
.
getter
,
PropIgnore
.
class
);
}
/**
* 字段和Getter方法是否为Transient关键字修饰的
*
* @return 是否为Transient关键字修饰的
* @since 5.3.11
*/
private
boolean
isTransientForGet
()
{
boolean
isTransient
=
ModifierUtil
.
hasModifier
(
this
.
field
,
ModifierUtil
.
ModifierType
.
TRANSIENT
);
// 检查Getter方法
if
(
false
==
isTransient
&&
null
!=
this
.
getter
)
{
isTransient
=
ModifierUtil
.
hasModifier
(
this
.
getter
,
ModifierUtil
.
ModifierType
.
TRANSIENT
);
// 检查注解
if
(
false
==
isTransient
)
{
isTransient
=
AnnotationUtil
.
hasAnnotation
(
this
.
getter
,
Transient
.
class
);
}
}
return
isTransient
;
}
/**
* 字段和Getter方法是否为Transient关键字修饰的
*
* @return 是否为Transient关键字修饰的
* @since 5.3.11
*/
private
boolean
isTransientForSet
()
{
boolean
isTransient
=
ModifierUtil
.
hasModifier
(
this
.
field
,
ModifierUtil
.
ModifierType
.
TRANSIENT
);
// 检查Getter方法
if
(
false
==
isTransient
&&
null
!=
this
.
setter
)
{
isTransient
=
ModifierUtil
.
hasModifier
(
this
.
setter
,
ModifierUtil
.
ModifierType
.
TRANSIENT
);
// 检查注解
if
(
false
==
isTransient
)
{
isTransient
=
AnnotationUtil
.
hasAnnotation
(
this
.
setter
,
Transient
.
class
);
}
}
return
isTransient
;
}
//------------------------------------------------------------------------------------ Private method end
}
hutool-core/src/main/java/cn/hutool/core/bean/copier/AbsCopier.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean.copier
;
import
cn.hutool.core.lang.copier.Copier
;
import
cn.hutool.core.util.ObjectUtil
;
/**
* 抽象的对象拷贝封装,提供来源对象、目标对象持有
*
* @param <S> 来源对象类型
* @param <T> 目标对象类型
* @author looly
* @since 5.8.0
*/
public
abstract
class
AbsCopier
<
S
,
T
>
implements
Copier
<
T
>
{
protected
final
S
source
;
protected
final
T
target
;
/**
* 拷贝选项
*/
protected
final
CopyOptions
copyOptions
;
public
AbsCopier
(
S
source
,
T
target
,
CopyOptions
copyOptions
)
{
this
.
source
=
source
;
this
.
target
=
target
;
this
.
copyOptions
=
ObjectUtil
.
defaultIfNull
(
copyOptions
,
CopyOptions:
:
create
);
}
}
hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
0 → 100644
View file @
45cda665
package
cn.hutool.core.bean.copier
;
import
cn.hutool.core.lang.Assert
;
import
cn.hutool.core.lang.copier.Copier
;
import
java.io.Serializable
;
import
java.lang.reflect.Type
;
import
java.util.Map
;
/**
* Bean拷贝,提供:
*
* <pre>
* 1. Bean 转 Bean
* 2. Bean 转 Map
* 3. Map 转 Bean
* 4. Map 转 Map
* </pre>
*
* @author looly
*
* @param <T> 目标对象类型
* @since 3.2.3
*/
public
class
BeanCopier
<
T
>
implements
Copier
<
T
>,
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
final
Copier
<
T
>
copier
;
/**
* 创建BeanCopier
*
* @param <T> 目标Bean类型
* @param source 来源对象,可以是Bean或者Map
* @param target 目标Bean对象
* @param copyOptions 拷贝属性选项
* @return BeanCopier
*/
public
static
<
T
>
BeanCopier
<
T
>
create
(
Object
source
,
T
target
,
CopyOptions
copyOptions
)
{
return
create
(
source
,
target
,
target
.
getClass
(),
copyOptions
);
}
/**
* 创建BeanCopier
*
* @param <T> 目标Bean类型
* @param source 来源对象,可以是Bean或者Map
* @param target 目标Bean对象
* @param destType 目标的泛型类型,用于标注有泛型参数的Bean对象
* @param copyOptions 拷贝属性选项
* @return BeanCopier
*/
public
static
<
T
>
BeanCopier
<
T
>
create
(
Object
source
,
T
target
,
Type
destType
,
CopyOptions
copyOptions
)
{
return
new
BeanCopier
<>(
source
,
target
,
destType
,
copyOptions
);
}
/**
* 构造
*
* @param source 来源对象,可以是Bean或者Map
* @param target 目标Bean对象
* @param targetType 目标的泛型类型,用于标注有泛型参数的Bean对象
* @param copyOptions 拷贝属性选项
*/
public
BeanCopier
(
Object
source
,
T
target
,
Type
targetType
,
CopyOptions
copyOptions
)
{
Assert
.
notNull
(
source
,
"Source bean must be not null!"
);
Assert
.
notNull
(
target
,
"Target bean must be not null!"
);
Copier
<
T
>
copier
;
if
(
source
instanceof
Map
)
{
if
(
target
instanceof
Map
)
{
//noinspection unchecked
copier
=
(
Copier
<
T
>)
new
MapToMapCopier
((
Map
<?,
?>)
source
,
(
Map
<?,
?>)
target
,
targetType
,
copyOptions
);
}
else
{
copier
=
new
MapToBeanCopier
<>((
Map
<?,
?>)
source
,
target
,
targetType
,
copyOptions
);
}
}
else
if
(
source
instanceof
ValueProvider
){
//noinspection unchecked
copier
=
new
ValueProviderToBeanCopier
<>((
ValueProvider
<
String
>)
source
,
target
,
targetType
,
copyOptions
);
}
else
{
if
(
target
instanceof
Map
)
{
//noinspection unchecked
copier
=
(
Copier
<
T
>)
new
BeanToMapCopier
(
source
,
(
Map
<?,
?>)
target
,
targetType
,
copyOptions
);
}
else
{
copier
=
new
BeanToBeanCopier
<>(
source
,
target
,
targetType
,
copyOptions
);
}
}
this
.
copier
=
copier
;
}
@Override
public
T
copy
()
{
return
copier
.
copy
();
}
}
Prev
1
…
5
6
7
8
9
10
11
12
13
…
19
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment