Skip to content

如何把一个类上面的注解转移到另外一个类

一个比较奇葩的需求,因为controller层的request对象(使用hibernate validate)换成proto对象,但是proto的校验功能protoc-gen-validate不完善,考虑把之前 在controller层的校验注解挪到service层的对象上面,手动操作工作量过大,故有此工具类

依赖包: com.github.javaparser:javaparser-symbol-solver-core:3.25.2

原理

  • 使用 javaparser 解析 source 对象和 target 对象源码
  • 将 source 对象和 target 对象的字段和对应的注解做映射
  • 比较字段名字,将 source 对象的注解加到 target 对象上
  • 重新生成 target 对象源码,写入文件完事
kotlin
import com.github.javaparser.JavaParser
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.body.FieldDeclaration
import java.io.File
import java.io.FileWriter
import kotlin.reflect.KClass

/**
 * 把 Java A类的注解 移到 Java B类
 * @author c
 */
fun getSourceCode(clazz: KClass<*>): CompilationUnit {
    val sourceFile = File(cls2Path(clazz.java))
    val compilationUnit = JavaParser().parse(sourceFile)

    return compilationUnit.result.orElseThrow { Exception("JavaParse Error") }
}

fun cls2Path(clazz: Class<*>): String {
    val classLoader = ClassLoader.getSystemClassLoader()
    // C:\code\v2x-device\matrix-device-client\src\main\java\com\sensetime\iag\v2x\device\sensor\vo\request\SensorNewRequest.java
    val res: String = clazz.`package`.name.replace('.', '/')
    return classLoader.getResource("$res/${clazz.simpleName}.class")
        ?.file
        ?.replace("target/classes", "src/main/java")
        ?.replace(".class", ".java")
        ?: throw Exception("parse path error")
}

fun clsAnnoMoving(clsSource: KClass<*>, clsTarget: KClass<*>): String {
    val sourceCodeCls = getSourceCode(clsSource)
    val needImport = sourceCodeCls.imports.filter {
        it.toString().let { iStr ->
            iStr.contains("validat")
                    || iStr.contains("NotBlankMessageConstant")
                    || iStr.contains("ProtocolVersionValidate")
        }
    }
    val fAnnoMap = sourceCodeCls.types?.get(0)?.let { td ->
        td.members
            .filterIsInstance<FieldDeclaration>()
            .associate {
                it.variables.first.get().name to it.annotations.filter { anno ->
                    !anno.toString().contains("ApiModelProperty")
                }
            }
    }

    val tarCodeCls = getSourceCode(clsTarget)


    tarCodeCls.types?.get(0)?.let { td ->
        td.members
            .filterIsInstance<FieldDeclaration>()
            .forEach { bd ->
                val needAddAnno = fAnnoMap?.get(bd.variables.first.get().name)
                needAddAnno?.filter { !bd.annotations.contains(it) }
                    ?.forEach { bd.addAnnotation(it) }
            }
    }

    needImport.forEach { tarCodeCls.imports.add(it) }
    return tarCodeCls.toString()
}

private fun doMovingAnno(
    clsA: KClass<*>,
    clsB: KClass<*>
): String {
    val targetCode = clsAnnoMoving(clsA, clsB)
    val targetPath = cls2Path(clsB.java)
    FileWriter(targetPath).use { fileWriter -> fileWriter.write(targetCode) }
    return targetCode
}

fun main() {
    val clsMap = mapOf(
        SensorNewRequest::class to VehicleSensor::class,
        VehicleNewRequest::class to Vehicle::class
    )

    clsMap.forEach { (clsSource, clsTarget) -> doMovingAnno(clsSource, clsTarget).let(::println) }
}

文章来源于自己总结和网络转载,内容如有任何问题,请大佬斧正!联系我