MST

星途 面试题库

面试题:Java代码生成技术与注解处理器的深度融合

在一个大型企业级项目中,需要利用Java注解处理器与代码生成技术来实现数据库访问层(DAO)的自动生成。要求根据数据库表结构,通过注解处理器生成对应的Java实体类、DAO接口及其实现类。请阐述整体的设计架构、技术选型,并给出核心代码示例及说明。
43.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

整体设计架构

  1. 数据库表结构读取:通过JDBC获取数据库元数据,解析表结构信息,包括表名、列名、数据类型等。
  2. 注解定义:定义自定义注解,用于标记需要生成DAO的实体类和DAO接口等。
  3. 注解处理器:编写注解处理器,根据注解标记和数据库表结构信息,生成Java实体类、DAO接口及其实现类。
  4. 代码生成:使用代码生成技术,如JavaPoet或Velocity等,将生成的代码写入文件。

技术选型

  1. JDBC:用于获取数据库元数据。
  2. Java注解:自定义注解标记相关类。
  3. Java注解处理器:基于JDK的注解处理工具,在编译期处理注解。
  4. JavaPoet:用于生成Java代码,简洁高效。

核心代码示例及说明

  1. 定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface GenerateDAO {
}

说明:GenerateDAO注解用于标记需要生成DAO的实体类。

  1. 注解处理器
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;

@SupportedAnnotationTypes("GenerateDAO")
public class DAOProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(GenerateDAO.class)) {
            String className = element.getSimpleName().toString();
            List<FieldSpec> fields = getTableColumns(className);
            generateEntityClass(className, fields);
            generateDAOInterface(className);
            generateDAOImplementation(className);
        }
        return true;
    }

    private List<FieldSpec> getTableColumns(String tableName) {
        List<FieldSpec> fields = new ArrayList<>();
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/yourdb", "user", "password");
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("DESCRIBE " + tableName)) {
            while (rs.next()) {
                String columnName = rs.getString("Field");
                String type = rs.getString("Type");
                FieldSpec field = FieldSpec.builder(ClassName.get("java.lang", getJavaType(type)), columnName, Modifier.PRIVATE).build();
                fields.add(field);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fields;
    }

    private void generateEntityClass(String className, List<FieldSpec> fields) {
        TypeSpec entityClass = TypeSpec.classBuilder(className)
              .addModifiers(Modifier.PUBLIC)
              .addFields(fields)
              .build();

        JavaFile javaFile = JavaFile.builder("com.example.entity", entityClass)
              .build();

        try {
            javaFile.writeTo(System.out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void generateDAOInterface(String className) {
        MethodSpec findByIdMethod = MethodSpec.methodBuilder("findById")
              .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
              .returns(ClassName.get("com.example.entity", className))
              .addParameter(ClassName.get("java.lang", "Integer"), "id")
              .build();

        TypeSpec daoInterface = TypeSpec.interfaceBuilder(className + "DAO")
              .addModifiers(Modifier.PUBLIC)
              .addMethod(findByIdMethod)
              .build();

        JavaFile javaFile = JavaFile.builder("com.example.dao", daoInterface)
              .build();

        try {
            javaFile.writeTo(System.out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void generateDAOImplementation(String className) {
        MethodSpec findByIdMethod = MethodSpec.methodBuilder("findById")
              .addModifiers(Modifier.PUBLIC)
              .returns(ClassName.get("com.example.entity", className))
              .addParameter(ClassName.get("java.lang", "Integer"), "id")
              .addStatement("$T result = null", ClassName.get("com.example.entity", className))
              .addStatement("return result")
              .build();

        TypeSpec daoImplementation = TypeSpec.classBuilder(className + "DAOImpl")
              .addModifiers(Modifier.PUBLIC)
              .addSuperinterface(ClassName.get("com.example.dao", className + "DAO"))
              .addMethod(findByIdMethod)
              .build();

        JavaFile javaFile = JavaFile.builder("com.example.dao.impl", daoImplementation)
              .build();

        try {
            javaFile.writeTo(System.out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String getJavaType(String sqlType) {
        // 简单类型映射
        if (sqlType.startsWith("varchar") || sqlType.startsWith("char")) {
            return "String";
        } else if (sqlType.startsWith("int")) {
            return "Integer";
        } else if (sqlType.startsWith("double")) {
            return "Double";
        }
        return "Object";
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

说明:

  • GenerateDAO注解处理器在编译期处理标记了GenerateDAO注解的类。
  • getTableColumns方法通过JDBC获取表的列信息,并映射为Java字段。
  • generateEntityClass方法生成Java实体类。
  • generateDAOInterface方法生成DAO接口。
  • generateDAOImplementation方法生成DAO接口的实现类。

以上代码仅是示例,实际应用中需根据具体需求完善,如数据库连接配置、异常处理、更多数据库类型映射等。