用Spring AOP、Java注解、反射对返回的结果集进行特殊处理_zhuyu19911016520-CSDN博客

沙海
沙海
沙海
994
文章
2
评论
2021年4月27日11:46:41
评论
2 12850字阅读42分50秒
摘要

用Spring AOP、Java注解、反射对返回的结果集进行特殊处理

用Spring AOP、Java注解、反射对返回的结果集进行特殊处理

用Spring AOP、Java注解、反射对返回的结果集进行特殊处理_zhuyu19911016520-CSDN博客

闪耀的瞬间
2020-09-05 12:13:12
用Spring AOP、Java注解、反射对返回的结果集进行特殊处理_zhuyu19911016520-CSDN博客
439

用Spring AOP、Java注解、反射对返回的结果集进行特殊处理_zhuyu19911016520-CSDN博客

收藏

1

分类专栏:
SpringBoot 项目开发
文章标签:
aop
注解
反射

版权

用Spring AOP、Java注解、反射对返回的结果集进行特殊处理_zhuyu19911016520-CSDN博客

1.目标:业务数据中的某些字段列,需要根据用户选择的语言类型自动处理后返回对应语言的数据

2.背景:系统需要适应多种语言:中文简体、中文繁体、英语等,一个业务数据中的某些列,会同时设置多种语言的数据,用户可根据自己喜欢的语言展示对应的数据

3.用户选择语言后,前端把该参数通过header头传递到后端,后端查询业务数据后,根据header头中的多语言参数,查询业务数据中某些列对应语言的数据,再覆盖到当前的数据中,然后返回给客户端

4.难点:1.结果集的数据有数组嵌套,2.尽量少改动业务代码,且提供好的性能,来满足业务要求,因此通过aop + 注解 的方式统一处理这种类型的业务

5.实现逻辑:返回的结果集对象中有List集合,在aop切面中把结果集获取出来,并循环结果集中的每一列,判断是否为多语言字段,或者是否为List对象,把所有需要多语言处理的字段全部放到一个List集合中,通过mysql in 查询一次把数据查询出来,并组装数据为map 键值对结构,再次对结果集进行循环,再次使用反射,对需要多语言处理的字段通过key在map 集合中查询,有数据则覆盖现有数据

6.实现方法:1.在返回实体字段中添加注解,标识某些字段是多语言字段,2.在业务方法上使用注解,标识该方法需要进行多语言处理,3.添加aop切面,对服务层使用了多语言处理注解的方法进行横切,来统一多语言处理

6.代码实现

  • 1.重要依赖
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.5</version>
  </dependency>
123456789
  • 2.添加注解:标识某些字段是多语言字段,注解中的字段与我们系统的业务逻辑有关
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiLanguageAnnotation {

    /**
     * 表名
     */
    String tabName() default "";

    /**
     * 列名
     */
    String colName() default  "";

    /**
     * 主键名
     * @return
     */
    String pkName() default "";
}
1234567891011121314151617181920
  • 3.添加注解:标识该方法需要进行多语言处理
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiLanguageResponse {

}
12345
  • 4.反射处理帮助类,这里写法不太优雅,功能倒是实现了
package com.es.util;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.egovchina.apicenter.es.config.MultiLanguageAnnotation;
import com.egovchina.apicenter.es.searchlogic.po.TabMultiLanguagePo;
import com.egovchina.apicenter.es.searchlogic.service.MultiLanguageService;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 多语言帮助类
 */
public class MultiLanguageUtil {

    /**
     * 设置多语言内容 <br/>
     * 两种使用示例: <br/>
     *  1.直接在方法上使用 @MultiLanguageResponse 注解,来处理多语言 <br/>
     *  2.使用代码方式: <br/>
          List<AffairsServiceAndAffairsDocument> list = esDatas.getDatas(); <br/>
          String languageCode = RequestHolder.getDataLanguageHeaderValue(); <br/>
          MultiLanguageUtil.setMultiLanguageInfo(multiLanguageService , list , languageCode); <br/>
     *
     * @param multiLanguageService es多语言查询服务
     * @param documents    要设置多语言字段的文档集合
     * @param languageCode 语言类型
     * @param <T>
     */
    public static <T> void setMultiLanguageInfo(MultiLanguageService multiLanguageService , List<T> documents , String languageCode){
        if(documents != null && documents.size() > 0){
            List<String> multiInfoIds = getMultiInfoId(documents , languageCode);
            //System.out.println("util multiInfoIds:" + JSON.toJSONString(multiInfoIds));
            if(multiInfoIds != null && multiInfoIds.size() > 0){
                //过滤重复multiInfoId
                multiInfoIds = multiInfoIds.stream().distinct().collect(Collectors.toList());
                //根据multiInfoId查询多语言内容
                List<TabMultiLanguagePo>  multiLanguageDocuments = multiLanguageService.getByMultiInfoId(multiInfoIds);
                if(multiLanguageDocuments != null && multiLanguageDocuments.size() > 0) {
                    //把从es查询出来的多语言键值对,序列化成map
                    Map<String , String> map = new HashMap<>(multiLanguageDocuments.size());
                    multiLanguageDocuments.forEach(item -> map.put(item.getMultiInfoId() , item.getContentText()));
                    //System.out.println("util map:" + JSON.toJSONString(map));

                    //设置多语言内容
                    setMultiValue(documents , languageCode , map);
                }
            }
        }
    }


    /**
     * 获取文档中有多语言注解的 multiInfoId
     * @param documents
     * @param languageCode
     * @param <T>
     * @return
     */
    private static <T> List<String> getMultiInfoId(List<T> documents , String languageCode){
        List<String> multiInfoIdList = new ArrayList<>(20);

        documents.forEach(item -> {
            try {
                Class clazz = item.getClass();             // 获取集合中的对象
                Field[] fields = clazz.getDeclaredFields();// 获取他的字段数组
                for (Field field : fields) {
                    field.setAccessible(true);         // 设置字段可访问
                    //获取带有 MultiLanguageAnnotation 注解的字段,并组装 multiInfoId
                    MultiLanguageAnnotation languageAnnotation = field.getDeclaredAnnotation(MultiLanguageAnnotation.class);
                    if (languageAnnotation != null) {
                        String multiInfoId = languageAnnotation.tabName() + languageAnnotation.colName() +
                                ReflectionUtils.getFieldValue(item, languageAnnotation.pkName()) + languageCode;
                        //System.out.println("multiInfoId:" + multiInfoId);
                        multiInfoIdList.add(multiInfoId);
                    }

                    //获取List泛型中带有 MultiLanguageAnnotation 注解的字段,并组装 multiInfoId
                    if (List.class.isAssignableFrom(field.getType())) {
                        Type genericType = field.getGenericType(); // 当前集合的泛型类型
                        if (null == genericType) {
                            continue;
                        }
                        if (genericType instanceof ParameterizedType) {
                            //ParameterizedType pt = (ParameterizedType) genericType;
                            //Class clz = (Class) pt.getActualTypeArguments()[0];//得到对象list中实例的类型
                            Object obj = field.get(item);
                            if(null == obj){
                                continue;
                            }
                            Class childClazz = obj.getClass();       //获取到属性的值的Class对象
                            if(null == childClazz){
                                continue;
                            }
                            Method m =  childClazz.getDeclaredMethod("size");
                            int size = (Integer) m.invoke(field.get(item));     //调用list的size方法,得到list的长度
                            for (int i = 0; i < size; i++) {//遍历list,调用get方法,获取list中的对象实例
                                Method getM = childClazz.getDeclaredMethod("get", int.class);
                                //getM.setAccessible(true);
                                Object childObj = getM.invoke(field.get(item), i);  //获取泛型的子类对象
                                Field[] childFields = childObj.getClass().getDeclaredFields(); //得到子类对象的字段数组
                                //System.out.println("list obj field size : " + objField.length);
                                for (Field childField : childFields){
                                    childField.setAccessible(true);
                                    MultiLanguageAnnotation childLanguageAnnotation = childField.getDeclaredAnnotation(MultiLanguageAnnotation.class);
                                    if (childLanguageAnnotation != null) {
                                        String childMultiInfoId = childLanguageAnnotation.tabName() + childLanguageAnnotation.colName() +
                                                ReflectionUtils.getFieldValue(childObj, childLanguageAnnotation.pkName()) + languageCode;
                                        //System.out.println("childMultiInfoId:" + childMultiInfoId);
                                        multiInfoIdList.add(childMultiInfoId);
                                    }
                                }
                            }
                        }
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return multiInfoIdList;
    }

    /**
     * 根据多语言 multiInfoId ,设置多语言内容
     * @param documents
     * @param map
     * @param <T>
     */
    private static <T> void setMultiValue(List<T> documents  , String languageCode , Map<String , String> map){

        documents.forEach(item -> {
            try {
                Class clazz = item.getClass();             // 获取集合中的对象
                Field[] fields = clazz.getDeclaredFields();// 获取他的字段数组
                for (Field field : fields) {
                    field.setAccessible(true);              // 设置字段可访问
                    //获取带有 MultiLanguageAnnotation 注解的字段,并组装 multiInfoId
                    MultiLanguageAnnotation languageAnnotation = field.getDeclaredAnnotation(MultiLanguageAnnotation.class);
                    if (languageAnnotation != null) {
                        String multiInfoId = languageAnnotation.tabName() + languageAnnotation.colName() +
                                ReflectionUtils.getFieldValue(item, languageAnnotation.pkName()) + languageCode;
                        String contentText = map.get(multiInfoId);
                        if(!StringUtils.isEmpty(contentText)){
                            field.set(item , contentText);
                            contentText = null;
                        }
                    }

                    //获取List泛型中带有 MultiLanguageAnnotation 注解的字段,并组装 multiInfoId
                    if (List.class.isAssignableFrom(field.getType())) {
                        Type genericType = field.getGenericType(); // 当前集合的泛型类型
                        if (null == genericType) {
                            continue;
                        }
                        if (genericType instanceof ParameterizedType) {
                            //ParameterizedType pt = (ParameterizedType) genericType;
                            //Class clz = (Class) pt.getActualTypeArguments()[0];//得到对象list中实例的类型
                            Object obj = field.get(item);
                            if(null == obj){
                                continue;
                            }
                            Class childClazz = obj.getClass();                  //获取到属性的值的Class对象
                            if(null == childClazz){
                                continue;
                            }
                            Method m =  childClazz.getDeclaredMethod("size");
                            int size = (Integer) m.invoke(field.get(item));     //调用list的size方法,得到list的长度
                            for (int i = 0; i < size; i++) {//遍历list,调用get方法,获取list中的对象实例
                                Method getM = childClazz.getDeclaredMethod("get", int.class);
                                Object childObj = getM.invoke(field.get(item), i);  //获取泛型的子类对象
                                Field[] childFields = childObj.getClass().getDeclaredFields(); //得到子类对象的字段数组
                                //System.out.println("list obj field size : " + objField.length);
                                for (Field childField : childFields){
                                    childField.setAccessible(true);
                                    MultiLanguageAnnotation childLanguageAnnotation = childField.getDeclaredAnnotation(MultiLanguageAnnotation.class);
                                    if (childLanguageAnnotation != null) {
                                        String childMultiInfoId = childLanguageAnnotation.tabName() + childLanguageAnnotation.colName() +
                                                ReflectionUtils.getFieldValue(childObj, childLanguageAnnotation.pkName()) + languageCode;
                                        String childContentText = map.get(childMultiInfoId);
                                        if(!StringUtils.isEmpty(childContentText)){
                                            childField.set(childObj , childContentText);
                                            childContentText = null;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  • 5.aop切面处理类
@Component
@Slf4j
@Aspect
public class MultiLanguageAspect {

    @Autowired
    private MultiLanguageService multiLanguageService;

    /**
     * 监听有多语言响应注解的方法,对结果集中的多语言字段进行处理
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "@annotation(com.apicenter.es.config.MultiLanguageResponse)")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = proceedingJoinPoint.proceed();
        //取到header头信息中的多语言参数字段
        String languageCode = RequestHolder.getDataLanguageHeaderValue();
        if(!StringUtils.isEmpty(languageCode)){
            //只对返回值类型为:ResultTemplate、List 两种类型的结果集进行多语言处理
            if(result instanceof ResultTemplate){
                ResultTemplate resultTemplate = (ResultTemplate)result;
                MultiLanguageUtil.setMultiLanguageInfo(multiLanguageService , resultTemplate.getData() , languageCode);
            } else if(result instanceof List){
                List list = (List)result;
                MultiLanguageUtil.setMultiLanguageInfo(multiLanguageService , list , languageCode);
            }
        }
        return result;
    }
}
1234567891011121314151617181920212223242526272829303132
  • 6.其他帮助类
public class RequestHolder {

    private static HttpServletRequest getHttpServletRequest() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * 获取多语言header参数
     * @return
     */
    public static String getDataLanguageHeaderValue() {
        HttpServletRequest httpServletRequest = getHttpServletRequest();
        if (httpServletRequest != null) {
            String header = httpServletRequest.getHeader("Data-Language");
            if(!StringUtils.isEmpty(header)){
                return header;
            }
        }
        return ""; //默认返回空
    }
}




public class ReflectionUtils {

    private static Logger log = LoggerFactory.getLogger(ReflectionUtils.class);

    /**
     * 直接读取对象的属性值, 忽略 private/protected 修饰符, 也不经过 getter
     *
     * @param object
     * @param fieldName
     * @return
     */
    public static Object getFieldValue(Object object, String fieldName) {
        Field field = getDeclaredField(object, fieldName);
        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
        }
        makeAccessible(field);
        Object result = null;
        try {
            result = field.get(object);
        } catch (IllegalAccessException e) {
            log.error("setFieldValue:", e);
        }
        return result;
    }

    /**
     * 直接设置对象属性值, 忽略 private/protected 修饰符, 也不经过 setter
     *
     * @param object
     * @param fieldName
     * @param value
     */
    public static void setFieldValue(Object object, String fieldName, Object value) {
        Field field = getDeclaredField(object, fieldName);

        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
        }

        makeAccessible(field);

        try {
            field.set(object, value);
        } catch (IllegalAccessException e) {
            log.error("setFieldValue:", e);
        }
    }


    /**
     * 通过反射, 获得定义 Class 时声明的父类的泛型参数的类型
     * 如: public EmployeeDao extends BaseDao<Employee, String>
     *
     * @param clazz
     * @param index
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Class getSuperClassGenricType(Class clazz, int index) {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            return Object.class;
        }
        return (Class) params[index];
    }
    /**
     * 循环向上转型, 获取对象的 DeclaredMethod
     *
     * @param object
     * @param methodName
     * @param parameterTypes
     * @return
     */
    public static Method getDeclaredMethod(Object object, String methodName, Class<?>[] parameterTypes) {
        for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredMethod(methodName, parameterTypes);
            } catch (NoSuchMethodException e) {
                //Method 不在当前类定义, 继续向上转型
            }
        }
        return null;
    }

    /**
     * 使 filed 变为可访问
     *
     * @param field
     */
    public static void makeAccessible(Field field) {
        if (!Modifier.isPublic(field.getModifiers())) {
            field.setAccessible(true);
        }
    }

    /**
     * 循环向上转型, 获取对象的 DeclaredField
     *
     * @param object
     * @param filedName
     * @return
     */
    public static Field getDeclaredField(Object object, String filedName) {
        for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredField(filedName);
            } catch (NoSuchFieldException e) {
                //Field 不在当前类定义, 继续向上转型
            }
        }
        return null;
    }
}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  • 7.在需要进行多语言处理的服务层方法上使用 @MultiLanguageResponse 注解即可实现该功能,且不改动原有业务代码
    @MultiLanguageResponse
    @Override
    public ResultTemplate<AffairsDocument> affairs(AffairsDto affairsDto) {
        //xxx 业务处理
        return ResultTemplateUtil.ofESDatasAndPage(data);
    }
123456
  • 8.ok,结合aop、annotation、反射在不改动业务情况下实现业务功能

继续阅读
weinxin
资源分享QQ群
本站是一个IT技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: