All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.yiuman.citrus.support.utils.CrudUtils Maven / Gradle / Ivy

There is a newer version: 0.15.0
Show newest version
package com.github.yiuman.citrus.support.utils;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.github.yiuman.citrus.support.crud.mapper.CrudMapper;
import com.github.yiuman.citrus.support.crud.mapper.TreeMapper;
import com.github.yiuman.citrus.support.crud.rest.CrudRestful;
import com.github.yiuman.citrus.support.crud.rest.QueryRestful;
import com.github.yiuman.citrus.support.crud.service.CrudService;
import com.github.yiuman.citrus.support.widget.Selections;
import com.github.yiuman.citrus.support.widget.Selects;
import com.github.yiuman.citrus.support.widget.Widget;
import javassist.CtClass;
import javassist.NotFoundException;
import javassist.bytecode.SignatureAttribute;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.BindingException;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.util.ReflectionUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * CRUD工具类
 *
 * @author yiuman
 * @date 2020/5/8
 */
@Slf4j
public final class CrudUtils {

    private CrudUtils() {
    }

    /**
     * 获取CrudService实例
     *
     * @param entityClass       实体class
     * @param keyClass          主键class
     * @param superServiceClass 父类的serviceClass
     * @param                实体类型
     * @param                主键类型
     * @param                CrudService的子类类型
     * @return CrudService实例
     * @throws Exception 反射异常
     */
    public static > S getCrudService(
            Class entityClass,
            Class keyClass,
            Class superServiceClass) throws Exception {
        Class crudServiceClass = getCrudServiceClass(entityClass, keyClass, superServiceClass);
        return SpringUtils.getBean(crudServiceClass, true);
    }

    /**
     * 获取CrudService 增删改查逻辑类的Class(没有则创建)
     *
     * @param entityClass       实体class
     * @param keyClass          主键class
     * @param superServiceClass 父类的CrudService类型的Class
     * @param                实体类型
     * @param                主键类型
     * @param                CrudService的子类类型
     * @return 增删改查逻辑类的Class (继承与CrudService子类)
     * @throws Exception 反射异常
     */
    @SuppressWarnings("unchecked")
    public static > Class getCrudServiceClass(
            Class entityClass,
            Class keyClass,
            Class superServiceClass) throws Exception {
        String entityClassName = entityClass.getName();
        String formatName = String.format("%sCrudService$$javassist", entityClassName);
        Class serviceClass;
        try {
            serviceClass = JavassistUtils.defaultPool().getClassLoader().loadClass(formatName);
        } catch (ClassNotFoundException e) {
            CtClass ctClass;
            try {
                ctClass = JavassistUtils.defaultPool().getCtClass(formatName);
                serviceClass = ctClass.getClass();
            } catch (NotFoundException notFoundCtClass) {
                ctClass = JavassistUtils.defaultPool().makeClass(formatName, JavassistUtils.getClass(superServiceClass));
                addTypeArgument(ctClass, superServiceClass, new Class[]{entityClass, keyClass}, null, null);
                serviceClass = ctClass.toClass();
            }
        }
        return (Class) serviceClass;
    }

    /**
     * 添加类的泛型
     *
     * @param ctClass                  Javassist的类型
     * @param supperClass              父类
     * @param superTypeArgsClasses     父类的泛型类型
     * @param interfaceClass           接口
     * @param interfaceTypeArgsClasses 接口的泛型类型
     */
    public static void addTypeArgument(CtClass ctClass, Class supperClass,
                                       Class[] superTypeArgsClasses,
                                       Class interfaceClass,
                                       Class[] interfaceTypeArgsClasses) {

        SignatureAttribute.ClassType supperClassType = getSignatureClassType(supperClass, superTypeArgsClasses);
        //完整的接口泛型描述
        SignatureAttribute.ClassType interFaceClassType = getSignatureClassType(interfaceClass, interfaceTypeArgsClasses);
        ctClass.setGenericSignature(new SignatureAttribute
                .ClassSignature(null, supperClassType, interFaceClassType == null ? null : new SignatureAttribute.ClassType[]{interFaceClassType})
                .encode());
    }

    /**
     * 获取泛型描述类型
     *
     * @param mainClass       主类型
     * @param typeArgsClasses 泛型 按顺序
     * @return 泛型描述
     */
    public static SignatureAttribute.ClassType getSignatureClassType(Class mainClass, Class... typeArgsClasses) {
        if (mainClass == null) {
            return null;
        }
        return new SignatureAttribute.ClassType(mainClass.getName(), getSignatureTypeArguments(typeArgsClasses));
    }

    /**
     * 获取泛型描述数组
     *
     * @param classes 泛型的类型
     * @return 泛型参数数组
     */
    public static SignatureAttribute.TypeArgument[] getSignatureTypeArguments(Class... classes) {
        if (classes == null || classes.length == 0) {
            return null;
        }
        SignatureAttribute.TypeArgument[] typeArguments = new SignatureAttribute.TypeArgument[classes.length];
        for (int i = 0; i < classes.length; i++) {
            typeArguments[i] = new SignatureAttribute.TypeArgument(new SignatureAttribute.ClassType(classes[i].getName()));
        }
        return typeArguments;
    }

    /**
     * 动态构建CRUDMAPPER
     *
     * @param entityClass 实体类Class
     * @param          实体类类型
     * @return CrudMapper接口的class
     * @throws Exception 反射异常,无法创建CLASS异常
     */
    @SuppressWarnings("unchecked")
    public static  Class> getMapperInterface(Class entityClass, Class mapperClass) throws Exception {
        String entityClassName = entityClass.getName();
        String mapperClassSimpleName = mapperClass.getSimpleName();
        String formatName = String.format("%s$$javassist", (entityClassName + mapperClassSimpleName));
        Class mapperInterface;
        try {
            mapperInterface = JavassistUtils.defaultPool().getClassLoader().loadClass(formatName);
        } catch (ClassNotFoundException e) {
            CtClass ctClass;
            try {
                ctClass = JavassistUtils.defaultPool().getCtClass(formatName);
                mapperInterface = ctClass.getClass();
            } catch (NotFoundException notFoundCtClass) {
                ctClass = JavassistUtils.defaultPool().makeInterface(formatName, JavassistUtils.getClass(mapperClass));
                addTypeArgument(ctClass, null, null, mapperClass, new Class[]{entityClass});
                mapperInterface = ctClass.toClass();
            }

        }


        return (Class>) mapperInterface;
    }


    public static , T> M getCrudMapper(Class entityClass) throws Exception {
        return  getMapper(entityClass, CrudMapper.class);
    }

    public static , T> M getTreeMapper(Class entityClass) throws Exception {
        return  getMapper(entityClass, TreeMapper.class);
    }

    /**
     * 根据实体类型,Mybatis的Mapper接口类型动态获取的实体映射接口 如:UserMapper extend BaseMapper
     * 第一次从SqlSessionTemplate取到直接返回,若取不到,会动态构建一个,通过字节码工具去生成字节码放到内存中
     *
     * @param entityClass 实体类型
     * @param          生成的接口类型
     * @param          实体类型
     * @return mapper接口的动态代理
     * @throws Exception 反射异常 Spring容器找不到的异常
     */
    @SuppressWarnings("unchecked")
    public static , T> M getMapper(Class entityClass, Class baseMapperClass) throws Exception {
        Class> mapperClass = getMapperInterface(entityClass, baseMapperClass);
        //多线程的使用 线程安全的SqlSessionTemplate
        SqlSessionTemplate sqlSessionTemplate = SpringUtils.getBean(SqlSessionTemplate.class);
        M mapper;
        try {
            mapper = (M) sqlSessionTemplate.getMapper(mapperClass);
        } catch (BindingException e) {
            sqlSessionTemplate.getConfiguration().addMapper(mapperClass);
            mapper = (M) sqlSessionTemplate.getMapper(mapperClass);
        }
        return mapper;

    }

    /**
     * 获取CRUD页面使用的小部件
     *
     * @param restful Crud
     * @return 小部件集合
     */
    public static > List getCrudWidgets(QueryRestful restful) {
        final List widgets = new LinkedList<>();
        ReflectionUtils.doWithMethods(restful.getClass(), method -> {
            try {
                widgets.add(getWidget(restful, method));
            } catch (Exception ex) {
                log.info("控件实例化报错", ex);
            }

        }, method -> method.getAnnotation(Selects.class) != null);
        return widgets;
    }


    public static > W getWidget(CrudRestful restful, String methodName) throws InvocationTargetException, IllegalAccessException {
        Method method = ReflectionUtils.findMethod(restful.getClass(), methodName);
        return getWidget(restful, method);
    }

    @SuppressWarnings("unchecked")
    public static > W getWidget(Object object, Method method) throws InvocationTargetException, IllegalAccessException {
        if (method == null) {
            return null;
        }
        Selects selects = method.getAnnotation(Selects.class);
        if (selects == null) {
            return null;
        }
        String text = org.springframework.util.StringUtils.hasText(selects.text()) ? selects.text() : selects.key();

        method.setAccessible(true);
        Object invoked = method.invoke(object);
        final List selectItems = new ArrayList<>();
        if (invoked instanceof Collection) {
            Collection collects = (Collection) invoked;
            collects.parallelStream().forEach(LambdaUtils.consumerWrapper(item -> {
                String labelFieldName = org.springframework.util.StringUtils.hasText(selects.label())
                        ? selects.label()
                        : selects.key();
                Field valueField = ReflectionUtils.findField(item.getClass(), selects.key());
                if (valueField == null) {
                    return;
                }
                valueField.setAccessible(true);
                Object value = valueField.get(item);
                String label;
                if (labelFieldName.equals(selects.key())) {
                    label = item.toString();
                } else {
                    Field labelField = ReflectionUtils.findField(item.getClass(), labelFieldName);
                    ReflectionUtils.makeAccessible(labelField);
                    label = ReflectionUtils.getField(labelField, item).toString();
                }

                selectItems.add(new Selections.SelectItem(selects.key(), label, value.toString()));
            }));
        }

        return (W) new Selections(text, selects.bind(), selectItems, selects.multiple());
    }


}