春のカスタムアノテーションゲームプレイ、エントリーから...

作成者:リンクに表示されます:https : //juejin.im/post/5cf376e16fb9a07eee5eb6eb

ビジネス開発プロセスでは、あらゆる種類の注釈に遭遇しますが、フレームワーク自体の注釈は、常に複雑なビジネスニーズを満たすことができるとは限りません。

アノテーションの場所に応じて、記事はフィールドアノテーション、メソッド、クラスアノテーションに分割され、カスタムアノテーションが導入されます。

フィールドの注釈

フィールド注釈は、一般的にフィールドが要件を満たしているかどうかを検証するために使用されます。hibernate-validate依存関係のような、検証注釈の多くを提供し@NotNull@Rangeなど、これらのアノテーションは、すべてのビジネスシナリオを満たすことができません。

たとえば、着信パラメーターを指定されたStringセットに含める場合、既存の注釈はニーズを満たすことができず、自分で実装する必要があります。

カスタム注釈

@Check注釈を@interface宣言して、注釈を定義します

@Target({ ElementType.FIELD}) //只允许用在类的字段上
@Retention(RetentionPolicy.RUNTIME) //注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解
@Constraint(validatedBy = ParamConstraintValidated.class)
public @interface Check {
    /**
     * 合法的参数值
     * */
    String[] paramValues();

    /**
     * 提示信息
     * */
    String message() default "参数不为指定值";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

@Targetは、注釈がそれらの要素の前に宣言できることを示すために注釈が使用される場所を定義します。

ElementType.TYPE:アノテーションはクラスの前でのみ宣言できることを示します。

ElementType.FIELD:アノテーションがクラスのフィールドの前でのみ宣言できることを示します。

ElementType.METHOD:このアノテーションは、クラスのメソッドの前にのみ宣言できることを示します。

ElementType.PARAMETER:これは、アノテーションがメソッドパラメータの前にのみ宣言できることを意味します。

ElementType.CONSTRUCTOR:これは、アノテーションがクラスの構築メソッドの前にのみ宣言できることを意味します。

ElementType.LOCAL_VARIABLE:これは、アノテーションがローカル変数の前にのみ宣言できることを意味します。

ElementType.ANNOTATION_TYPE:これは、注釈が1つの注釈タイプの前にのみ宣言できることを意味します。

ElementType.PACKAGE:アノテーションはパッケージ名の前にのみ宣言できることを意味します

@Constraintは、validatedByを使用して、アノテーションに関連付けられたバリデーターを指定します

@Retentionは、アノテーションクラスのライフサイクルを記述するために使用されます。

RetentionPolicy.SOURCE:注釈はソースファイルにのみ保持されます

RetentionPolicy.CLASS :注釈はクラスファイルに保持され、JVM仮想マシンにロードされると破棄されます

RetentionPolicy.RUNTIME:アノテーションはプログラムの実行中も保持されますが、現時点では、特定のクラスで定義されているすべてのアノテーションをリフレクションによって取得できます。

バリデータークラス

バリデータークラスはConstraintValidatorジェネリックインターフェースを実装する必要があります

public class ParamConstraintValidated implements ConstraintValidator<Check, Object> {
    /**
     * 合法的参数值,从注解中获取
     * */
    private List<String> paramValues;

    @Override
    public void initialize(Check constraintAnnotation) {
        //初始化时获取注解上的值
        paramValues = Arrays.asList(constraintAnnotation.paramValues());
    }

    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        if (paramValues.contains(o)) {
            return true;
        }
}

最初のジェネリックパラメータタイプCheck:アノテーション、2番目のジェネリックパラメータObject:チェックフィールドタイプ。実現する必要がinitializeあり、isValidメソッド、isValidメソッドは検証ロジック、initializeメソッドの初期化作業

使い方

エンティティークラスを定義する

@Data
public class User {
    /**
     * 姓名
     * */
    private String name;

    /**
     * 性别 man or women
     * */
    @Check(paramValues = {"man", "woman"})
    private String sex;
}

sexチェックフィールドを、その値がなければなりませんwomanman

テスト

@RestController("/api/test")
public class TestController {
    @PostMapping
    public Object test(@Validated @RequestBody User user) {
        return "hello world";
    }
}

Userオブジェクトに@Validated注釈を追加する必要あることに注意してくださいここで@Valid注釈を使用することもできます。@ Validatedと@Validの違いについては、この提案を参照してください。

メソッド、クラスアノテーション

データベースからの最初のルックアップデータがルックアップしない場合、ルックアップから、ルックアップから、このクラスのメソッドまたは特定のメソッドにアクセスできるのは、許可されたユーザーのみがアクセスできるため、このような要求の開発プロセスで発生guava cacheredisます。 、そして最後にmysql(マルチレベルキャッシュ)を見つけます

現時点では、アノテーションをカスタマイズしてこの要件を完了することができます。最初のシナリオは権限検証アノテーションを定義することであり、2番目のシナリオはspring-data-redisパッケージの下に同様の@Cacheableアノテーションを定義することです。

許可アノテーション

カスタム注釈

@Target({ ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionCheck {
    /**
     * 资源key
     * */
    String resourceKey();
}

アノテーションのスコープはクラスまたはメソッドにあります

インターセプタークラス

public class PermissionCheckInterceptor extends HandlerInterceptorAdapter {
    /**
     * 处理器处理之前调用
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        PermissionCheck permission = findPermissionCheck(handlerMethod);

        //如果没有添加权限注解则直接跳过允许访问
        if (permission == null) {
            return true;
        }

        //获取注解中的值
        String resourceKey = permission.resourceKey();

        //TODO 权限校验一般需要获取用户信息,通过查询数据库进行权限校验
        //TODO 这里只进行简单演示,如果resourceKey为testKey则校验通过,否则不通过
        if ("testKey".equals(resourceKey)) {
            return true;
        }

        return false;
    }

    /**
     * 根据handlerMethod返回注解信息
     *
     * @param handlerMethod 方法对象
     * @return PermissionCheck注解
     */
    private PermissionCheck findPermissionCheck(HandlerMethod handlerMethod) {
        //在方法上寻找注解
        PermissionCheck permission = handlerMethod.getMethodAnnotation(PermissionCheck.class);
        if (permission == null) {
            //在类上寻找注解
            permission = handlerMethod.getBeanType().getAnnotation(PermissionCheck.class);
        }

        return permission;
    }
}

権限の検証のロジックは、権限があればアクセスでき、権限がないとアクセスできないということです。本質はインターセプターです。最初に注釈を取得し、次に検証のために注釈のフィールドを取得する必要があります。検証が渡され、戻り値が渡されますtrue。それ以外の場合は返されます。false

テスト

 @GetMapping("/api/test")
 @PermissionCheck(resourceKey = "test")
 public Object testPermissionCheck() {
     return "hello world";
 }

このメソッドには権限の確認が必要なため、PermissionCheck注釈が追加されます。

アノテーションのキャッシュ

カスタム注釈

@Target({ ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomCache {
    /**
     * 缓存的key值
     * */
    String key();
}

アノテーションはメソッドまたはクラスで使用できますが、キャッシュアノテーションは通常、メソッドで使用されます。公開番号のJavaテクノロジースタックに注目し、バックグラウンドでspringに返信して、私がコンパイルした最も完全なSpringチュートリアルを取得してください。

セクション

@Aspect
@Component
public class CustomCacheAspect {
    /**
     * 在方法执行之前对注解进行处理
     *
     * @param pjd
     * @param customCache 注解
     * @return 返回中的值
     * */
    @Around("@annotation(com.cqupt.annotation.CustomCache) && @annotation(customCache)")
    public Object dealProcess(ProceedingJoinPoint pjd, CustomCache customCache) {
        Object result = null;

        if (customCache.key() == null) {
            //TODO throw error
        }

        //TODO 业务场景会比这个复杂的多,会涉及参数的解析如key可能是#{id}这些,数据查询
        //TODO 这里做简单演示,如果key为testKey则返回hello world
        if ("testKey".equals(customCache.key())) {
            return "hello word";
        }

        //执行目标方法
        try {
            result = pjd.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return result;
    }
}

キャッシュされたアノテーションはメソッドが実行される前に戻り値を持つ必要があるため、アノテーションはインターセプターによって処理されませんが、アスペクトを使用してメソッドが実行される前にアノテーションは処理されます。

アノテーションに戻り値がない場合、メソッドで値を返します

テスト

@GetMapping("/api/cache")
@CustomCache(key = "test")
public Object testCustomCache() {
    return "don't hit cache";
}

私のブログでもっと読むことをお勧めします:

1. Java JVM、コレクション、マルチスレッド、および新機能に関する一連のチュートリアル

2. Spring MVC、Spring Boot、Spring Cloudシリーズのチュートリアル

3. Maven、Git、Eclipse、Intellij IDEAシリーズツールのチュートリアル

4. Java、バックエンド、アーキテクチャー、アリババなどの主要メーカーから最新のインタビューの質問

気分がいいです。いいね+進むを忘れないでください!

最後に、スタックリーダーのWeChat公式アカウントに注意してください。Javaテクノロジースタック、返信:福祉、2020年に向けて私がまとめた最新のJavaインタビューの質問の無料コピーを入手できます。これは、ルーチンなしで本当に完全です(回答を含む)。

おすすめ

転載: blog.csdn.net/youanyyou/article/details/108397147