最近工作的时候发现项目里的entity类需要加JPA的@Table和@Column注解,但是都得手动加。我总结了一下,加的注解name都是把java的upper camel case改成lower underscore case, 然后我就想,这么规律的事情,是人干的吗?于是就想能不能让程序在运行的时候自动加上注解,于是经过我的一番折腾,终于实现了这个功能。用的就是大名鼎鼎的Byte Buddy.

简介

Byte Buddy 是一个代码生成和操作库,用于在 Java 应用程序运行时创建和修改 Java 类,无需编译器的帮助。除了Java 类库附带的代码生成实用程序,Byte Buddy 允许创建任意类,并且不限于实现用于创建运行时代理的接口。此外,Byte Buddy 提供了一个方便的 API,用于手动、使用 Java 代理或在构建期间更改类。这个工具目前是用过的字节码操作最舒服的工具了。

具体实现
  • 先添加依赖
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.10.20</version>
        </dependency>

        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.11.0</version>
        </dependency>
  • 在jvm上安装代理
ByteBuddy byteBuddy=new ByteBuddy();
ByteBuddyAgent.install();
  • 重新定义类
其中class就是需要重新定义的类
DynamicType.Builder builder=byteBuddy.redefine(clazz);

定义注解
AnnotationDescription.Builder tableAnnotationBuilder= AnnotationDescription.Builder.ofType(Table.class);

添加注解
builder = builder.annotateType(tableAnnotationBuilder.define("name",tableName).build());

生成新的类,然后加载
builder.make().load(Thread.currentThread().getContextClassLoader(), ClassReloadingStrategy.fromInstalledAgent());

注意: builder连续添加注解时,必须要写成builder = builder.annotateType(), 因为builder每调用一次方法,builder的值都会发生改变,所以每次都要复制给原来的builder.

超级简单有木有,只能说这种API用着太舒服

以下是完整代码,完全可以复制直接用
@Component
//public class JpaAnnotationProcessor implements ApplicationRunner {
public class JpaAnnotationProcessor{

    //@Autowired
    //THtCMapper tHtCMapper;

    private static final Logger logger= LoggerFactory.getLogger(JpaAnnotationProcessor.class);

    private static Converter<String,String> lc2LuConverter=
            CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.LOWER_UNDERSCORE);
    private static Converter<String,String> uc2LuConverter=
            CaseFormat.UPPER_CAMEL.converterTo(CaseFormat.LOWER_UNDERSCORE);

    private static void addAnnotation(List<Class> classes, ByteBuddy byteBuddy){
        for(Class clazz:classes){
            boolean redefine=false;
            DynamicType.Builder builder=byteBuddy.redefine(clazz);
            if(!containsAnnotation(clazz, Table.class)){
                AnnotationDescription.Builder tableAnnotationBuilder= AnnotationDescription.Builder.ofType(Table.class);
                String tableName=uc2LuConverter.convert(clazz.getSimpleName());
                builder = builder.annotateType(tableAnnotationBuilder.define("name",tableName).build());
                logger.info("给类:{}添加table注解",clazz.getName());
                redefine=true;
            }
            Field[] fields = clazz.getDeclaredFields();
            for(Field field:fields){
                Class annotationType;
                if(field.getName().equals("id")){
                    annotationType= Id.class;
                } else {
                    annotationType= Column.class;
                }
                if(!containsAnnotation(field,annotationType)){
                    AnnotationDescription.Builder columnAnnotationBuilder= AnnotationDescription.Builder.ofType(annotationType);
                    if(!field.getName().equals("id")){
                        columnAnnotationBuilder= columnAnnotationBuilder.define("name", lc2LuConverter.convert(field.getName()));
                    }
                    builder = builder.field((ElementMatcher<FieldDescription>) target -> target.getName().equals(field.getName()))
                            .annotateField(columnAnnotationBuilder.build());
                    redefine=true;
                    logger.info("给类{}的{}添加注解{}",clazz.getName(),field.getName(),annotationType.getName());
                }
            }
            if(redefine){
                builder.make().load(Thread.currentThread().getContextClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
            }
        }

    }

    private static boolean containsAnnotation(Field field,Class annotationType){
        return containsAnnotation(field.getAnnotations(),annotationType);
    }
    private static boolean containsAnnotation(Class clazz,Class annotationType){
        return containsAnnotation(clazz.getAnnotations(),annotationType);
    }
    private static boolean containsAnnotation(Annotation[] srcAnnotations, Class annotationType){
        boolean result=false;
        for(Annotation srcAnnotation:srcAnnotations){
            if(annotationType.equals(srcAnnotation.annotationType())){
                result=true;
                break;
            }
        }
        return result;
    }

    private static void printAnnotations(Class clazz){
        List<Field> fields= Arrays.asList(clazz.getDeclaredFields());
        fields.forEach(field -> {
            System.out.println(field.getName()+" annotations:");
            Annotation[] fieldAnnotations = field.getAnnotations();
            for (Annotation annotation : fieldAnnotations) {
                System.out.println(annotation);
            }
        });
        System.out.println("class annotations:");
        System.out.println(Arrays.asList(clazz.getAnnotations()));
    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐