lombok + Orika + Builder Pattern

Reference


Introduction

  1. Builder Pattern is suggested in Effective Java and used to generate immutable object.
  2. Builder Pattern introduces many codes, lombok project makes it easier
  3. We often use Orika to map data when transfer object from UI entity to domain entity or data persistence object to domain entity.
  4. It's hard to map Java Bean to Builder Pattern object because Builder Pattern doesn't follows Java Bean convention. It causes we can't leverage lombok and Orika at the same time

How to fix

  1. Copy and paste the following code: BuilderPropertyResolver
  2. /**
     * This class is used to map from an object to a builder who follows Builder Pattern.
     * 
     * <pre>
     * MapperFactory factory = new DefaultMapperFactory.Builder()
     *  .propertyResolverStrategy(new BuilderPropertyResolver())
     *  .build();
     *  factory.registerObjectFactory((Object o, MappingContext mappingContext) 
     *  -&gt; new MyBuilder(), TypeFactory.valueOf(MyBuilder.class));
     *  </pre>
     */
    public class BuilderPropertyResolver extends IntrospectorPropertyResolver {
    
     private static final Logger logger = LoggerFactory.getLogger(BuilderPropertyResolver.class);
     
     @Override
     protected void collectProperties(Class<?> type, Type<?> referenceType, Map<String, Property> properties) {
      super.collectProperties(type, referenceType, properties);
      if (StringUtils.endsWith(type.getName(), "Builder")) {
       Set<String> fieldNames = Arrays.stream(type.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet());
       Arrays.stream(type.getDeclaredMethods())
         .filter(method -> fieldNames.contains(method.getName()))
         .filter(method -> isWriteMethodInBuilder(type, method))
         .forEach(method -> {
          Property.Builder builder = new Property.Builder();
          builder.expression(method.getName());
          builder.name(method.getName());
          builder.setter(method.getName() + "(%s)");
          Class<?> fieldType = method.getParameterTypes()[0];
          builder.type(this.resolvePropertyType(null, fieldType, type, referenceType));
          Property property = builder.build(this);
          properties.put(method.getName(), property);
         });
      }
     }
    
     private boolean isWriteMethodInBuilder(Class<?> type, Method method) {
      try {
       Class<?> returnType = method.getReturnType();
       Class<?> fieldType = type.getDeclaredField(method.getName()).getType();
       boolean onlyOneParameter = method.getParameterTypes().length == 1;
       boolean methodReturnBuilderType = returnType.equals(type);
       Class<?> methodParamType = method.getParameterTypes()[0];
       boolean isFieldSetter = fieldType.equals(methodParamType);
       return methodReturnBuilderType && onlyOneParameter && isFieldSetter;
      } catch (NoSuchFieldException e) {
       e.printStackTrace();
       return false;
      }
     }
    
    }
    
  3. Leverage this object
    public class Mapper {
     
     public static void main(String[] params) {
      MapperFactory factory = new DefaultMapperFactory.Builder()
        .propertyResolverStrategy(new BuilderPropertyResolver())
        .build();
      factory.registerObjectFactory((Object o, MappingContext mappingContext) 
        -> Person.builder(), TypeFactory.valueOf(Person.PersonBuilder.class));
      
      
      PersonBO bo = new PersonBO();
      bo.setName("TEST");
      factory.classMap(PersonBO.class, Person.class);
      System.out.println(factory.getMapperFacade().map(bo, Person.PersonBuilder.class).build());
      
      Person.PersonBuilder builder = Person.builder().list(Arrays.asList("A"));
      factory.getMapperFacade().map(bo, builder);
      System.out.println(builder);
     }
    
     @Data
     public static class PersonBO {
      private String name;
      private List<String> list;
     }
     
     @ToString
     @Builder
     public static class Person {
      private String name;
      private List<String> list;
     }
     
    }
    

別名演算法 Alias Method

 題目 每個伺服器支援不同的 TPM (transaction per minute) 當 request 來的時候, 系統需要馬上根據 TPM 的能力隨機找到一個適合的 server. 雖然稱為 "隨機", 但還是需要有 TPM 作為權重. 解法 別名演算法...