自定义注解

概要

前面用了自定义注解实现了限流,就顺便把自定义注解写一下

基本语法

import java.lang.annotation.*;
 
@Retention(RetentionPolicy.RUNTIME) // 指定注解的保留策略
@Target({ElementType.TYPE, ElementType.METHOD}) // 指定注解的使用范围
public @interface MyAnnotation {
    // 在这里定义注解的元素
    String value(); // 示例元素,名为"value"
    int count() default 1; // 示例元素,名为"count",并指定默认值
}

@Retention

Retention(RetentionPolicy.RUNTIME):指定了注解的保留策略。RetentionPolicy.RUNTIME表示注解会被保留到运行时,这样我们就可以通过反射来获取注解信息。

在 Java 中,@Retention 注解用于指定注解的保留策略,即注解在什么级别保存,有以下三种保留策略类型:

RetentionPolicy.SOURCE:源代码级别保留策略表示注解只会保留在源代码中,编译器会丢弃这些注解,不会包含在编译后的 class 文件中。这意味着无法通过反射在运行时获取这些注解的信息。

RetentionPolicy.CLASS:类级别保留策略表示注解会被保留到编译后的 class 文件中,但是 JVM 加载类时会丢弃这些注解。这意味着在运行时无法通过反射获取这些注解的信息,默认情况下,如果不指定 @Retention,则为 RetentionPolicy.CLASS。

RetentionPolicy.RUNTIME:运行时保留策略表示注解会被保留到运行时,编译器会将注解信息包含在编译后的 class 文件中,并且 JVM 加载类时会将这些注解加载到内存中,因此可以通过反射在运行时获取这些注解的信息。

@Target

@Target({ElementType.TYPE.ElementType.METHOD}):指定了注解可以应用的地方,比如类、方法、字段等。在这个例子中,MyAnnotation 注解可以应用在类和方法上。

以下是 ElementType 的值:

1. `ElementType.ANNOTATION_TYPE`:可以应用在注解类型上。
2. `ElementType.CONSTRUCTOR`:可以应用在构造方法上。
3. `ElementType.FIELD`:可以应用在字段(成员变量)上。
4. `ElementType.LOCAL_VARIABLE`:可以应用在局部变量上。
5. `ElementType.METHOD`:可以应用在方法上。
6. `ElementType.PACKAGE`:可以应用在包上。
7. `ElementType.PARAMETER`:可以应用在方法的参数上。
8. `ElementType.TYPE`:可以应用在类、接口(包括注解类型)或枚举上。

应用场景

限流

上一篇有提到,拦截器+自定义注解实现

权限验证

也是上一篇有提到,拦截器+redis缓存实现。也有SpringSecurity可以实现。这里可以用权限枚举+自定义注解

参数校验

之前的项目用的是validation-api里面的,如果用自定义注解的话,在自定义注解上加上校验类
这个API注解的链接:API注解

注解

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {IsDateTimeValidator.class}) // 标明由哪个类执行校验逻辑
public @interface IsDateTime {
}

校验类

public class IsDateTimeValidator implements ConstraintValidator<IsDateTime, String> {}

记录操作日志√

下面都是用的是切面+自定义注解实现的

自定义日志注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAround {
    String value() default "";
}

切面

@Component
@Aspect
public class LogAroundAspect {

    @Around(value = "anyMethod() && @annotation(logAround)")
    public Object logAround(ProceedingJoinPoint jp, LogAround logAround) throws Throwable {

        Logger log = LoggerFactory.getLogger(jp.getTarget().getClass());
        String methodName = jp.getSignature().getName();
        String value = logAround.value();

        if (log.isInfoEnabled()) {
            log.info("进入方法:" + value + "----" + methodName);
        }

        long start = System.currentTimeMillis();

        Object o = jp.proceed();

        long end = System.currentTimeMillis();

        if (log.isInfoEnabled()) {
            log.info("方法结束:" + value + "----" + methodName + ", 用时:" + (end - start) + "毫秒");
        }

        return o;
    }

    /**
     * 第一个*表示要拦截的目标方法返回值任意(也可以明确指定返回值类型)
     * 第二个*表示包中或者子包的任意类(也可以明确指定类)
     * 第三个*表示类中的任意方法
     * 最后面的两个点表示方法参数任意,个数任意,类型任意
     */
    @Pointcut("execution(* com.bluewind.shorturl..*.*(..))")
    public void anyMethod() {

    }

    @Pointcut("execution(public * *(..))")
    public void anyPublicMethod() {

    }

}

运行
在这里插入图片描述
在这里插入图片描述

事务管理

自定义注解(其实这部分相当于@Transacation了)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTransactional {

}


切面

@Aspect
@Component
public class MyTransactionAspect {

    @Autowired
    private MyTransactionalUtil transactionalUtil;

    @Around(value = "@annotation(com.guqueyue.myTransactional.annotation.MyTransactional)") 
    public Object around(ProceedingJoinPoint joinPoint) {

		Object result = null;
        TransactionStatus begin = null;
        try {
            // 开启事务
            begin = transactionalUtil.begin();

            // 执行目标方法
            result = joinPoint.proceed();

            // 提交事务
            transactionalUtil.commit(begin);

        } catch (Throwable e) {
            transactionalUtil.rollback(begin);
            log.info("事务回滚...");
            e.printStackTrace();
        }

        return result;
    }
}


缓存管理

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheData {

    /**
     * 自定义缓存key前缀
     * 默认:方法名
     */
    String keyPrefix() default "";

    /**
     * 缓存过期时间
     * 单位默认:秒,不设置默认10分钟
     */
    int expireTime() default 600;

    /**
     * 缓存过期-随机数
     * 单位默认:秒,默认0分钟
     * 注:设置后实际过期时间,
     * 会在expireTime基础上继续累积(0~randomExpire)之间的秒数,防止缓存大量失效大面积穿透,造成雪崩
     */
    int randomExpire() default 0;

    /**
     * 是否存储为null 的返回
     * 注:防止缓存穿透,默认true,建议查询为空时,也进行缓存
     */
    boolean storageNullFlag() default true;
}

但是吧,我觉得一个方法可能会被很多地方调用,但是又不是所有调用方都需要缓存

数据源切换

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DataSource {
 
    String dataSourceName() default DataSourceNames.MYSQLDB1;
 
}

再加上定义数据源常量和切面实现动态切换数据库
但是吧,不知道什么个应用场景能用上它

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值