传送门
在比较早的时候,就讨论过java反射的一些用法及概念:
Java反射系列(1):入门基础
以及反射的基石Class对象!
Java反射系列(2):从Class获取父类方法说起
今天就从工作中实际的例子来看看反射的应用。
兼容性引发的"血案"
对于Java程序员来说,调用别人写的类库、使用开源的Jar包似乎无可厚非,甚至是"天经地义"!正是开源的这种无私精神带来了这门语言的繁荣,打造了其遍地开花的生态。而github更是诸多极客们一展风采的宝库舞台,但是这也带来了问题:里面的各种库质量良莠不齐,既有spring这种Java体系"基石"类的产品,也有很多"小而美"的工具包,比如hutool包!
但凡事有利就有弊,最近系统就出现了因为升级hutool包导致功能不可用的场景:
系统里面有一个对db数据自动加解密Mybatis插件,存储时将表里面的数据自动加密,读取时自动解密。其实现原理就是通过Mybatis的拦截器,拦截请求之后,得到原始里面数据,加解密之后用反射机制替换原始数据以此实现动态加解密功能。功能并不复杂,抛开效率性能等因素之外,也是一个比较合理的数据"脱敏"实现机制。但是里面的的反射机制用到了hutool的包,升级时版本不兼容导致功能不可能用,最终用sprig包的ReflectionUtils工具进行了紧急替换,评估之后最终移除了该hutool的引用,后续不再使用!
由此可见,一个好的开源产品,除了功能强大易用之外,升级的版本兼容性也是必不可少的条件之一。这里引用一下网上对此的观点:
ReflectionUtils的原理
对于上面提到的加解密插件,后续可以提供一个实现到码云供参考,这里仅仅表明它是反射机制使用的一个具体场景,不是这里的重点,就不做过多讨论。
目的有三
而这里重点目标有三个:
- 使用开源包时要慎重,尤其是在生产级应用,越是大公司对此要求越严格
- ReflectionUtils的使用,熟悉一下里面的API
- 最终了解反射的用途,深入反射机制
ReflectionUtils的API使用
ReflectionUtils是spring的核心包提供工具类,路径为:org.springframework.util.ReflectionUtils。
在Java反射系列(2):从Class获取父类方法说起里面讨论了Class对象,所以这里重点看看另外几个比较重要的对象:Method,Field。
还是以前面例子的一个简单请求类为例,父类BaseAuthReq :
public class BaseAuthReq
{
/** 应用ID */
private String clientId;
/** 应用身份密钥 */
private String clientSecret;
public String getClientId()
{
return clientId;
}
public void setClientId(String clientId)
{
this.clientId = clientId;
}
public String getClientSecret()
{
return clientSecret;
}
public void setClientSecret(String clientSecret)
{
this.clientSecret = clientSecret;
}
}
GetTokenReq继承BaseAuthReq:
public class GetTokenReq extends BaseAuthReq
{
/** 授权码类型 */
private String grantType;
/** 授权码 */
private String code;
public String getGrantType()
{
return grantType;
}
public void setGrantType(String grantType)
{
this.grantType = grantType;
}
public String getCode()
{
return code;
}
public void setCode(String code)
{
this.code = code;
}
}
Method
GetTokenReq类里面有2个属性,grantType、code及对应的get、set方法,那通过ReflectionUtils工具类如何获取这些方法呢?
获取所有声明的方法getAllDeclaredMethods
观察下它的API,里面有一个方法,看起来可以满足需求:
public static Method[] getAllDeclaredMethods(Class<?> leafClass)
现在写一个测试方法来验证一下:
@Test
public void testMethod() {
// 获取当前类的Class对象
Class clazz = GetTokenReq.class;
System.out.println("clazz:" + clazz.getName());
Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : allDeclaredMethods) {
System.out.println(method.getName());
}
}
运行一下,输出打印结果:
clazz:com.tw.tsm.auth.dto.request.GetTokenReq
// 以下4个为目标方法
setGrantType
getCode
getGrantType
setCode
getClientId
setClientId
setClientSecret
getClientSecret
finalize
wait
wait
wait
equals
toString
hashCode
getClass
clone
notify
notifyAll
registerNatives
从输出可以看出,GetTokenReq类里面2个属性grantType、code对应的get、set方法都打印出来了,但是除此以外,还多了很多方法,它们又是从哪里来的呢?
把上面的输出打印改一下:
@Test
public void testMethod()
{
// 获取当前类的Class对象
Class clazz = GetTokenReq.class;
System.out.println("clazz:" + clazz.getName());
Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : allDeclaredMethods)
{
// 输出方法所在类,及方法名称
System.out.println("class:" + method.getDeclaringClass().getName() + ", method:" + method.getName());
}
}
运行一下,输出打印结果:
clazz:com.tw.tsm.auth.dto.request.GetTokenReq
class:com.tw.tsm.auth.dto.request.GetTokenReq, method:getGrantType
class:com.tw.tsm.auth.dto.request.GetTokenReq, method:setCode
class:com.tw.tsm.auth.dto.request.GetTokenReq, method:getCode
class:com.tw.tsm.auth.dto.request.GetTokenReq, method:setGrantType
class:com.tw.tsm.auth.dto.BaseAuthReq, method:setClientId
class:com.tw.tsm.auth.dto.BaseAuthReq, method:setClientSecret
class:com.tw.tsm.auth.dto.BaseAuthReq, method:getClientSecret
class:com.tw.tsm.auth.dto.BaseAuthReq, method:getClientId
class:java.lang.Object, method:finalize
class:java.lang.Object, method:wait
class:java.lang.Object, method:wait
class:java.lang.Object, method:wait
class:java.lang.Object, method:equals
class:java.lang.Object, method:toString
class:java.lang.Object, method:hashCode
class:java.lang.Object, method:getClass
class:java.lang.Object, method:clone
class:java.lang.Object, method:notify
class:java.lang.Object, method:notifyAll
class:java.lang.Object, method:registerNatives
从上面的输出可以清晰的看到,getAllDeclaredMethods可以打印类自身及所有父类的方法: