最近业务有需求根据配置的表达式获取对应 Java Bean 中的字段值,开始的想法是自己写一个基于反射获取字段的工具类,然后再加上缓存,而后又想起 MyBatis 中的表达式解析,底层使用的 OGNL 库,于是查询了相关文档,决定直接使用 OGNL 库实现该需求,不再重复造轮子。
如果查看 MyBatis 的 pom.xml
,可以发现含有如下声明:
1 | <dependency> |
同时在 pom.xml
中还使用了 maven-shade-plugin
对 OGNL 的相关类进行重定位:
1 | <plugin> |
pom.xml
的源码可以参见:pom.xml,关于重定位类,我之前编写 Spark 应用也用到过,参见:Relocating Classes。
关于 OGNL 在 MyBatis 中的应用,主要是使用表达式获取相应对象中的值,代码位于 OgnlCache.java,比较简单,主要就是调用 OGNL 库实现,然后缓存了解析的表达式,这里就不贴了。我们主要看看 OGNL 底层的代码,此处记录最常见的获取对象字段值的实现,从最核心的 getValue
方法开始,源码位于 Ognl.java at master:
1 | /** |
如果是获取对象字段值,跟随源码,会调用至 ObjectPropertyAccessor
类的 getPossibleProperty
方法,源码位于 ObjectPropertyAccessor.java at master:
1 | /** |
可以看出,先尝试获取方法值,获取不到再获取字段值,其中获取方法是获取以 get
开头与属性名匹配的且参数个数为 0
的方法,获取字段是直接获取与属性名匹配的字段,中途解析了类的方法信息或字段信息后会将这些信息进行缓存,避免后续重复解析,最后调用反射对象的获取值方法获取的字段的值,该实现方式和之前预想的相同,附上相关源码 OgnlRuntime.java at master:
1 | public static Method getGetMethod( OgnlContext unused, Class<?> targetClass, String propertyName ) |
1 | public static Object getFieldValue( OgnlContext context, Object target, String propertyName, |
中途通过反射解析方法和字段时会用到 OgnlCache.java at master,以避免重复解析类。
Reference
OGNL - Apache Commons OGNL - Object Graph Navigation Library