Spring @AliasFor 的三种用法,亲测可用,包含测试代码

查看Spring中的@AliasFor的文档,英文描述如下:

Usage Scenarios
Explicit aliases within an annotation: within a single annotation, @AliasFor can be declared on a pair of attributes to signal that they are interchangeable aliases for each other.
Explicit alias for attribute in meta-annotation: if the annotation attribute of @AliasFor is set to a different annotation than the one that declares it, the attribute is interpreted as an alias for an attribute in a meta-annotation (i.e., an explicit meta-annotation attribute override). This enables fine-grained control over exactly which attributes are overridden within an annotation hierarchy. In fact, with @AliasFor it is even possible to declare an alias for the value attribute of a meta-annotation.
Implicit aliases within an annotation: if one or more attributes within an annotation are declared as attribute overrides for the same meta-annotation attribute (either directly or transitively), those attributes will be treated as a set of implicit aliases for each other, resulting in behavior analogous to that for explicit aliases within an annotation.

下面对每个进行理解并测试

一、Explicit aliases within an annotation

within a single annotation, @AliasFor can be declared on a pair of attributes to signal that they are interchangeable aliases for each other

意思是注解中的属性可以互相为别名进行使用

验证代码:

//定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAliasAnnotation {

    @AliasFor(value = "location")
    String value() default "";

    @AliasFor(value = "value")
    String location() default "";
}

//编写测试类
public class MyClass {

    @MyAliasAnnotation(location = "这是值")
    public static void one(){
    }

    @MyAliasAnnotation(value = "这是值")
    public static void one2(){
    }
}

//编写测试代码
public class MyClassTest {
    
    @Test
    public void test1() throws NoSuchMethodException {
        Consumer<MyAliasAnnotation> logic = a -> {
            Assert.assertTrue("", "这是值".equals(a.value()));
            Assert.assertTrue("", a.value().equals(a.location()));
        };

        MyAliasAnnotation aliasAnnotation = AnnotationUtils.findAnnotation(MyClass.class.getMethod("one"), MyAliasAnnotation.class);
        logic.accept(aliasAnnotation);

        MyAliasAnnotation aliasAnnotation2 = AnnotationUtils.findAnnotation(MyClass.class.getMethod("one2"), MyAliasAnnotation.class);
        logic.accept(aliasAnnotation2);

    }

}

测试结果截图

 

二、Explicit alias for attribute in meta-annotation

if the annotation attribute of @AliasFor is set to a different annotation than the one that declares it, the attribute is interpreted as an alias for an attribute in a meta-annotation (i.e., an explicit meta-annotation attribute override). This enables fine-grained control over exactly which attributes are overridden within an annotation hierarchy. In fact, with @AliasFor it is even possible to declare an alias for the value attribute of a meta-annotation.

简略意思是注解中使用了元注解时,可以对元注解的值进行重写,目的是为了能达到和类继承中override相似的功能

验证代码:

//编写元注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AnnotaionBase {
    String value() default "";
}

//编写子注解,其中子注解打上了元注解@AnnotaionBase标识
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@AnnotaionBase
public @interface AnnotationChild {

    @AliasFor(annotation = AnnotaionBase.class, attribute = "value")
    String extendValue() default "";
}


//编写测试类
@AnnotationChild(extendValue = "extendValue")
public class ExtendClass {

}

//编写测试代码
public class ExtendAnnotationTest {
    @Test
    public void test() throws NoSuchMethodException {
        AnnotaionBase annotaionBase = AnnotatedElementUtils.findMergedAnnotation(ExtendClass.class, AnnotaionBase.class);
        Assert.assertTrue("", annotaionBase.value().equals("extendValue"));
    }
}

测试结果截图

其中要注意的是这里要使用AnnotatedElementUtils,如果还是用AnnotationUtils会发现继承不起作用,这个在AnnotationUtils类的英文文档中也有说明:

在这里也说明了看文档的重要性,往往很多坑在文档里会有特别说明

三、Implicit aliases within an annotation

if one or more attributes within an annotation are declared as attribute overrides for the same meta-annotation attribute (either directly or transitively), those attributes will be treated as a set of implicit aliases for each other, resulting in behavior analogous to that for explicit aliases within an annotation.

简略意思是注解中使用了元注解时,可以对元注解的值进行重写,并且可用多个不同的别名进行重写(其实就是一和二的结合)

验证代码:

//编写元注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AnnotaionBase {
    String value() default "";
}

//编写子注解,其中子注解打上了元注解@AnnotaionBase标识
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@AnnotaionBase
public @interface AnnotationChildMutilAttribute {

    @AliasFor(annotation = AnnotaionBase.class, attribute = "value")
    String extendValue1() default "";

    @AliasFor(annotation = AnnotaionBase.class, attribute = "value")
    String extendValue2() default "";
}

//编写测试类
public class ExtendMutilClass {

    @AnnotationChildMutilAttribute(extendValue1 = "extendValue1")
    public static class extendValue1{
    }

    @AnnotationChildMutilAttribute(extendValue2 = "extendValue2")
    public static class extendValue2{
    }
}

//编写测试代码
public class ExtendAnnotaionMutilAttributeTest {
    @Test
    public void test(){
        AnnotationChildMutilAttribute mergedAnnotation1 = AnnotatedElementUtils.findMergedAnnotation(ExtendMutilClass.extendValue1.class, AnnotationChildMutilAttribute.class);
        Assert.assertTrue(mergedAnnotation1.extendValue1().equals("extendValue1"));
        Assert.assertTrue(mergedAnnotation1.extendValue2().equals("extendValue1"));

        AnnotationChildMutilAttribute mergedAnnotation2 = AnnotatedElementUtils.findMergedAnnotation(ExtendMutilClass.extendValue2.class, AnnotationChildMutilAttribute.class);
        Assert.assertTrue(mergedAnnotation2.extendValue1().equals("extendValue2"));
        Assert.assertTrue(mergedAnnotation2.extendValue2().equals("extendValue2"));
    }
}

测试结果截图

 

<think>我们正在讨论Spring注解的实际应用,特别是自定义注解和组合常用SpringBoot注解的使用方法。根据用户的问题,他想要一个关于Spring注解的实战教程,重点在自定义注解的实现和详解。结合用户提供的引用,我们可以从以下几个方面展开:1.自定义注解的基础知识:如何定义注解(使用@interface),元注解(如@Target,@Retention等)的作用。2.自定义注解的处理:如何通过AOP、拦截器或者注解处理器来处理自定义注解。3.实际应用场景:如日志记录、权限验证、数据加密、参数校验等。4.组合注解的使用:如何将多个SpringBoot注解组合成一个自定义注解,简化配置。下面我将按照这些要点进行详细讲解,并给出代码示例。###1.自定义注解基础自定义注解是通过Java的`@interface`关键字定义的。在定义时,我们需要使用元注解来指定注解的使用范围、保留策略等。**元注解说明**:-`@Target`:指定注解可以应用的目标元素类型(如类、方法、字段等)。常用值有`ElementType.TYPE`(类/接口)、`ElementType.METHOD`(方法)、`ElementType.FIELD`(字段)等。-`@Retention`:指定注解的保留策略。通常我们使用`RetentionPolicy.RUNTIME`,这样注解在运行时可以通过反射获取。-`@Documented`:表明该注解应该被javadoc工具记录。-`@Inherited`:表明子类可以继承父类中的该注解。**示例:定义一个方法级别的注解**```javaimportjava.lang.annotation.*;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceMyCustomAnnotation{Stringvalue()default"";//注解属性,可以设置默认值booleanenable()defaulttrue;}```###2.自定义注解的处理定义注解后,我们需要在运行时处理它。处理方式通常有以下几种:-**使用AOP(面向切面编程)**:这是最常用的方式,通过定义一个切面,在方法执行前后处理注解。-**使用拦截器(Interceptor)**:在Web应用中,可以通过拦截器处理控制器方法上的注解。-**实现`BeanPostProcessor`接口**:在Spring容器初始化Bean前后进行处理。####示例:使用AOP处理自定义注解假设我们定义了一个用于记录方法执行时间的注解`@LogExecutionTime`。**步骤1:定义注解**```java@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceLogExecutionTime{}```**步骤2:创建切面处理注解**```javaimportorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.springframework.stereotype.Component;@Aspect@ComponentpublicclassLogExecutionTimeAspect{//环绕通知:匹配带有@LogExecutionTime注解的方法@Around("@annotation(LogExecutionTime)")publicObjectlogExecutionTime(ProceedingJoinPointjoinPoint)throwsThrowable{longstartTime=System.currentTimeMillis();Objectresult=joinPoint.proceed();//执行目标方法longendTime=System.currentTimeMillis();System.out.println(joinPoint.getSignature()+"executedin"+(endTime-startTime)+"ms");returnresult;}}```**步骤3:在方法上使用注解**```java@ServicepublicclassMyService{@LogExecutionTimepublicvoidserve()throwsInterruptedException{Thread.sleep(1000);//模拟耗时操作}}```当调用`serve()`方法时,切面会计算并打印方法执行时间。###3.实际应用场景####场景1:权限验证(引用[3]中提到)我们可以定义一个`@PreAuthorize`注解(类似SpringSecurity的注解),结合AOP进行权限检查。**定义注解:**```java@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfacePreAuthorize{String[]roles()default{};}```**切面处理:**```java@Aspect@ComponentpublicclassAuthorizationAspect{@Around("@annotation(preAuthorize)")publicObjectauthorize(ProceedingJoinPointjoinPoint,PreAuthorizepreAuthorize)throwsThrowable{//模拟获取当前用户角色(实际中可能从SecurityContext获取)Set<String>userRoles=getCurrentUserRoles();//检查用户是否拥有注解中指定的任意角色for(Stringrole:preAuthorize.roles()){if(userRoles.contains(role)){returnjoinPoint.proceed();//有权限,执行方法}}thrownewAccessDeniedException("Accessdenied");}}```####场景2:参数校验(引用[5]中提到)虽然Spring提供了`@Valid`注解,但我们也可以自定义校验注解,例如校验字符串不能包含非法字符。**定义注解:**```java@Target({ElementType.FIELD,ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Constraint(validatedBy=ForbiddenCharsValidator.class)//指定校验器public@interfaceNoForbiddenChars{Stringmessage()default"包含非法字符";Class<?>[]groups()default{};Class<?extendsPayload>[]payload()default{};Stringforbidden()default"";//指定非法字符}```**实现校验器:**```javapublicclassForbiddenCharsValidatorimplementsConstraintValidator<NoForbiddenChars,String>{privateStringforbiddenChars;@Overridepublicvoidinitialize(NoForbiddenCharsconstraintAnnotation){this.forbiddenChars=constraintAnnotation.forbidden();}@OverridepublicbooleanisValid(Stringvalue,ConstraintValidatorContextcontext){if(value==null)returntrue;for(charc:forbiddenChars.toCharArray()){if(value.indexOf(c)!=-1)returnfalse;}returntrue;}}```**使用:**```javapublicclassUser{@NoForbiddenChars(forbidden="!@#$%")privateStringusername;}```###4.组合注解的使用在SpringBoot中,我们可以将多个注解组合成一个自定义注解,简化配置。例如,创建一个组合了`@Configuration`、`@EnableTransactionManagement`和`@ComponentScan`的注解。**定义组合注解:**```java@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration@EnableTransactionManagement@ComponentScan(basePackages="com.example")public@interfaceMyAppConfig{}```这样,在配置类上使用`@MyAppConfig`就等同于同时使用了这三个注解。###总结通过自定义注解,我们可以将横切关注点(如日志、权限、校验)与业务逻辑分离,提高代码的可读性和可维护性。处理自定义注解的常用方式是AOP,它允许我们在不修改原有代码的基础上增强功能。同时,组合注解能减少重复配置,使代码更简洁[^2][^3][^4]。§§相关问题§§1.如何通过反射获取自定义注解的属性值?2.在SpringBoot中如何实现自定义注解的优先级控制(例如多个切面的执行顺序)?3.如何为自定义注解编写单元试?</think>### Spring 注解实战教程:自定义注解实现与详解 在 Spring Boot 中,自定义注解是扩展框架功能、实现代码复用的强大工具。下面通过实战示例讲解如何创建和使用自定义注解,结合 AOP 实现功能增强。 #### 一、自定义注解基础 1. **注解定义语法** ```java import java.lang.annotation.*; @Target(ElementType.METHOD) // 作用在方法上 @Retention(RetentionPolicy.RUNTIME) // 运行时保留 @Documented public @interface PerformanceMonitor { String value() default ""; // 可选参数 boolean logParams() default false; // 是否记录参数 } ``` - `@Target`:指定注解应用位置(方法、类、字段等) - `@Retention`:决定注解在何时可用(SOURCE/CLASS/RUNTIME) - 注解属性:支持基本类型、String、Class、枚举等数据类型[^2][^4] #### 二、实战案例:方法性能监控 1. **创建注解处理器(AOP实现)** ```java @Aspect @Component public class PerformanceAspect { // 定义切入点:所有被@PerformanceMonitor注解的方法 @Around("@annotation(monitor)") public Object monitorPerformance(ProceedingJoinPoint joinPoint, PerformanceMonitor monitor) throws Throwable { long start = System.currentTimeMillis(); String methodName = joinPoint.getSignature().getName(); try { return joinPoint.proceed(); // 执行目标方法 } finally { long duration = System.currentTimeMillis() - start; System.out.printf("[%s]执行耗时: %dms%n", methodName, duration); // 如果开启参数记录 if(monitor.logParams()) { Object[] args = joinPoint.getArgs(); System.out.println("方法参数: " + Arrays.toString(args)); } } } } ``` 2. **业务层使用注解** ```java @Service public class OrderService { @PerformanceMonitor(logParams = true) public Order createOrder(OrderRequest request) { // 业务逻辑... } } ``` 当调用`createOrder()`时,会自动输出: ``` [createOrder]执行耗时: 45ms 方法参数: [OrderRequest(userId=101, items=[...])] ``` #### 三、高级应用:组合注解 将多个 Spring 注解组合成自定义注解,简化配置: ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @RestController @RequestMapping("/api/v1") @ResponseBody public @interface ApiV1Controller { @AliasFor(annotation = RequestMapping.class, attribute = "path") String[] value() default {}; } ``` 使用组合注解: ```java @ApiV1Controller("/orders") public class OrderController { // 等效于: // @RestController // @RequestMapping("/api/v1/orders") // @ResponseBody } ``` #### 四、实际应用场景 1. **权限控制** ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequireRole { UserRole value(); } // AOP实现权限校验 @Before("@annotation(requireRole)") public void checkRole(RequireRole requireRole) { if(!currentUser.hasRole(requireRole.value())) { throw new AccessDeniedException(); } } ``` 2. **数据加密(引用[4]场景)** ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface EncryptResponse {} // 使用AOP对返回值加密 @AfterReturning(pointcut="@annotation(EncryptResponse)", returning="result") public void encryptResult(Object result) { return EncryptionUtil.encrypt(result); } ``` 3. **参数校验(引用[5]扩展)** ```java @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = PhoneValidator.class) public @interface ValidPhone { String message() default "无效的手机号"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } // 自定义校验器 public class PhoneValidator implements ConstraintValidator<ValidPhone, String> { public boolean isValid(String phone, ConstraintValidatorContext context) { return phone.matches("^1[3-9]\\d{9}$"); } } ``` #### 五、最佳实践 1. **设计原则** - 单一职责:每个注解只解决一个问题 - 明确命名:如`@CacheResult`比`@Cache`更清晰 - 合理默认值:减少必要配置项 2. **性能考虑** - 避免在注解处理器中执行耗时操作 - 使用`@Conditional`实现条件化注解处理 - 通过`@Order`控制多个切面的执行顺序 3. **调试技巧** ```java // 检查方法是否包含注解 if(method.isAnnotationPresent(PerformanceMonitor.class)) { PerformanceMonitor ann = method.getAnnotation(PerformanceMonitor.class); } ``` 自定义注解通过将横切关注点(日志、安全、事务等)与业务逻辑分离,显著提升代码的可维护性和复用性[^3][^4]。掌握注解+AOP的组合使用,是 Spring 高级开发的关键技能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值