使用Java注解自动化处理对应关系实现注释代码化

java

概述###

假设我们要从一个 ES 索引(相当于一张DB表)查询数据,ES表有 biz_no, type, status 等字段, 而应用对象则有属性 bizNo, type, status 等。这样,就会面临“将应用对象的属性与ES字段对应起来”的问题(实际上是个ORM)。

固然可以通过注释来说明,不过这样显得比较生硬。因为注释并不起实际作用,代码里还得写一套映射关系,就会存在注释与代码不一致的情况。 那么,是否可以将这种对应关系的注释用代码形式来解决呢? Java 注解可以解决这个问题。

实现###

定义注解####

首先定义注解类。注解类需要提供对应的ES字段名 name、类型 type 以及是否必传 required。

  • @Retention 指明注解在何时起作用,这里是在运行时。
  • @Target 指明注解应用于何种对象,这里应用于字段。

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.FIELD)

@Documented

public @interface EsField {

/**

* 对应的ES字段名

*/

String name();

/**

* 对应的ES字段的值类型

* @return

*/

String type() default "";

/**

* 是否必传

*/

boolean required() default false;

}

应用领域对象####

接着,将注解应用到应用领域对象。为简洁,应用领域对象只有四个字段。

@Data

public class CustomerDomain implements DomainSearch {

/** 主键ID */

@EsField(name="id", required = true)

private Long id;

/** 业务编号 */

@EsField(name="biz_no")

private String bizNo;

/** 状态 */

@EsField(name="status", type="list")

private List<Integer> status;

/** 类型 */

@EsField(name="type", type="list")

private List<Integer> type;

}

注解解析器####

接着,需要提供注解解析器,将对应的映射关系转成ES查询对象的一部分。

  • 使用接口的默认方法来实现,是为了支持不同的业务类自动可以转化为ES查询串;
  • 注解解析器需要使用Java反射机制,来获取相应的字段,以及字段上的注解定义,然后根据字段的类型、值、注解定义来做相应处理;
  • 使用反射来处理字段时,由于字段一般是私有的,因此必须先设置为可访问的,处理完成后还原为不可访问;
  • EsField field = f.getAnnotation(EsField.class) 用来获取字段上的注解信息(name, type, required);Object value = f.get(customerDomain) 用来获取字段的值;字段的其他类型信息可以通过 Field 的方法拿到。

public interface DomainSearch {

Log logger = LogFactory.getLog(DomainSearch.class);

default String toEsQuery() {

Object customerDomain = this;

EsQuery esQuery = new EsQuery();

Field[] fields = this.getClass().getDeclaredFields();

for (Field f: fields) {

try {

if (Modifier.isStatic(f.getModifiers())) {

continue;

}

f.setAccessible(true);

Object value = f.get(customerDomain);

if (f.getAnnotation(EsField.class) != null) {

EsField field = f.getAnnotation(EsField.class);

if (field.required() && value == null) {

throw new RuntimeException("field '" + field + "' is required. value is null");

}

if (isNeedOmitted(value)) {

f.setAccessible(false);

continue;

}

if ((value instanceof List) && ((List)value).size() == 1) {

// 针对 List 中单个值做优化查询

esQuery = esQuery.addTermFilter(field.name(), ((List)value).get(0));

}

else {

esQuery = esQuery.addTermFilter(field.name(), value);

}

}

f.setAccessible(false);

} catch (Exception ex) {

logger.error("failed to build es query for field: " + f.getName(), ex);

throw new RuntimeException(ex.getCause());

}

}

return esQuery.toJsonString();

}

/**

* 判断是否需要忽略该字段的查询

* @param value 字段值

* @return 是否要忽略

*/

default boolean isNeedOmitted(Object value) {

if (value == null) {

return true;

}

// 空字符串搜索值忽略

if ((value instanceof String) && StringUtils.isBlank(value.toString())) {

return true;

}

// 空列表串忽略

if ((value instanceof List) && ((List)value).isEmpty()) {

return true;

}

return false;

}

}

限于公司的源代码不公开原则,这里 EsQuery 实现就不给出了;且 EsQuery 的实现并不是本文的重点。

小结###

通过ES搜索示例,展示了如何运用注解自动化处理领域对象属性与底层ES存储字段之间的对应关系。实际上,如果想为应用对象或组件添加某种说明或注释,不妨先想想是否可以通过注解自动化处理。注解亦可用于框架自动处理对象与组件的集成。Spring框架的Resource, Component, AOP,以及 Plugin 化设计思想等都是好的应用例子。

如果有注释,那就让它代码化。

以上是 使用Java注解自动化处理对应关系实现注释代码化 的全部内容, 来源链接: utcz.com/z/393380.html

回到顶部