新手原创,不喜轻喷,欢迎交流。
最近项目交付了,想着项目中遇到的一个问题,在传递返回的包装类给app端的时候有的字段位是null,如果这个字段app端需要其中的值,如果获取的值是null的时候会导致app的闪退,为了解决这个问题,我不得不把所有需要传递给app端且有可能为空的字段一个一个赋“”或者0等,为了简便,我把返回的包装类的数据类型只保留String和Integer.写出了以下方法:
@SuppressWarnings("unused")
public class ParameterInit {
public static
Class extends Object> c = o.getClass(); //获取要返回的类的class
Method[] methods = c.getMethods(); //获取其中的方法(自定义的方法都是public)
for (Method m : methods) {
String methodName = m.getName(); //获取方法名
if(methodName.startsWith("set")) { //判断方法前缀(整个操作只有set/get有关)
String getMethod = methodName.substring(3); //截取除set外的其余部分
Method method = c.getMethod("get"+getMethod ); //拼成get就变成了这个属性的get方法
Object invoke = method.invoke(o); //因为get方法是没有参数的,所以直接传obj进去运行方法
if( invoke == null ) { //判断当前方法的返回值
Type[] genericParameterTypes = m.getGenericParameterTypes(); //获取set方法的参数类型
if( genericParameterTypes[0] == String.class) { //匹配参数类型}
注:以上操作的参数Object是经过springframework的BeanUtils.copyProperties赋值过后的类
做完这些之后高兴的和领导说了一下我做了一个工具类,然后领导说就这个问题把copyProperties研究一下,做一个类似的吧
下面就说说对copyProperties的研究心得,做为一个新手,写的不好大家不要喷,谢谢
先说一下spring源在下载,用git下的方式是
git clone https://github.com/spring-projects/spring-framework.git
spring源码是用gradle写的,所以需要用gradle编译才能放到eclipse中,gradle安装这个大家百度吧
然后用gradle编译的命令是 gradle cleanidea eclipse
以下是copyProperties的源码
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, null, (String[]) null);
}
可以看到,这里面只是一个方法的调用
private static void copyProperties(Object source, Object target, Class> editable, String... ignoreProperties)
throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class> actualEditable = target.getClass();首先Assert.notNull这是spring-core包中的一个类,用于判断对象是否为null,为null抛出异常
Class> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
这一段是获取目标bean的class和对第三个参数的判断,第三个参数字面意思是editable是target的实例,不是就抛异常
平时没用过,这次没有深入研究、
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
getPropertyDescriptors是BeanUtils中的另一个静态方法
public static PropertyDescriptor[] getPropertyDescriptors(Class> clazz) throws BeansException {
CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
return cr.getPropertyDescriptors();
}
CachedIntrospectionResults.forClass调用的是CachedIntrospectionResults类的方法
/**
* Create CachedIntrospectionResults for the given bean class.
* @param beanClass the bean class to analyze
* @return the corresponding CachedIntrospectionResults
* @throws BeansException in case of introspection failure
*/
@SuppressWarnings("unchecked")
static CachedIntrospectionResults forClass(Class> beanClass) throws BeansException {
CachedIntrospectionResults results = strongClassCache.get(beanClass);
if (results != null) {
return results;
}
results = softClassCache.get(beanClass);
if (results != null) {
return results;
}
results = new CachedIntrospectionResults(beanClass);
ConcurrentMap
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
isClassLoaderAccepted(beanClass.getClassLoader())) {
classCacheToUse = strongClassCache;
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}
classCacheToUse = softClassCache;
}
CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results);
return (existing != null ? existing : results);
}
CachedIntrospectionResults.forClass这个方法我找了很多资料都没有很明白,综合这些资料和这个类上面的注解,初步了解到这个类是一个”自省缓存结果集“
补充一些资料说明就是,这里copyProperties用到的是javaBean中的自省,自省和反射有什么区别呢?
====================================================================================
摘自 https://blog.csdn.net/zhanjixun/article/details/68256724
List
这里分析的是忽略的字段,就是那些你不想操作的字段!
for (PropertyDescriptor targetPd : targetPds) {
//获取字段的写方法,--set
Method writeMethod = targetPd.getWriteMethod();
//如果方法不为空,且没有忽略
if (writeMethod != null && (ignoreList == null || (!ignoreList.contains(targetPd.getName())))) {
//获取原对象的字段
if (sourcePd != null) {
//如果原对象的字段是存在的,获取原对象的这个字段的get方法
if (readMethod != null &&
//判断源对象的get的返回值类型和目标对象的set参数是否一致
try {
//如果类型一致再判断方法是不是public修饰的
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
//如果不是就强制反射
}
//然后获取值
}
//赋值
这中间还穿插了好多个方法,没有详细贴上来,在文章最后我会贴一张大图,有兴趣的可以看看
一段代码跑下来感觉脑细胞死了不少,似乎头发掉了几根。。。好复杂的样子,所以我决定使用普通反射,代码如下:
基于目标类中的字段全是String类型的来做的
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import com.financial.core.SuperStringUtils;
import com.financial.core.date.DateUtil;
import com.financial.core.exception.ServiceException;
public class BeanCopyUtils {
public static void copyProperties(Object source,Object target) {
try {
copyProperties(source, target,(String[]) null);
} catch (ServiceException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void copyProperties(Object source,Object target,String... ignoreProperties) throws ServiceException, Exception {
if(SuperStringUtils.isBlank(source) || SuperStringUtils.isBlank(target)) {
throw new ServiceException("500","源类或目标类不能为空");
}
Class> targetClass = target.getClass();
Class> sourceClass = source.getClass();
Field[] targetFields = targetClass.getDeclaredFields();
Field[] sourceFields = sourceClass.getDeclaredFields();
ArrayList
for (Field field : targetFields) {
String targetMethod = obtainSetMethod(field);
Method method = targetClass.getMethod(targetMethod, String.class);
//如果源类中有这个字段,赋值源类中的值给目标类
if(checkField(field,fieldList)){
Method fieldMethod = sourceClass.getMethod(obtainGetMethod(field));
//获取源对象中get方法的返回值
Object sourceResult = fieldMethod.invoke(source);}