注解五:字段翻译(注解+反射+动态属性)

0、需求

实现字段的自动翻译功能,例如sex=1表示男,region="1"表示国外。
在发送请求之后,将原本的字段和翻译字段都返回给前端。

例如:存在实体Payment,属性只包括(id、serial、region、sex)
调用接口时,自动返回如下内容,sexMeaning 和 regionMeaning
在这里插入图片描述

1、POM

其他POM如:jdbc、web、boot不列出来

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.2.4</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>

2、entity、controller、service、dao、mapper

2.1、entity

public class Payment implements Serializable {
    private Long id;
    private String serial;
    @TestAnnotation(renderers = RegionRender.class)
    private String region="1";
    @TestAnnotation(renderers = GenderRenderer.class)
    private Integer sex=1;

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getSerial() {
        return serial;
    }

    public void setSerial(String serial) {
        this.serial = serial;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}

2.2、controller

@RestController
public class PaymentController{

    @Resource
    private PaymentService paymentService;

    @PostMapping(value = "/payment/create")
    public Payment create(@RequestBody Payment payment)
    {
        return paymentService.create(payment);
    }
}

2.3、service

public interface PaymentService
{
    Payment create(Payment payment);
}
@Service
public class PaymentServiceImpl implements PaymentService
{
    @Resource
    private PaymentDao paymentDao;

    @Override
    public Payment create(Payment payment)
    {
        paymentDao.create(payment);
//        payment.setSex(0);
        return payment;
    }
}

2.4、dao

@Mapper
public interface PaymentDao
{
    int create(Payment payment);
}

2.5、mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.zhb.dao.PaymentDao">

    <insert id="create" parameterType="com.zhb.entity.Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial)  values(#{serial});
    </insert>
</mapper>

3、render(注解、接口、实现类)

3.1、注解@TestAnnotation

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
    Class<? extends ValueRenderer>[] renderers() default {};
}

3.2、接口ValueRenderer

public interface ValueRenderer {
    Object render(Object value, Object data);
}

3.3、RegionRender

public class RegionRender implements ValueRenderer {
    @Override
    public Object render(Object value, Object data) {
        if (value instanceof String) {
            String flag = (String) value;
            if ("0".equals(flag)) {
                return "国内";
            }
            return "国外";
        }
        return value;
    }
}

3.4、GenderRenderer

public class GenderRenderer implements ValueRenderer {
    @Override
    public Object render(Object value, Object data) {
        if (value instanceof Integer) {
            Integer longTermFlag = (Integer) value;
            if (Integer.valueOf(1).equals(longTermFlag)) {
                return "男";
            }
            return "女";
        }
        return value;
    }
}

4、aspect

@Aspect
@Component
public class PaymentAspect {
    private static final Logger logger = LoggerFactory.getLogger(PaymentAspect.class);

    @Around(
            value = "execution(* com.zhb.service.impl.PaymentServiceImpl.create(..))"
    )
    @SuppressWarnings({"unchecked"})
    public Object userInsert(ProceedingJoinPoint joinPoint) {
        Object render=null;
        try {
            Object result= joinPoint.proceed();
            logger.info("调用" + joinPoint.getTarget() + "的" + joinPoint.getSignature().getName()
                    + "方法。方法返回值:" + result);
            render = RenderUtil.render(result);
        } catch (Throwable e) {
            logger.error(joinPoint.getSignature().getName() + "方法发生异常:" + e);
        }finally {
            logger.info(joinPoint.getSignature().getName() + "方法结束执行。");
        }
        return render;
    }
}

5、utils

5.1、RenderUtil

public class RenderUtil {

        public static Object render(Object object) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
            Class<?> clazz = object.getClass();
            Field[] fields = clazz.getDeclaredFields();
            String s = null;
            Map<String, Object> addProperties = new HashMap();
            Object target=null;
            for(int i=0;i< fields.length;i++){
                if(fields[i].isAnnotationPresent(TestAnnotation.class)){
                    TestAnnotation annotation = fields[i].getAnnotation(TestAnnotation.class);
                    System.out.println("field内容是"+fields[i]);
                    List<Class<? extends ValueRenderer>> renderList=new ArrayList<>();
                    for(int c=0;c<annotation.renderers().length;c++){
                        renderList.add(annotation.renderers()[c]);
                        System.out.println(annotation.renderers()[c]);
                    }

                    //获取方法的索引  例如 public java.lang.Integer annotion2.TestAnnotationService.test
                    int index=fields[i].toString().lastIndexOf(".")+1;
                    //根据索引截取具体的方法
                    String methodString =fields[i].toString().substring(index);
                    //将方法的首字母大写
                    String methodString1 = toUpperFirstCharacter(methodString);

                    //根据反射获取方法字段值,根据get方法获取
                    Method getTest = clazz.getDeclaredMethod("get"+methodString1);
                    Object invoke = getTest.invoke(object);

                    //调用具体的方法,可能有多个注解的处理
                    for(Class<? extends ValueRenderer>  render:renderList) {
                        String render1 = (String)render.getMethod("render", Object.class, Object.class).invoke(render.newInstance(), invoke, null);
                        System.out.println(render1);
                        addProperties.put(methodString+"Meaning",render1);
                    }
                }
                target = ReflectUtil.getTarget(object, addProperties);
            }
            return target;
        }

        /**
         * 将字符串的首字母转大写
         * @param s
         * @return
         */
        private static String toUpperFirstCharacter(String s) {
            // 利用ascii编码的前移,效率要高于截取字符串进行转换的操作
            char[] cs = s.toCharArray();
            if (Character.isLowerCase(cs[0])) {
                cs[0] -= 32;
                return String.valueOf(cs);
            }
            return s;
        }
}

5.2、ReflectUtil

使用cglib动态生成类与使用 commons-beanutils获取源对象属性-类型集合,封装成新对象并设置值

public class ReflectUtil {
 
    static Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
 
    public static Object getTarget(Object dest, Map<String, Object> addProperties) {
        // get property map
        PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
        PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
        Map<String, Class> propertyMap = Maps.newHashMap();
        for (PropertyDescriptor d : descriptors) {
            if (!"class".equalsIgnoreCase(d.getName())) {
                propertyMap.put(d.getName(), d.getPropertyType());
            }
        }
        // add extra properties
        addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass()));
        // new dynamic bean
        DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap);
        // add old value
        propertyMap.forEach((k, v) -> {
            try {
                // filter extra properties
                if (!addProperties.containsKey(k)) {
                    dynamicBean.setValue(k, propertyUtilsBean.getNestedProperty(dest, k));
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        });
        // add extra value
        addProperties.forEach((k, v) -> {
            try {
                dynamicBean.setValue(k, v);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        });
        Object target = dynamicBean.getTarget();
        return target;
    }

    public static class DynamicBean {
        /**
         * 目标对象
         */
        private Object target;
 
        /**
         * 属性集合
         */
        private BeanMap beanMap;
 
        public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
            this.target = generateBean(superclass, propertyMap);
            this.beanMap = BeanMap.create(this.target);
        }
 
 
        /**
         * bean 添加属性和值
         *
         * @param property
         * @param value
         */
        public void setValue(String property, Object value) {
            beanMap.put(property, value);
        }
 
        /**
         * 获取属性值
         *
         * @param property
         * @return
         */
        public Object getValue(String property) {
            return beanMap.get(property);
        }
 
        /**
         * 获取对象
         *
         * @return
         */
        public Object getTarget() {
            return this.target;
        }
 
 
        /**
         * 根据属性生成对象
         *
         * @param superclass
         * @param propertyMap
         * @return
         */
        private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
            BeanGenerator generator = new BeanGenerator();
            if (null != superclass) {
                generator.setSuperclass(superclass);
            }
            BeanGenerator.addProperties(generator, propertyMap);
            return generator.create();
        }
    }
 
}

6、总结

  1. @Around(处理入口),render(翻译值),ReflectUtil(生成新字段)
  2. 实际开发中不需要每次都进行render实现类的配置,可以做成配置项,从redis里面取值。——约定大于配置,配置大于代码
  3. 代码是一面,思想是另一面。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值