Java 反射工具类
大约 3 分钟
Java 反射工具类
关于反射的一些常用操作。除了使用 cglib 生成新的 bean部分,大部分操作均为暴力访问取值()。
Maps
请查看这篇 Map工具类
Collections
getOne 方法只是对 list.stream.filter(fn).getOne().orElse(null) 的简单封装
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Predicate;
import org.springframework.lang.Nullable;
public final class Collections {
public static boolean isEmpty(Collection<?> c){
return c == null || c.size() == 0;
}
/**
* 如果有空值
*/
public static boolean isNull(Object... objects){
for (Object o : objects) {
if (o==null){
return true;
}
}
return false;
}
@SafeVarargs
public static <T> T getNotNull(T... arr){
for (T t : arr) {
if (t != null){
return t;
}
}
return null;
}
@Nullable
public static <T> T getOne(Collection<T> list, Predicate<T> predicate){
if (list==null){
return null;
}
return list.stream().filter(predicate).findFirst().orElse(null);
}
@Nullable
public static <T> T getOne(T[] list, Predicate<T> predicate){
if (list==null){
return null;
}
return Arrays.stream(list).filter(predicate).findFirst().orElse(null);
}
}
StringUtils
import java.util.Locale;
public class StringUtils {
public static String upperHump(String src){
return src.substring(0, 1).toUpperCase(Locale.ROOT) + src.substring(1);
}
public static String lowerHump(String src){
return src.substring(0, 1).toLowerCase(Locale.ROOT) + src.substring(1);
}
public static boolean notEmpty(String typeName) {
return typeName != null && !typeName.isEmpty();
}
/**
* 循环重复的字符片段
* @param frag 片段 eg:!
* @param nth 重复次数 eg: 3
* @return eg:!!!
*/
public static String replicate(String frag, int nth) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < nth; i++) result.append(frag);
return result.toString();
}
}
工具类
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;
/**
* 反射工具类
* @author sa@linkot.cn
*/
public class ReflectUtils {
static final Pattern METHOD_PATTERN = Pattern.compile("^(?<class>.*)\\.(?<method>[a-zA-Z0-9_]*)$");
/**
* 根据全限定名获取类名和方法名
* @param methodName 方法名,例: cn.linkot.project.domain.Human.getName
* @return cn.linkot.project.domain.Human, getName
*/
public static String[] extractNames(String methodName){
Matcher m = METHOD_PATTERN.matcher(methodName);
if (m.matches()){
return new String[]{m.group("class"), m.group("method")};
}else {
throw new IllegalArgumentException("匹配失败无效的方法名:"+methodName);
}
}
/**
* 根据全限定名获取类名
* @param methodName 方法名,例: cn.linkot.project.domain.Human.getName
* @return 类
*/
public static Class<?> getClassByMethodName(String methodName) throws ClassNotFoundException {
String className = METHOD_PATTERN.matcher(methodName).group("class");
return className != null ? Class.forName(className) : null;
}
public static Method getUniqueMethodByName(String methodName)
throws ClassNotFoundException {
String[] names = extractNames(methodName);
Class<?> clz = Class.forName(names[0]);
return Collections.getOne(clz.getDeclaredMethods(), e -> names[1].equals(e.getName()));
}
/**
* 获取对象的所有字段
* 包含父字段
*/
public static List<Field> getDeclaredFields(Class<?> clz){
List<Field> fields = Arrays.stream(clz.getDeclaredFields()).collect(Collectors.toList());
appendParentFields(clz, fields);
return fields;
}
/**
* 递归父类的所有字段并添加到 list
* @param clz 当前类
* @param list 要添加到的 list
*/
private static void appendParentFields(Class<?> clz, List<Field> list){
Class<?> superclass = clz.getSuperclass();
if (superclass ==null){
return;
}
list.addAll(Arrays.asList(superclass.getDeclaredFields()));
appendParentFields(superclass, list);
}
/**
* 创建新的 bean 并且添加新的自定义字段
* @param base 原方法
* @param dicts 新方法
* @return cglib 生成的新对象
*/
@SafeVarargs
private static Object genBean(List<Field> base, Dict<String, Object>... dicts){
//防止重复添加字段
Set<String> set = new HashSet<>();
BeanGenerator gen = new BeanGenerator();
gen.addProperty("originBean", Object.class);
for (Field field : base) {
String name = field.getName();
if (set.contains(name)){
continue;
}
gen.addProperty(name, field.getType());
set.add(name);
}
for (Dict<String, Object> dict : dicts) {
String name = dict.getKey();
if (set.contains(name)){
continue;
}
gen.addProperty(name, dict.getValue()!=null? dict.getValue().getClass() : Object.class);
set.add(name);
}
return gen.create();
}
/**
* 为对象添加新的字段并复制值
* 返回添加后生成的新对象
* originBean属性指向原对象
*/
@SafeVarargs
public static Object addFields(Object o, Dict<String, Object>... dicts)
throws IllegalAccessException {
Class<?> clz = o.getClass();
List<Field> fields = getDeclaredFields(clz);
BeanMap map = BeanMap.create(genBean(fields, dicts));
map.putAll(Maps.asMap(dicts));
map.put("originBean", o);
for (Field f : fields) {
String fieldName = f.getName();
try{
Method method = clz.getMethod("get" + StringUtils.upperHump(fieldName));
Object result = method.invoke(o);
if (result != null){
map.put(fieldName, result);
}
}catch (NoSuchMethodException | InvocationTargetException ignore){
}
}
return map.getBean();
}
}
关于 addFields(为对象添加新的字段并复制值/动态添加字段) 方法
- 该方法返回为通过 cglib 生成的新的bean,并不是在原对象上添加字段
- 新对象的 originBean 属性指向原对象
- 获取原对象字段值的部分为暴力访问
- 若原对象或原对象的父类有字段名和要新添加的字段名重复,则原对象的字段优先(新对象的字段类型和字段值都会为原对象一样)