字段翻译
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、总结
- @Around(处理入口),render(翻译值),ReflectUtil(生成新字段)
- 实际开发中不需要每次都进行render实现类的配置,可以做成配置项,从redis里面取值。——约定大于配置,配置大于代码
- 代码是一面,思想是另一面。