蚂蚁Java面试被问:注解的工作原理及如何自定义注解

一、注解基础概念

1. 注解的本质

java

复制

下载

复制代码
// 注解的本质是一个接口,继承自java.lang.annotation.Annotation
// 编译后生成.class文件,运行时可通过反射读取

// 示例:查看注解的继承关系
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";
}

// 编译后查看:
// javap -c MyAnnotation.class
// 可以看到:interface MyAnnotation extends java.lang.annotation.Annotation

2. 注解的分类

java

复制

下载

复制代码
// 1. 标记注解(无成员)
@interface MarkerAnnotation {}

// 2. 单值注解
@interface SingleValueAnnotation {
    String value();
}

// 3. 完整注解
@interface FullAnnotation {
    String name();
    int version() default 1;
    String[] tags() default {};
}

二、元注解详解

1. @Target - 指定注解使用位置

java

复制

下载

复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

// 可以指定多个目标位置
@Target({ElementType.TYPE,           // 类、接口、枚举
         ElementType.FIELD,          // 字段
         ElementType.METHOD,         // 方法
         ElementType.PARAMETER,      // 参数
         ElementType.CONSTRUCTOR,    // 构造函数
         ElementType.LOCAL_VARIABLE, // 局部变量
         ElementType.ANNOTATION_TYPE,// 注解类型
         ElementType.PACKAGE,        // 包
         ElementType.TYPE_PARAMETER, // 类型参数(Java 8+)
         ElementType.TYPE_USE})      // 类型使用(Java 8+)
public @interface MultiTargetAnnotation {}

// 实际应用示例
@Target(ElementType.METHOD)
public @interface LogExecutionTime {}

2. @Retention - 指定注解保留策略

java

复制

下载

复制代码
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 三种保留策略:
// 1. SOURCE: 仅存在于源码,编译时丢弃
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {}  // 如@Override、@SuppressWarnings

// 2. CLASS: 存在于.class文件,但JVM运行时不可见(默认)
@Retention(RetentionPolicy.CLASS)
public @interface ClassAnnotation {}  // 字节码工具使用

// 3. RUNTIME: 运行时可见,可通过反射读取
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnnotation {}  // Spring、MyBatis等框架使用

// 实际对比
public class RetentionDemo {
    @SourceAnnotation  // 编译后消失
    @ClassAnnotation   // .class中存在,运行时无法获取
    @RuntimeAnnotation // 运行时可通过反射获取
    public void test() {}
}

3. @Documented - 包含在Javadoc中

java

复制

下载

复制代码
import java.lang.annotation.Documented;

// 使用@Documented的注解会出现在Javadoc中
@Documented
public @interface ApiDocumentation {
    String author();
    String date();
    String description() default "";
}

/**
 * 示例类
 * @author John
 */
@ApiDocumentation(author = "John", date = "2024-01-01", 
                  description = "用户服务类")
public class UserService {
    // 这个注解会出现在生成的Javadoc中
}

4. @Inherited - 允许子类继承

java

复制

下载

复制代码
import java.lang.annotation.Inherited;

// 父类的注解会被子类继承
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritableAnnotation {
    String value();
}

// 父类
@InheritableAnnotation("parent")
class ParentClass {}

// 子类自动继承@InheritableAnnotation
class ChildClass extends ParentClass {
    // 通过反射可以获取到注解
}

// 测试继承性
public class InheritedTest {
    public static void main(String[] args) {
        // 检查子类是否继承了注解
        InheritableAnnotation ann = ChildClass.class
            .getAnnotation(InheritableAnnotation.class);
        System.out.println(ann.value()); // 输出: parent
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

5. @Repeatable - 可重复注解(Java 8+)

java

复制

下载

复制代码
import java.lang.annotation.Repeatable;

// 1. 定义容器注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {
    Role[] value();
}

// 2. 定义可重复注解
@Repeatable(Roles.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {
    String name();
    int level() default 1;
}

// 3. 使用可重复注解
@Role(name = "admin")
@Role(name = "user", level = 2)
@Role(name = "guest")
public class User {
    // 等价于:@Roles({@Role(name="admin"), @Role(name="user"), @Role(name="guest")})
}

// 4. 读取重复注解
public class RepeatableDemo {
    public static void main(String[] args) {
        // 方法1:通过容器注解
        Roles roles = User.class.getAnnotation(Roles.class);
        if (roles != null) {
            for (Role role : roles.value()) {
                System.out.println(role.name() + ": " + role.level());
            }
        }
        
        // 方法2:直接获取重复注解(Java 8+)
        Role[] roleArray = User.class.getAnnotationsByType(Role.class);
    }
}

三、注解的工作原理(字节码层面)

1. 编译时处理

java

复制

下载

复制代码
// 注解在编译时的处理流程

// 1. 定义注解处理器
@SupportedAnnotationTypes("com.example.*") // 处理的注解类型
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, 
                          RoundEnvironment roundEnv) {
        // 处理被注解的元素
        for (Element element : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
            // 生成代码、检查约束等
            generateCode(element);
        }
        return true; // 注解已处理,不会传递给其他处理器
    }
    
    private void generateCode(Element element) {
        // 使用JavaPoet等库生成代码
        // 例如:Lombok、MapStruct就是这样工作的
    }
}

// 2. 注册处理器(META-INF/services/javax.annotation.processing.Processor)
// 内容:com.example.MyAnnotationProcessor

// 3. 编译时使用
// javac -processor com.example.MyAnnotationProcessor MyClass.java

2. 运行时处理(反射)

java

复制

下载

复制代码
import java.lang.reflect.*;

// 运行时注解处理的完整流程
public class AnnotationRuntimeProcessor {
    
    // 1. 类级别注解
    public void processClassAnnotations(Class<?> clazz) {
        // 获取类上的所有注解
        Annotation[] annotations = clazz.getAnnotations();
        
        // 获取特定注解
        MyAnnotation myAnn = clazz.getAnnotation(MyAnnotation.class);
        if (myAnn != null) {
            System.out.println("Class annotation value: " + myAnn.value());
        }
        
        // 获取继承的注解(需要@Inherited)
        Annotation[] inherited = clazz.getAnnotationsByType(MyAnnotation.class);
    }
    
    // 2. 方法级别注解
    public void processMethodAnnotations(Class<?> clazz) throws Exception {
        Method[] methods = clazz.getDeclaredMethods();
        
        for (Method method : methods) {
            // 获取方法上的注解
            Annotation[] methodAnnotations = method.getAnnotations();
            
            // 获取参数注解
            Annotation[][] paramAnnotations = method.getParameterAnnotations();
            
            // 获取返回类型注解
            AnnotatedType returnType = method.getAnnotatedReturnType();
            
            // 获取异常注解
            AnnotatedType[] exceptionTypes = method.getAnnotatedExceptionTypes();
        }
    }
    
    // 3. 字段级别注解
    public void processFieldAnnotations(Class<?> clazz) throws Exception {
        Field[] fields = clazz.getDeclaredFields();
        
        for (Field field : fields) {
            // 获取字段注解
            Annotation[] fieldAnnotations = field.getAnnotations();
            
            // 获取类型注解(泛型等)
            AnnotatedType fieldType = field.getAnnotatedType();
            
            // 示例:处理自定义注解
            FieldValidation validation = field.getAnnotation(FieldValidation.class);
            if (validation != null) {
                validateField(field, validation);
            }
        }
    }
    
    // 4. 参数级别注解
    public void processParameterAnnotations(Method method) {
        Parameter[] parameters = method.getParameters();
        
        for (int i = 0; i < parameters.length; i++) {
            Parameter param = parameters[i];
            Annotation[] paramAnnotations = param.getAnnotations();
            
            // Java 8+:获取参数名(需要-parameters编译参数)
            String paramName = param.getName();
            
            // 处理参数注解
            for (Annotation ann : paramAnnotations) {
                if (ann instanceof NotNull) {
                    // 验证参数不为空
                } else if (ann instanceof Range) {
                    Range range = (Range) ann;
                    // 验证参数范围
                }
            }
        }
    }
}

3. 字节码操作(ASM/Javassist)

java

复制

下载

复制代码
// 使用ASM操作字节码处理注解
import org.objectweb.asm.*;

public class AnnotationBytecodeProcessor {
    
    public byte[] processAnnotations(byte[] classBytes) {
        ClassReader cr = new ClassReader(classBytes);
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
        
        // 访问类级别的注解
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
            @Override
            public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
                // descriptor示例: "Lcom/example/MyAnnotation;"
                
                if (descriptor.equals("Lcom/example/MyAnnotation;")) {
                    // 处理特定注解
                    return new AnnotationVisitor(Opcodes.ASM7, 
                        super.visitAnnotation(descriptor, visible)) {
                        
                        @Override
                        public void visit(String name, Object value) {
                            // 处理注解属性
                            super.visit(name, value);
                        }
                    };
                }
                return super.visitAnnotation(descriptor, visible);
            }
        };
        
        cr.accept(cv, 0);
        return cw.toByteArray();
    }
}

// 使用Javassist(更简单)
import javassist.*;

public class JavassistAnnotationProcessor {
    public void processAnnotations() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get("com.example.MyClass");
        
        // 获取类注解
        Object[] annotations = ctClass.getAnnotations();
        
        // 获取方法注解
        CtMethod method = ctClass.getDeclaredMethod("myMethod");
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        
        // 动态添加注解(运行时)
        ClassFile cf = ctClass.getClassFile();
        AnnotationsAttribute attr = new AnnotationsAttribute(
            cf.getConstPool(), AnnotationsAttribute.visibleTag);
        
        javassist.bytecode.annotation.Annotation ann = 
            new javassist.bytecode.annotation.Annotation(
                "com.example.NewAnnotation", cf.getConstPool());
        ann.addMemberValue("value", 
            new StringMemberValue("test", cf.getConstPool()));
        
        attr.addAnnotation(ann);
        cf.addAttribute(attr);
    }
}

四、自定义注解实战

1. 场景1:自定义验证注解

java

复制

下载

复制代码
// 1. 定义验证注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Validation {
    // 验证规则枚举
    enum Rule {
        NOT_NULL,
        NOT_EMPTY,
        EMAIL,
        PHONE,
        MIN,
        MAX,
        RANGE,
        REGEX
    }
    
    Rule rule();
    String message() default "验证失败";
    int min() default 0;
    int max() default Integer.MAX_VALUE;
    String pattern() default "";
}

// 具体验证注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EmailValidator.class) // 关联验证器
public @interface Email {
    String message() default "邮箱格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = RangeValidator.class)
public @interface Range {
    int min() default 0;
    int max() default 100;
    String message() default "数值必须在{min}和{max}之间";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// 2. 实现验证器
public class EmailValidator implements ConstraintValidator<Email, String> {
    private static final Pattern EMAIL_PATTERN = 
        Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$");
    
    @Override
    public void initialize(Email constraintAnnotation) {
        // 初始化工作
    }
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) return true; // @NotNull单独处理
        return EMAIL_PATTERN.matcher(value).matches();
    }
}

// 3. 验证处理器
public class ValidationProcessor {
    public static <T> List<String> validate(T obj) {
        List<String> errors = new ArrayList<>();
        Class<?> clazz = obj.getClass();
        
        // 验证字段
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            
            try {
                Object value = field.get(obj);
                
                // 检查@NotNull
                NotNull notNull = field.getAnnotation(NotNull.class);
                if (notNull != null && value == null) {
                    errors.add(field.getName() + ": " + notNull.message());
                }
                
                // 检查@Email
                Email email = field.getAnnotation(Email.class);
                if (email != null && value != null) {
                    EmailValidator validator = new EmailValidator();
                    validator.initialize(email);
                    if (!validator.isValid((String)value, null)) {
                        errors.add(field.getName() + ": " + email.message());
                    }
                }
                
                // 检查@Range
                Range range = field.getAnnotation(Range.class);
                if (range != null && value != null) {
                    int intValue = (int) value;
                    if (intValue < range.min() || intValue > range.max()) {
                        errors.add(field.getName() + ": " + range.message()
                            .replace("{min}", String.valueOf(range.min()))
                            .replace("{max}", String.valueOf(range.max())));
                    }
                }
                
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return errors;
    }
}

// 4. 使用示例
public class User {
    @NotNull(message = "用户名不能为空")
    private String username;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Range(min = 18, max = 60, message = "年龄必须在{min}到{max}岁之间")
    private int age;
    
    // getters and setters
}

// 5. 测试验证
public class ValidationTest {
    public static void main(String[] args) {
        User user = new User();
        user.setUsername(null);
        user.setEmail("invalid-email");
        user.setAge(16);
        
        List<String> errors = ValidationProcessor.validate(user);
        errors.forEach(System.out::println);
        // 输出:
        // username: 用户名不能为空
        // email: 邮箱格式不正确
        // age: 年龄必须在18到60岁之间
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

2. 场景2:AOP切面注解

java

复制

下载

复制代码
// 1. 定义切面注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecution {
    String value() default "";  // 业务描述
    boolean timing() default true;  // 是否记录执行时间
    Level level() default Level.INFO;  // 日志级别
    
    enum Level {
        DEBUG, INFO, WARN, ERROR
    }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
    String key() default "";  // 缓存key
    long ttl() default 3600;  // 过期时间(秒)
    boolean refresh() default false; // 是否强制刷新
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOnFailure {
    int maxAttempts() default 3;
    long delay() default 1000;  // 重试延迟(毫秒)
    Class<? extends Throwable>[] retryFor() default {Exception.class};
}

// 2. 切面处理器(使用Spring AOP)
@Aspect
@Component
public class AnnotationAspect {
    private static final Logger logger = LoggerFactory.getLogger(AnnotationAspect.class);
    
    // 环绕通知处理@LogExecution
    @Around("@annotation(logExecution)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint, 
                                   LogExecution logExecution) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        
        if (logExecution.timing()) {
            logger.info("开始执行方法: {}", methodName);
        }
        
        try {
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            
            if (logExecution.timing()) {
                logger.info("方法 {} 执行完成,耗时: {}ms", 
                    methodName, endTime - startTime);
            }
            
            return result;
        } catch (Exception e) {
            logger.error("方法 {} 执行失败: {}", methodName, e.getMessage());
            throw e;
        }
    }
    
    // 环绕通知处理@Cacheable
    @Around("@annotation(cacheable)")
    public Object handleCache(ProceedingJoinPoint joinPoint, 
                              Cacheable cacheable) throws Throwable {
        // 生成缓存key
        String cacheKey = generateCacheKey(joinPoint, cacheable);
        
        // 尝试从缓存获取
        Object cachedValue = cacheManager.get(cacheKey);
        if (cachedValue != null && !cacheable.refresh()) {
            return cachedValue;
        }
        
        // 执行方法并缓存结果
        Object result = joinPoint.proceed();
        cacheManager.set(cacheKey, result, cacheable.ttl());
        
        return result;
    }
    
    // 环绕通知处理@RetryOnFailure
    @Around("@annotation(retryOnFailure)")
    public Object retryOnFailure(ProceedingJoinPoint joinPoint, 
                                 RetryOnFailure retryOnFailure) throws Throwable {
        int attempts = 0;
        Throwable lastException;
        
        do {
            attempts++;
            try {
                return joinPoint.proceed();
            } catch (Throwable e) {
                lastException = e;
                
                // 检查是否是需要重试的异常
                boolean shouldRetry = false;
                for (Class<? extends Throwable> exType : retryOnFailure.retryFor()) {
                    if (exType.isInstance(e)) {
                        shouldRetry = true;
                        break;
                    }
                }
                
                if (!shouldRetry || attempts >= retryOnFailure.maxAttempts()) {
                    throw e;
                }
                
                logger.warn("方法执行失败,进行第{}次重试,异常: {}", 
                    attempts, e.getMessage());
                
                // 延迟后重试
                Thread.sleep(retryOnFailure.delay());
            }
        } while (attempts < retryOnFailure.maxAttempts());
        
        throw lastException;
    }
    
    private String generateCacheKey(ProceedingJoinPoint joinPoint, Cacheable cacheable) {
        if (!cacheable.key().isEmpty()) {
            return cacheable.key();
        }
        
        // 基于方法签名和参数生成key
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getMethod().getName();
        Object[] args = joinPoint.getArgs();
        
        StringBuilder keyBuilder = new StringBuilder(methodName);
        for (Object arg : args) {
            keyBuilder.append(":").append(arg != null ? arg.toString() : "null");
        }
        
        return keyBuilder.toString();
    }
}

// 3. 使用示例
@Service
public class UserService {
    
    @LogExecution(value = "获取用户信息", timing = true)
    @Cacheable(key = "user:#id", ttl = 300)
    public User getUserById(Long id) {
        // 从数据库获取用户
        return userRepository.findById(id);
    }
    
    @LogExecution(value = "更新用户信息")
    @RetryOnFailure(maxAttempts = 3, delay = 1000, 
                   retryFor = {SQLException.class})
    public User updateUser(User user) {
        // 更新用户,遇到数据库异常时重试
        return userRepository.save(user);
    }
    
    @LogExecution(value = "批量处理用户", level = LogExecution.Level.WARN)
    @Cacheable(refresh = true)  // 强制刷新缓存
    public List<User> batchProcess(List<Long> userIds) {
        // 批量处理逻辑
        return userRepository.findAllById(userIds);
    }
}

3. 场景3:自定义ORM注解

java

复制

下载

复制代码
// 1. 定义实体注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
    String tableName() default "";
    String schema() default "";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String name() default "";
    String type() default "VARCHAR";
    int length() default 255;
    boolean nullable() default true;
    boolean unique() default false;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
    String generator() default "auto";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transient {
    // 标记字段不持久化
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OneToMany {
    String mappedBy() default "";
    CascadeType[] cascade() default {};
    FetchType fetch() default FetchType.LAZY;
    
    enum CascadeType {
        ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH
    }
    
    enum FetchType {
        LAZY, EAGER
    }
}

// 2. 实体类示例
@Entity(tableName = "users")
public class User {
    @Id(generator = "uuid")
    @Column(name = "user_id", type = "VARCHAR", length = 36)
    private String id;
    
    @Column(name = "username", nullable = false, unique = true)
    private String username;
    
    @Column(name = "email", nullable = false)
    private String email;
    
    @Column(name = "age", type = "INTEGER")
    private Integer age;
    
    @Transient
    private String tempData;  // 不持久化
    
    @OneToMany(mappedBy = "user", cascade = OneToMany.CascadeType.ALL)
    private List<Order> orders;
    
    // getters and setters
}

// 3. 注解处理器(简单ORM)
public class SimpleORM {
    private final DataSource dataSource;
    
    public SimpleORM(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    // 创建表
    public void createTable(Class<?> entityClass) {
        Entity entityAnn = entityClass.getAnnotation(Entity.class);
        if (entityAnn == null) {
            throw new IllegalArgumentException("Class is not an entity");
        }
        
        String tableName = entityAnn.tableName().isEmpty() ? 
            entityClass.getSimpleName().toLowerCase() : entityAnn.tableName();
        
        StringBuilder sql = new StringBuilder();
        sql.append("CREATE TABLE IF NOT EXISTS ").append(tableName).append(" (");
        
        List<String> columns = new ArrayList<>();
        List<String> primaryKeys = new ArrayList<>();
        
        for (Field field : entityClass.getDeclaredFields()) {
            Column columnAnn = field.getAnnotation(Column.class);
            if (columnAnn != null) {
                String columnDef = buildColumnDefinition(field, columnAnn);
                columns.add(columnDef);
                
                // 检查是否是主键
                if (field.getAnnotation(Id.class) != null) {
                    primaryKeys.add(columnAnn.name().isEmpty() ? 
                        field.getName() : columnAnn.name());
                }
            }
        }
        
        sql.append(String.join(", ", columns));
        
        if (!primaryKeys.isEmpty()) {
            sql.append(", PRIMARY KEY (").append(String.join(", ", primaryKeys)).append(")");
        }
        
        sql.append(")");
        
        executeSQL(sql.toString());
    }
    
    // 插入实体
    public <T> void insert(T entity) throws IllegalAccessException {
        Class<?> entityClass = entity.getClass();
        Entity entityAnn = entityClass.getAnnotation(Entity.class);
        String tableName = getTableName(entityClass, entityAnn);
        
        List<String> columnNames = new ArrayList<>();
        List<Object> values = new ArrayList<>();
        List<String> placeholders = new ArrayList<>();
        
        for (Field field : entityClass.getDeclaredFields()) {
            field.setAccessible(true);
            
            Column columnAnn = field.getAnnotation(Column.class);
            Transient transientAnn = field.getAnnotation(Transient.class);
            
            if (columnAnn != null && transientAnn == null) {
                String columnName = columnAnn.name().isEmpty() ? 
                    field.getName() : columnAnn.name();
                Object value = field.get(entity);
                
                columnNames.add(columnName);
                values.add(value);
                placeholders.add("?");
            }
        }
        
        String sql = String.format("INSERT INTO %s (%s) VALUES (%s)",
            tableName,
            String.join(", ", columnNames),
            String.join(", ", placeholders));
        
        executeUpdate(sql, values.toArray());
    }
    
    // 查询实体
    public <T> T findById(Class<T> entityClass, Object id) throws Exception {
        Entity entityAnn = entityClass.getAnnotation(Entity.class);
        String tableName = getTableName(entityClass, entityAnn);
        
        // 找到主键字段
        Field idField = null;
        String idColumn = null;
        
        for (Field field : entityClass.getDeclaredFields()) {
            if (field.getAnnotation(Id.class) != null) {
                idField = field;
                Column columnAnn = field.getAnnotation(Column.class);
                idColumn = columnAnn != null && !columnAnn.name().isEmpty() ? 
                    columnAnn.name() : field.getName();
                break;
            }
        }
        
        if (idField == null) {
            throw new IllegalArgumentException("Entity has no @Id field");
        }
        
        String sql = String.format("SELECT * FROM %s WHERE %s = ?", 
            tableName, idColumn);
        
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setObject(1, id);
            
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    T entity = entityClass.getDeclaredConstructor().newInstance();
                    mapResultSetToEntity(rs, entity);
                    return entity;
                }
            }
        }
        
        return null;
    }
    
    private String buildColumnDefinition(Field field, Column columnAnn) {
        StringBuilder def = new StringBuilder();
        
        String columnName = columnAnn.name().isEmpty() ? 
            field.getName() : columnAnn.name();
        def.append(columnName).append(" ").append(columnAnn.type());
        
        if (columnAnn.type().equals("VARCHAR") || columnAnn.type().equals("CHAR")) {
            def.append("(").append(columnAnn.length()).append(")");
        }
        
        if (!columnAnn.nullable()) {
            def.append(" NOT NULL");
        }
        
        if (columnAnn.unique()) {
            def.append(" UNIQUE");
        }
        
        return def.toString();
    }
    
    private String getTableName(Class<?> entityClass, Entity entityAnn) {
        if (entityAnn == null) {
            throw new IllegalArgumentException("Class is not an entity");
        }
        
        return entityAnn.tableName().isEmpty() ? 
            entityClass.getSimpleName().toLowerCase() : entityAnn.tableName();
    }
    
    private void mapResultSetToEntity(ResultSet rs, Object entity) 
            throws SQLException, IllegalAccessException {
        Class<?> entityClass = entity.getClass();
        
        for (Field field : entityClass.getDeclaredFields()) {
            field.setAccessible(true);
            
            Column columnAnn = field.getAnnotation(Column.class);
            Transient transientAnn = field.getAnnotation(Transient.class);
            
            if (columnAnn != null && transientAnn == null) {
                String columnName = columnAnn.name().isEmpty() ? 
                    field.getName() : columnAnn.name();
                
                Object value = rs.getObject(columnName);
                field.set(entity, value);
            }
        }
    }
    
    private void executeSQL(String sql) {
        // 执行SQL
        System.out.println("Executing SQL: " + sql);
    }
    
    private void executeUpdate(String sql, Object[] params) {
        // 执行带参数的更新
        System.out.println("Executing update: " + sql);
    }
}

// 4. 使用示例
public class ORMTest {
    public static void main(String[] args) throws Exception {
        // 创建数据源
        DataSource dataSource = createDataSource();
        
        // 创建ORM实例
        SimpleORM orm = new SimpleORM(dataSource);
        
        // 创建表
        orm.createTable(User.class);
        
        // 插入用户
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setUsername("john_doe");
        user.setEmail("john@example.com");
        user.setAge(30);
        
        orm.insert(user);
        
        // 查询用户
        User foundUser = orm.findById(User.class, user.getId());
        System.out.println("Found user: " + foundUser.getUsername());
    }
}

4. 场景4:自定义配置注解

java

复制

下载

复制代码
// 1. 定义配置注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
    String prefix() default "";
    boolean reloadable() default false;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
    String value();  // 例如: "${db.url:jdbc:mysql://localhost:3306/test}"
    String defaultValue() default "";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NestedConfiguration {
    String prefix() default "";
}

// 2. 配置类示例
@Configuration(prefix = "app", reloadable = true)
public class AppConfig {
    @Value("${app.name:MyApp}")
    private String appName;
    
    @Value("${app.version:1.0.0}")
    private String version;
    
    @NestedConfiguration(prefix = "database")
    private DatabaseConfig database;
    
    @NestedConfiguration(prefix = "cache")
    private CacheConfig cache;
    
    // getters and setters
}

@Configuration(prefix = "database")
public class DatabaseConfig {
    @Value("${url:jdbc:mysql://localhost:3306/test}")
    private String url;
    
    @Value("${username:root}")
    private String username;
    
    @Value("${password:}")
    private String password;
    
    @Value("${pool.maxSize:10}")
    private int maxPoolSize;
    
    // getters and setters
}

// 3. 配置处理器
public class ConfigProcessor {
    private final Properties properties;
    private final Map<String, Object> configCache = new ConcurrentHashMap<>();
    
    public ConfigProcessor(String configFile) throws IOException {
        this.properties = loadProperties(configFile);
    }
    
    public <T> T getConfig(Class<T> configClass) {
        String cacheKey = configClass.getName();
        
        return (T) configCache.computeIfAbsent(cacheKey, k -> {
            try {
                return createConfigInstance(configClass);
            } catch (Exception e) {
                throw new RuntimeException("Failed to create config instance", e);
            }
        });
    }
    
    private <T> T createConfigInstance(Class<T> configClass) 
            throws IllegalAccessException, InstantiationException {
        Configuration classAnn = configClass.getAnnotation(Configuration.class);
        String basePrefix = classAnn != null ? classAnn.prefix() : "";
        
        T instance = configClass.newInstance();
        
        for (Field field : configClass.getDeclaredFields()) {
            field.setAccessible(true);
            
            // 处理@Value注解
            Value valueAnn = field.getAnnotation(Value.class);
            if (valueAnn != null) {
                String propertyKey = resolvePropertyKey(valueAnn.value(), basePrefix);
                String propertyValue = properties.getProperty(propertyKey);
                
                if (propertyValue == null && !valueAnn.defaultValue().isEmpty()) {
                    propertyValue = valueAnn.defaultValue();
                }
                
                if (propertyValue != null) {
                    Object convertedValue = convertValue(propertyValue, field.getType());
                    field.set(instance, convertedValue);
                }
            }
            
            // 处理@NestedConfiguration注解
            NestedConfiguration nestedAnn = field.getAnnotation(NestedConfiguration.class);
            if (nestedAnn != null && !field.getType().isPrimitive()) {
                String nestedPrefix = basePrefix + 
                    (basePrefix.isEmpty() ? "" : ".") + nestedAnn.prefix();
                
                Object nestedConfig = createNestedConfig(field.getType(), nestedPrefix);
                field.set(instance, nestedConfig);
            }
        }
        
        return instance;
    }
    
    private String resolvePropertyKey(String valueExpr, String basePrefix) {
        // 解析${key:default}格式
        Pattern pattern = Pattern.compile("\\$\\{([^:}]+)(?::([^}]+))?\\}");
        Matcher matcher = pattern.matcher(valueExpr);
        
        if (matcher.matches()) {
            String key = matcher.group(1);
            String fullKey = basePrefix.isEmpty() ? key : basePrefix + "." + key;
            return properties.containsKey(fullKey) ? fullKey : key;
        }
        
        return valueExpr;
    }
    
    private Object convertValue(String value, Class<?> targetType) {
        if (targetType == String.class) {
            return value;
        } else if (targetType == Integer.class || targetType == int.class) {
            return Integer.parseInt(value);
        } else if (targetType == Long.class || targetType == long.class) {
            return Long.parseLong(value);
        } else if (targetType == Boolean.class || targetType == boolean.class) {
            return Boolean.parseBoolean(value);
        } else if (targetType == Double.class || targetType == double.class) {
            return Double.parseDouble(value);
        } else if (targetType == List.class) {
            return Arrays.asList(value.split(","));
        }
        
        throw new IllegalArgumentException("Unsupported type: " + targetType);
    }
    
    private Object createNestedConfig(Class<?> configClass, String prefix) 
            throws IllegalAccessException, InstantiationException {
        Object nestedInstance = configClass.newInstance();
        
        for (Field field : configClass.getDeclaredFields()) {
            field.setAccessible(true);
            
            Value valueAnn = field.getAnnotation(Value.class);
            if (valueAnn != null) {
                String propertyKey = prefix + "." + 
                    valueAnn.value().replace("${", "").replace("}", "");
                String propertyValue = properties.getProperty(propertyKey);
                
                if (propertyValue != null) {
                    Object convertedValue = convertValue(propertyValue, field.getType());
                    field.set(nestedInstance, convertedValue);
                } else if (!valueAnn.defaultValue().isEmpty()) {
                    Object convertedValue = convertValue(valueAnn.defaultValue(), field.getType());
                    field.set(nestedInstance, convertedValue);
                }
            }
        }
        
        return nestedInstance;
    }
    
    private Properties loadProperties(String configFile) throws IOException {
        Properties props = new Properties();
        
        try (InputStream is = getClass().getClassLoader()
                .getResourceAsStream(configFile)) {
            if (is != null) {
                props.load(is);
            }
        }
        
        // 加载系统属性
        props.putAll(System.getProperties());
        
        // 加载环境变量(转换为点分隔格式)
        System.getenv().forEach((key, value) -> {
            String propKey = key.toLowerCase().replace('_', '.');
            props.setProperty(propKey, value);
        });
        
        return props;
    }
}

// 4. 使用示例
public class ConfigTest {
    public static void main(String[] args) throws Exception {
        // 配置文件内容:
        // app.name=MyApplication
        // app.version=2.0.0
        // app.database.url=jdbc:mysql://localhost:3306/prod
        // app.database.username=admin
        // app.database.password=secret
        // app.database.pool.maxSize=20
        // app.cache.enabled=true
        // app.cache.ttl=3600
        
        ConfigProcessor processor = new ConfigProcessor("application.properties");
        
        AppConfig config = processor.getConfig(AppConfig.class);
        
        System.out.println("App Name: " + config.getAppName());  // MyApplication
        System.out.println("Version: " + config.getVersion());   // 2.0.0
        System.out.println("DB URL: " + config.getDatabase().getUrl());  
        // jdbc:mysql://localhost:3306/prod
        System.out.println("Max Pool Size: " + config.getDatabase().getMaxPoolSize());  
        // 20
    }
}

五、高级特性与最佳实践

1. 注解处理器性能优化

java

复制

下载

复制代码
// 1. 缓存反射结果
public class AnnotationCache {
    private static final Map<Class<?>, Map<String, Annotation>> CLASS_CACHE = 
        new ConcurrentHashMap<>();
    
    private static final Map<Method, Annotation[]> METHOD_CACHE = 
        new ConcurrentHashMap<>();
    
    public static <A extends Annotation> A getAnnotation(
            Class<?> clazz, Class<A> annotationType) {
        return clazz.getAnnotation(annotationType); // JVM会缓存
    }
    
    public static Annotation[] getMethodAnnotations(Method method) {
        return METHOD_CACHE.computeIfAbsent(method, m -> m.getAnnotations());
    }
}

// 2. 使用AnnotationUtils(Spring提供)
import org.springframework.core.annotation.AnnotationUtils;

public class SpringAnnotationHelper {
    public static <A extends Annotation> A findAnnotation(
            Class<?> clazz, Class<A> annotationType) {
        // 支持查找继承的注解、元注解等
        return AnnotationUtils.findAnnotation(clazz, annotationType);
    }
    
    public static Object getValue(Annotation annotation, String attributeName) {
        // 安全获取注解属性值
        return AnnotationUtils.getValue(annotation, attributeName);
    }
}

// 3. 预编译注解处理器(APT)
// 使用Java Annotation Processing Tool在编译时处理注解
// 生成代码,减少运行时反射开销

2. 注解的安全考虑

java

复制

下载

复制代码
// 1. 验证注解来源
public class SecureAnnotationProcessor {
    public static boolean isValidAnnotationSource(Annotation annotation) {
        // 检查注解类是否来自可信来源
        Class<?> annClass = annotation.annotationType();
        String className = annClass.getName();
        
        // 只允许来自特定包的注解
        return className.startsWith("com.company.secure.");
    }
    
    // 2. 防止注解属性注入
    public static String sanitizeAnnotationValue(Object value) {
        if (value instanceof String) {
            String strValue = (String) value;
            // 防止XSS等攻击
            return strValue.replace("<", "&lt;")
                          .replace(">", "&gt;")
                          .replace("\"", "&quot;");
        }
        return String.valueOf(value);
    }
}

// 3. 注解权限控制
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
    String value();  // 需要的权限
}

@RequiresPermission("admin")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminOnly {}

public class PermissionAspect {
    @Before("@annotation(adminOnly)")
    public void checkPermission(JoinPoint joinPoint, AdminOnly adminOnly) {
        // 获取当前用户权限
        String currentUserRole = getCurrentUserRole();
        
        // 获取注解上的权限要求
        RequiresPermission requiresPermission = 
            adminOnly.annotationType().getAnnotation(RequiresPermission.class);
        
        if (requiresPermission != null) {
            String requiredRole = requiresPermission.value();
            if (!requiredRole.equals(currentUserRole)) {
                throw new SecurityException("Permission denied");
            }
        }
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

3. 与Java新特性结合

java

复制

下载

复制代码
// 1. 与Records结合(Java 14+)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataModel {
    String tableName();
    boolean audited() default false;
}

@DataModel(tableName = "users", audited = true)
public record UserRecord(
    @Column(name = "user_id") 
    Long id,
    
    @Column(name = "username", nullable = false)
    String username,
    
    @Column(name = "email") 
    String email
) {}

// 2. 与Sealed Classes结合(Java 17+)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DomainEvent {
    String eventType();
    int version() default 1;
}

@DomainEvent(eventType = "USER_CREATED", version = 2)
public sealed class UserEvent permits UserCreatedEvent, UserUpdatedEvent {
    private final Long userId;
    private final Instant timestamp;
    
    public UserEvent(Long userId) {
        this.userId = userId;
        this.timestamp = Instant.now();
    }
}

// 3. 与Pattern Matching结合(Java 16+)
public class AnnotationPatternMatching {
    public static void processAnnotation(Annotation ann) {
        switch (ann) {
            case LogExecution logExec -> 
                System.out.println("Log level: " + logExec.level());
            case Cacheable cache -> 
                System.out.println("Cache TTL: " + cache.ttl());
            case RetryOnFailure retry -> 
                System.out.println("Max attempts: " + retry.maxAttempts());
            case null, default -> 
                System.out.println("Unknown annotation");
        }
    }
}

六、总结与最佳实践

自定义注解最佳实践:

  1. 明确目的:注解应该解决特定问题,避免过度设计

  2. 合理命名:使用名词+Annotation或动词+able等约定

  3. 提供默认值:为注解属性提供合理的默认值

  4. 文档完善:使用@Documented和Javadoc说明用法

  5. 性能考虑:避免在频繁执行的代码路径中使用运行时注解

🔧 选择正确的保留策略:

  • SOURCE级别:仅用于代码检查、文档生成(如@Override)

  • CLASS级别:字节码处理工具使用(如Lombok)

  • RUNTIME级别:框架集成、运行时决策(如Spring、JPA)

性能优化建议:

  1. 缓存反射结果:避免重复调用getAnnotation()

  2. 使用编译时处理:对于代码生成,优先使用APT

  3. 延迟初始化:在真正需要时才处理注解

  4. 批量处理:一次处理多个注解元素

🛡️ 安全注意事项:

  1. 验证注解来源:防止恶意注解注入

  2. 清理注解值:防止XSS等攻击

  3. 权限控制:敏感操作需要权限验证

  4. 输入验证:注解属性值也需要验证

📚 学习路径建议:

  1. 从简单标记注解开始

  2. 学习元注解的作用

  3. 掌握反射API处理注解

  4. 了解编译时注解处理(APT)

  5. 研究主流框架的注解实现

记住 :注解是Java的元编程能力体现,合理使用可以极大提高代码的声明性和可维护性,但滥用会导致代码难以理解和调试。注解应该让代码更清晰,而不是更复杂

相关推荐
love530love2 小时前
【笔记】ComfyUI “OSError: [WinError 38] 已到文件结尾” 报错解决方案
人工智能·windows·python·aigc·comfyui·winerror 38
幽络源小助理2 小时前
SpringBoot+Vue摄影师分享社区源码 – Java项目免费下载 | 幽络源
java·vue.js·spring boot
似水এ᭄往昔2 小时前
【C++】--封装红⿊树实现mymap和myset
开发语言·数据结构·c++·算法·stl
0和1的舞者2 小时前
《软件测试分类指南:8 大维度 + 核心要点梳理》
java·软件测试·单元测试·测试·黑盒测试·白盒测试·测试分类
charlie1145141912 小时前
嵌入式现代C++教程:C++98——从C向C++的演化(3)
c语言·开发语言·c++·笔记·学习·嵌入式
lcreek2 小时前
LeetCode215. 数组中的第K个最大元素、LeetCode912. 排序数组
python·算法·leetcode
TAEHENGV2 小时前
创建目标模块 Cordova 与 OpenHarmony 混合开发实战
android·java·开发语言
程序员zgh2 小时前
C语言 指针用法与区别(指针常量、常量指针、指针函数、函数指针、二级指针)
c语言·开发语言·jvm·c++
cqbzcsq2 小时前
蛋白质功能预测模型DAMPE论文阅读报告
论文阅读·人工智能·python·深度学习·生物信息学