一、注解基础概念
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("<", "<")
.replace(">", ">")
.replace("\"", """);
}
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");
}
}
}
六、总结与最佳实践
✅ 自定义注解最佳实践:
-
明确目的:注解应该解决特定问题,避免过度设计
-
合理命名:使用名词+Annotation或动词+able等约定
-
提供默认值:为注解属性提供合理的默认值
-
文档完善:使用@Documented和Javadoc说明用法
-
性能考虑:避免在频繁执行的代码路径中使用运行时注解
🔧 选择正确的保留策略:
-
SOURCE级别:仅用于代码检查、文档生成(如@Override)
-
CLASS级别:字节码处理工具使用(如Lombok)
-
RUNTIME级别:框架集成、运行时决策(如Spring、JPA)
⚡ 性能优化建议:
-
缓存反射结果:避免重复调用getAnnotation()
-
使用编译时处理:对于代码生成,优先使用APT
-
延迟初始化:在真正需要时才处理注解
-
批量处理:一次处理多个注解元素
🛡️ 安全注意事项:
-
验证注解来源:防止恶意注解注入
-
清理注解值:防止XSS等攻击
-
权限控制:敏感操作需要权限验证
-
输入验证:注解属性值也需要验证
📚 学习路径建议:
-
从简单标记注解开始
-
学习元注解的作用
-
掌握反射API处理注解
-
了解编译时注解处理(APT)
-
研究主流框架的注解实现
记住 :注解是Java的元编程能力体现,合理使用可以极大提高代码的声明性和可维护性,但滥用会导致代码难以理解和调试。注解应该让代码更清晰,而不是更复杂。