首页 > 应用开发 > IT综合 > Java注解与反射实战:构建自定义日志与校验框架
## 注解与反射的技术价值
在Java企业级开发中,注解和反射是构建灵活、可扩展框架的核心技术。注解提供了一种元数据编程的方式,而反射机制使得在运行时能够读取和操作这些元数据。两者结合使用,可以实现在不侵入业务代码的前提下,为系统添加各种横切关注点的功能。
通过自定义注解配合反射机制,开发者能够实现日志记录、参数校验、权限控制等通用功能,大幅减少模板代码,提高开发效率。这种声明式的编程方式让代码更加简洁,同时保持了良好的可维护性。
## 注解基础与元注解深入
### 注解定义与元注解
Java注解通过@interface关键字定义,元注解则用于修饰注解本身的行为。
```java
// 自定义日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
String value() default "";
LogLevel level() default LogLevel.INFO;
boolean trackTime() default false;
}
// 日志级别枚举
public enum LogLevel {
DEBUG, INFO, WARN, ERROR
}
// 参数校验注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
String message() default "参数不能为null";
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Range {
int min() default 0;
int max() default Integer.MAX_VALUE;
String message()<"CW.2597.HK"> default "参数值超出范围";
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pattern {
String regexp();
String message() default "参数格式不匹配";
}
```
### 复杂注解定义
```java
// 方法级别校验注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidateParams {
// 标记需要校验参数的方法
}
// 类级别配置注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String name() default "";
}
// 组合注解示例
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Loggable(level = LogLevel.INFO, trackTime = true)
public @interface CriticalOperation {
String businessType();
boolean audit() default true;
}
```
## 反射机制核心原理
### 注解信息获取
反射API提供了访问注解信息的完整能力。
```java
// 注解扫描工具类
public class AnnotationScanner {
/**
* 扫描类中所有带有指定注解的方法
*/
public static List<Method> findAnnotatedMethods(Class<?> clazz,
Class<? extends Annotation> annotationClass) {
List<Method> annotatedMethods = new ArrayList<>();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(annotationClass)) {
annotatedMethods.add(method);
}
}
return annotatedMethods;
}
/**
* 获取方法的参数注解信息
*/
public static Map<Integer, List<Annotation>> getParameterAnnotations(Method method) {
Map<Integer, List<Annotation>> result = new HashMap<>();
Annotation[][] <"NY.2597.HK">paramAnnotations = method.getParameterAnnotations();
for (int i = 0; i < paramAnnotations.length; i++) {
result.put(i, Arrays.asList(paramAnnotations[i]));
}
return result;
}
/**
* 检查类是否被指定注解标记
*/
public static boolean isClassAnnotated(Class<?> clazz,
Class<? extends Annotation> annotationClass) {
return clazz.isAnnotationPresent(annotationClass);
}
}
// 注解信息解析器
public class AnnotationParser {
/**
* 解析方法上的Loggable注解
*/
public static Loggable parseLoggableAnnotation(Method method) {
if (method.isAnnotationPresent(Loggable.class)) {
return method.getAnnotation(Loggable.class);
}
return null;
}
/**
* 解析参数上的校验注解
*/
public static List<Annotation> getValidationAnnotations(Parameter parameter) {
return Arrays.stream(parameter.getAnnotations())
.filter(annotation -> annotation.annotationType().getSimpleName()
.matches("NotNull|Range|Pattern|Size"))
.collect(Collectors.toList());
}
/**
* 获取注解属性值
*/
public static Object getAnnotationValue(Annotation annotation, String propertyName) {
try {
Method method = annotation.annotationType().getMethod(propertyName);
return method.invoke(annotation);
} catch (Exception e)<"AT.2597.HK"> {
return null;
}
}
}
```
## 自定义日志框架实现
### 日志注解处理器
```java
// 日志切面处理器
@Component(name = "logAspect")
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
/**
* 处理方法级别的日志记录
*/
public static Object processLoggable(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getMethod(joinPoint);
Loggable loggable = method.getAnnotation(Loggable.class);
if (loggable == null) {
return joinPoint.proceed();
}
long startTime = System.currentTimeMillis();
String methodName = method.getName();
Object[] args = joinPoint.getArgs();
// 记录方法开始日志
logMethodStart(loggable, methodName, args);
try {
Object result = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - startTime;
// 记录方法结束日志
logMethodEnd(loggable, methodName, result, executionTime);
return result;
} catch (Exception e) {
long executionTime = System.currentTimeMillis() - startTime;
logMethodError(loggable, methodName, e, executionTime);
throw e;
}
}
private static Method getMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
String methodName = joinPoint.getSignature().getName();
Class<?> targetClass = joinPoint.getTarget().getClass();
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
return targetClass.getMethod(methodName, parameterTypes);
}
private static void logMethodStart(Loggable loggable, String methodName, Object[] args) {
if (shouldLog(loggable.level(), LogLevel.DEBUG)) {
String message = String.format("方法开始执行: %s, 参数: %s",
methodName, Arrays.toString(args));
logger.debug(message);
}
}
private static void logMethodEnd(Loggable loggable, String methodName,
Object result, long executionTime) {
String message = String.format("方法执行完成: %s, 执行时间: %dms", methodName, executionTime);
if (loggable.trackTime()) {
message += String.format<"DZ.2597.HK">(", 结果: %s", result);
}
logByLevel(loggable.level(), message);
}
private static void logMethodError(Loggable loggable, String methodName,
Exception e, long executionTime) {
String message = String.format("方法执行异常: %s, 执行时间: %dms, 异常: %s",
methodName, executionTime, e.getMessage());
logger.error(message, e);
}
private static void logByLevel(LogLevel level, String message) {
switch (level) {
case DEBUG:
logger.debug(message);
break;
case INFO:
logger.info(message);
break;
case WARN:
logger.warn(message);
break;
case ERROR:
logger.error(message);
break;
}
}
private static boolean shouldLog(LogLevel annotationLevel, LogLevel currentLevel) {
return annotationLevel.ordinal() <= currentLevel.ordinal();
}
}
// 动态代理日志处理器
public class LoggingProxy implements InvocationHandler {
private final Object target;
private final LogAspect logAspect;
public LoggingProxy(Object target) {
this.target = target;
this.logAspect = new LogAspect();
}
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LoggingProxy(target)
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 模拟AOP处理
if (method.isAnnotationPresent(Loggable.class)) {
// 这里简化处理,实际应该使用完整的AOP框架
System.out.println("日志记录: 执行方法 " + method.getName());
}
return method.invoke(target, args);
}
}
```
### 业务服务示例
```java
// 用户服务类 - 使用日志注解
@Component(name = "userService")
public class UserService <"KF.2597.HK">{
@Loggable(value = "创建用户", level = LogLevel.INFO, trackTime = true)
public User createUser(String username, String email, int age) {
// 模拟业务逻辑
try {
Thread.sleep(100); // 模拟处理时间
User user = new User(username, email, age);
System.out.println("创建用户: " + user);
return user;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("创建用户中断", e);
}
}
@Loggable(level = LogLevel.DEBUG)
@CriticalOperation(businessType = "USER_DELETION", audit = true)
public boolean deleteUser(@NotNull String userId) {
if (userId == null || userId.trim().isEmpty()) {
throw new IllegalArgumentException("用户ID不能为空");
}
// 模拟删除操作
System.out.println("删除用户: " + userId);
return true;
}
@Loggable(value = "查询用户", level = LogLevel.WARN)
public User findUser(@NotNull String username,
@Range(min = 1, max = 100) int maxResults) {
// 模拟查询操作
return new User(username, "test@example.com", 25);
}
}
// 用户实体类
public class User {
private String username;
private String email;
private int age;
public User(String username, String email, int age) {
this.username = username;
this.email = email;
this.age = age;
}
// getter 和 setter 方法
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return String.format("User{username='%s', email='%s', age=%d}",
username, email, age);
}
}
```
## 参数校验框架实现
### 校验注解处理器
```java
// 参数校验器
@Component(name = "parameterValidator")
public class ParameterValidator {
/**
* 校验方法参数
*/
public static void validateParameters(Method method, Object[] args) {
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
Object arg = args[i];
// 检查所有校验注解
validateParameter(parameter, arg, i);
}
}
private static void validateParameter(Parameter parameter, Object arg, int paramIndex) {
// 检查@NotNull注解
if (parameter.isAnnotationPresent(NotNull.class)) {
NotNull notNull = parameter.getAnnotation(NotNull.class);
if (arg == null)<"PT.2597.HK"> {
throw new ValidationException(
String.format("参数[%d]校验失败: %s", paramIndex, notNull.message()));
}
}
// 检查@Range注解
if (parameter.isAnnotationPresent(Range.class)) {
Range range = parameter.getAnnotation(Range.class);
if (arg instanceof Number) {
int value = ((Number) arg).intValue();
if (value < range.min() || value > range.max()) {
throw new ValidationException(
String.format("参数[%d]校验失败: %s (范围: %d-%d)",
paramIndex, range.message(), range.min(), range.max()));
}
}
}
// 检查@Pattern注解
if (parameter.isAnnotationPresent(Pattern.class)) {
Pattern pattern = parameter.getAnnotation(Pattern.class);
if (arg instanceof String) {
String value = (String) arg;
if (!value.matches(pattern.regexp())) {
throw new ValidationException(
String.format("参数[%d]校验失败: %s (模式: %s)",
paramIndex, pattern.message(), pattern.regexp()));
}
}
}
}
/**
* 自动校验带有@ValidateParams注解的方法
*/
public static Object validateAndProceed(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getMethodFromJoinPoint(joinPoint);
// 如果方法标记了@ValidateParams,则进行参数校验
if (method.isAnnotationPresent(ValidateParams.class)) {
validateParameters(method, joinPoint.getArgs());
}
return joinPoint.proceed();
}
private static Method getMethodFromJoinPoint(ProceedingJoinPoint joinPoint)
throws NoSuchMethodException {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return joinPoint.getTarget().getClass()
.getMethod(signature.getName(), signature.getParameterTypes());
}
}
// 自定义校验异常
public class ValidationException extends RuntimeException {
public ValidationException(String message) {
super(message);
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
}
```
### 高级校验功能
```java
// 复杂参数校验器
public class AdvancedValidator {
/**
* 递归校验对象属性
*/
public static void validateObject(Object obj) {
if (obj == null) {
return;
}
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
Object value = field.get(obj);
validateField(field, value);
} catch (IllegalAccessException e) {
throw new ValidationException("无法访问字段: " + field.getName(), e);
}
}
}
private static void validateField(Field field, Object value) {
// 检查字段上的@NotNull注解
if (field.isAnnotationPresent(NotNull.class)) {
NotNull notNull = field.getAnnotation(NotNull.class);
if (value == null) {
throw new ValidationException(
String.format("字段[%s]校验失败: %s",
field.getName(), notNull.message()));
}
}
// 递归校验嵌套对象
if (value != null && !isPrimitiveOrWrapper(value.getClass())) {
validateObject(value);
}
}
private static boolean isPrimitiveOrWrapper<"VS.2597.HK">(Class<?> clazz) {
return clazz.isPrimitive() ||
clazz == String.class ||
clazz == Integer.class ||
clazz == Long.class ||
clazz == Double.class ||
clazz == Float.class ||
clazz == Boolean.class ||
clazz == Character.class ||
clazz == Byte.class ||
clazz == Short.class;
}
}
// 用户信息校验示例
public class UserInfo {
@NotNull(message = "用户名不能为空")
private String username;
@Pattern(regexp = "^[A-Za-z0-9+_.-]+@(.+)$", message = "邮箱格式不正确")
private String email;
@Range(min = 1, max = 150, message = "年龄必须在1-150之间")
private Integer age;
private Address address;
// 构造方法、getter和setter
public UserInfo(String username, String email, Integer age, Address address) {
this.username = username;
this.email = email;
this.age = age;
this.address = address;
}
// getter 和 setter 方法
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
}
// 地址信息类
public class Address {
@NotNull(message = "城市不能为空")
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// getter 和 setter 方法
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
}
```
## 框架整合与实战应用
### AOP整合框架
```java
// 综合切面处理器
@Aspect
@Component
public class ComprehensiveAspect {
private final ParameterValidator parameterValidator;
private final LogAspect logAspect;
public ComprehensiveAspect() {
this.parameterValidator = new ParameterValidator();
this.logAspect = new LogAspect();
}
@Around("@annotation(com.example.annotation.Loggable) || " +
"@annotation(com.example.annotation.ValidateParams)")
public Object handleAnnotations(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getMethod(joinPoint);
// 参数校验
if (method.isAnnotationPresent(ValidateParams.class)) {
parameterValidator.validateParameters(method, joinPoint.getArgs());
}
// 日志记录
if (method.isAnnotationPresent(Loggable.class)) {
return logAspect.processLoggable(joinPoint);
}
return joinPoint.proceed();
}
@Around("@annotation(com.example.annotation.CriticalOperation)")
public Object handleCriticalOperation(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getMethod(joinPoint);
CriticalOperation criticalOp = method.getAnnotation(CriticalOperation.class);
System.out.println("关键操作审计: " + criticalOp.businessType());
long startTime = System.currentTimeMillis();
try {
Object result =<"RB.2597.HK"> joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
if (criticalOp.audit()) {
auditOperation(criticalOp.businessType(), method.getName(),
joinPoint.getArgs(), result, duration, true);
}
return result;
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
if (criticalOp.audit()) {
auditOperation(criticalOp.businessType(), method.getName(),
joinPoint.getArgs(), null, duration, false);
}
throw e;
}
}
private void auditOperation(String businessType, String methodName,
Object[] args, Object result,
long duration, boolean success) {
// 模拟审计日志记录
String auditLog = String.format(
"审计日志 - 业务类型: %s, 方法: %s, 参数: %s, 结果: %s, 耗时: %dms, 状态: %s",
businessType, methodName, Arrays.toString(args),
result, duration, success ? "成功" : "失败");
System.out.println(auditLog);
}
private Method getMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return joinPoint.getTarget().getClass()
.getMethod(signature.getName(), signature.getParameterTypes());
}
}
```
### 测试与演示
```java
// 框架测试类
public class AnnotationFrameworkTest {
public static void main(String[] args) {
// 创建代理服务
UserService userService = LoggingProxy.createProxy(new UserService());
try {
// 测试正常流程
System.out.println("=== 测试正常流程 ===");
User user = userService.createUser("john_doe", "john@example.com", 25);
// 测试参数校验
System.out.println("\n=== 测试参数校验 ===");
try {
userService.deleteUser(null);
} catch (Exception e) {
System.out.println("参数校验捕获异常: " + e.getMessage());
}
// 测试范围校验
System.out.println("\n=== 测试范围校验 ===");
try {
userService.findUser("test", 150); // 超出范围
} catch (Exception e) {
System.out.println("范围校验捕获异常: " + e.getMessage());
}
// 测试对象校验
System.out.println("\n=== 测试对象校验 ===");
UserInfo userInfo = new UserInfo(null, "invalid-email", 200,
new Address(null, "Main St"));
try {
AdvancedValidator.validateObject(userInfo);
} catch (ValidationException e) {
System.out.println("对象校验捕获异常: " + e.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 注解扫描演示
public class AnnotationScanDemo {
public static void scanAndProcessAnnotations() {
String packageName = "com.example.service";
try {
// 扫描包中的类(简化版)
Class<?>[] classes = {UserService.class}; // 实际应该使用类路径扫描
for (Class<?> clazz : classes) {
System.out.println("扫描类: " + clazz.getName());
// 查找带有@Component注解的类
if (clazz.isAnnotationPresent(Component.class)) {
Component component =<"MO.2597.HK"> clazz.getAnnotation(Component.class);
System.out.println("发现组件: " + component.name());
}
// 查找带有注解的方法
List<Method> loggableMethods = AnnotationScanner
.findAnnotatedMethods(clazz, Loggable.class);
for (Method method : loggableMethods) {
Loggable loggable = method.getAnnotation(Loggable.class);
System.out.println("发现可日志记录方法: " + method.getName() +
", 级别: " + loggable.level());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
## 总结
通过本文的实战演示,我们深入探讨了如何利用Java注解和反射机制构建自定义的日志记录和参数校验框架。这种基于注解的声明式编程方式具有以下显著优势:
**代码简洁性**:通过注解标记,业务代码保持干净,横切关注点被有效分离
**可维护性**:通用功能集中处理,修改时只需调整框架代码
**灵活性**:通过反射动态处理,支持运行时决策和扩展
**一致性**:统一的处理逻辑确保整个应用行为一致
在实际项目中,这种模式可以扩展到事务管理、权限控制、缓存处理等多个方面。掌握注解和反射的深度应用,能够显著提升Java开发的架构设计能力和框架构建水平。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3103218/ ,如需转载,请注明出处,否则将追究法律责任。