大连建设科技网站apmserv访问本地网站

当前位置: 首页 > news >正文

大连建设科技网站,apmserv访问本地网站,阳江房产信息网官网,策划品牌全案在我们开发过程中常常有一个需求#xff0c;就是要知道实体类中Getter方法对应的属性名称#xff08;Field Name#xff09;#xff0c;例如实体类属性到数据库字段的映射#xff0c;我们常常是硬编码指定 属性名#xff0c;这种硬编码有两个缺点。 1、编码效率低#x…在我们开发过程中常常有一个需求就是要知道实体类中Getter方法对应的属性名称Field Name例如实体类属性到数据库字段的映射我们常常是硬编码指定 属性名这种硬编码有两个缺点。 1、编码效率低因为要硬编码写属性名很可能写错需要非常小心时间浪费在了不必要的检查上。 2、容易让开发人员踩坑例如有一天发现实体类中Field Name定义的不够明确希望换一个Field Name那么代码所有硬编码的Field Name都要跟着变更对于未并更的地方是无法在编译期发现的。只要有未变更的地方都可能导致bug的出现。 而使用了方法引用后如果Field Name变更及其对应的Getter/Setter方法变更编译器便可以实时的帮助我们检查变更的代码在编译器给出错误信息。 那么如何通过方法引用获取Getter方法对应的Field Name呢 Java8中给我们提供了实现方式首先要做的就是定义一个可序列化的函数式接口(实现Serializable)实现如下 /*** Created by bruce on 2020/4/10 14:16*/ FunctionalInterface public interface SerializableFunctionT, R extends FunctionT, R, Serializable {}而在使用时,我们需要传递Getter方法引用 //方法引用 SerializableFunctionPeople, String getName1 People::getName; Field field ReflectionUtil.getField(getName1);下面看具体怎么解析这个SerializableFunction完整实现如下ReflectionUtil public class ReflectionUtil {private static MapSerializableFunction?, ?, Field cache new ConcurrentHashMap();public static T, R String getFieldName(SerializableFunctionT, R function) {Field field ReflectionUtil.getField(function);return field.getName();}public static Field getField(SerializableFunction?, ? function) {return cache.computeIfAbsent(function, ReflectionUtil::findField);}public static Field findField(SerializableFunction?, ? function) {Field field null;String fieldName null;try {// 第1步 获取SerializedLambdaMethod method function.getClass().getDeclaredMethod(writeReplace);method.setAccessible(Boolean.TRUE);SerializedLambda serializedLambda (SerializedLambda) method.invoke(function);// 第2步 implMethodName 即为Field对应的Getter方法名String implMethodName serializedLambda.getImplMethodName();if (implMethodName.startsWith(get) implMethodName.length() 3) {fieldName Introspector.decapitalize(implMethodName.substring(3));} else if (implMethodName.startsWith(is) implMethodName.length() 2) {fieldName Introspector.decapitalize(implMethodName.substring(2));}// 第3步 获取的Class是字符串并且包名是“/”分割需要替换成“.”才能获取到对应的Class对象String declaredClass serializedLambda.getImplClass().replace(/, .);Class? aClass Class.forName(declaredClass, false, ClassUtils.getDefaultClassLoader());// 第4步 Spring 中的反射工具类获取Class中定义的Fieldfield ReflectionUtils.findField(aClass, fieldName);} catch (Exception e) {e.printStackTrace();}// 第5步 如果没有找到对应的字段应该抛出异常if (field ! null) {return field;}throw new NoSuchFieldError(fieldName);} }该类中主要有如下三个方法 String getFieldName(SerializableFunction function) 获取Field的字符串nameField getField(SerializableFunction function)从缓存中查询方法引用对应的Field,如果没有则通过findField(SerializableFunction function)方法反射获取Field findField(SerializableFunction function) 反射获取方法应用对应的Field 实现原理 1、首先我们看最后一个方法Field findField(SerializableFunction function)该方法中第一步是通过SerializableFunction对象获取Class即传递的方法引用然后反射获取writeReplace()方法,并调用该方法获取导SerializedLambda对象。 2、SerializedLambda是Java8中提供主要就是用于封装方法引用所对应的信息主要的就是方法名、定义方法的类名、创建方法引用所在类。 3、拿到这些信息后便可以通过反射获取对应的Field。 4、而在方法Field getField(SerializableFunction function)中对获取到的Field进行缓存避免每次都反射获取造成资源浪费。 除此之外似乎还有一些值得思考的问题 writeReplace()方法是哪来的呢? 首先简单了解一下java.io.Serializable接口该接口很常见我们在持久化一个对象或者在RPC框架之间通信使用JDK序列化时都会让传输的实体类实现该接口该接口是一个标记接口没有定义任何方法但是该接口文档中有这么一段描述 Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private, protected and package-private access. Subclass access to this method follows java accessibility rules.概要意思就是说如果想在序列化时改变序列化的对象可以通过在实体类中定义任意访问权限的Object writeReplace()来改变默认序列化的对象。 那么我们的定义的SerializableFunction中并没有定义writeReplace()方法这个方法是哪来的呢 代码中SerializableFunction,Function只是一个接口但是其在最后必定也是一个实现类的实例对象而方法引用其实是在运行时动态创建的当代码执行到方法引用时,如People::getName最后会经过 java.lang.invoke.LambdaMetafactory java.lang.invoke.InnerClassLambdaMetafactory 去动态的创建实现类。而在动态创建实现类时则会判断函数式接口是否实现了Serializable如果实现了则添加writeReplace()
值得注意的是代码中多次编写的同一个方法引用他们创建的是不同Function实现类即他们的Function实例对象也并不是同一个 我们可以通过如下属性配置将 动态生成的Class保存到 磁盘上 System.setProperty(jdk.internal.lambda.dumpProxyClasses, .);示例代码如下: 动态生成的Class如下:
一个方法引用创建一个实现类他们是不同的对象那么ReflectionUtil中将SerializableFunction最为缓存key还有意义吗 答案是肯定有意义的因为同一方法中的定义的Function只会动态的创建一次实现类并只实例化一次当该方法被多次调用时即可走缓存中查询该方法引用对应的Field 这里的缓存Key应该选用SerializableFunction#Class还是SerializableFunction实例对象好呢? 看到有些实现使用SerializableFunction的Class作为缓存key代码如下 public static Field getField(SerializableFunction?, ? function) {//使用SerializableFunction的Class作为缓存key导致每次都调用function.getClass()return cache.computeIfAbsent(function.getClass(), ReflectionUtil::findField); }但是个人建议采用SerializableFunction对象因为无论方法被调用多少次方法代码块内的方法引用对象始终是同一个如果采用其Class做为缓存key每次查询缓存时都需要调用native方法function.getClass()获取其Class也是一种资源损耗。 总结Java如何通过方法引用获取属性名实现及思考至此结束。直接使用ReflectionUtil即可