Gameplay d'annotations personnalisées Spring, de l'entrée à ...

Auteur: comme c'est doux dans le lien: https://juejin.im/post/5cf376e16fb9a07eee5eb6eb

Dans le processus de développement commercial, nous rencontrerons toutes sortes d'annotations, mais les propres annotations du framework ne sont pas toujours en mesure de répondre à des besoins commerciaux complexes.Nous pouvons personnaliser les annotations pour répondre à nos besoins.

Selon l'emplacement des annotations, l'article sera divisé en annotations de champ, méthodes et annotations de classe pour introduire des annotations personnalisées.

Annotation de champ

Les annotations de champ sont généralement utilisées pour vérifier si le champ répond aux exigences. hibernate-validateDependency fournit de nombreuses annotations de vérification, telles que @NotNull, @Rangeetc., mais ces annotations ne peuvent pas répondre à tous les scénarios d'entreprise.

Par exemple, si nous voulons que les paramètres entrants soient dans l' Stringensemble spécifié , les annotations existantes ne peuvent pas répondre aux besoins et doivent être implémentées par nous-mêmes.

Annotation personnalisée

Définir une @Checkannotation, en @interfacedéclarant une annotation

@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 définit où l'annotation est utilisée pour indiquer que l'annotation peut être déclarée avant ces éléments.

ElementType.TYPE: Indique que l'annotation ne peut être déclarée que devant une classe.

ElementType.FIELD: Indique que l'annotation ne peut être déclarée que devant un champ d'une classe.

ElementType.METHOD: Indique que cette annotation ne peut être déclarée qu'avant une méthode d'une classe.

ElementType.PARAMETER: Cela signifie que l'annotation ne peut être déclarée qu'avant un paramètre de méthode.

ElementType.CONSTRUCTOR: Cela signifie que l'annotation ne peut être déclarée qu'avant la méthode de construction d'une classe.

ElementType.LOCAL_VARIABLE: Cela signifie que l'annotation ne peut être déclarée que devant une variable locale.

ElementType.ANNOTATION_TYPE: Cela signifie que l'annotation ne peut être déclarée qu'avant un type d'annotation.

ElementType.PACKAGE: Cela signifie que l'annotation ne peut être déclarée qu'avant un nom de package

@Constraint spécifie le validateur associé à l'annotation à l'aide de validatedBy

@Retention est utilisé pour décrire le cycle de vie de la classe d'annotation.

RetentionPolicy.SOURCE: Les annotations ne sont conservées que dans le fichier source

RetentionPolicy.CLASS : Les annotations sont conservées dans le fichier de classe et ignorées lorsqu'elles sont chargées dans la machine virtuelle JVM

RetentionPolicy.RUNTIME: Les annotations sont conservées pendant l'exécution du programme, à ce stade, toutes les annotations définies sur une certaine classe peuvent être obtenues par réflexion.

Classe de validateur

La classe de validateur doit implémenter une ConstraintValidatorinterface générique

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;
        }
}

Le premier type de paramètre générique Check: annotation, le deuxième paramètre générique Object: type de champ de contrôle. Besoin de réaliser initializeet de isValidméthode, la isValidméthode est la logique de vérification, le initializetravail d'initialisation de la méthode

Comment utiliser

Définir une classe d'entité

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

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

sexVérifiez le champ, sa valeur doit être womanouman

tester

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

Notez que vous devez Userajouter des @Validatedannotations à l' objet. Vous pouvez également utiliser des @Validannotations ici . La différence entre @Validated et @Valid, consultez cette suggestion.

Méthode, annotation de classe

Rencontré dans le processus de développement d'une telle demande, car seuls les utilisateurs autorisés peuvent accéder aux méthodes de cette classe ou à une méthode spécifique, lorsque les premières données de recherche de la base de données ne regardent pas, commencez par guava cachele look, du redislook , Et enfin trouver mysql(cache multi-niveaux).

À ce stade, nous pouvons personnaliser les annotations pour répondre à cette exigence. Le premier scénario consiste à définir une annotation de vérification des autorisations, et le deuxième scénario consiste à définir une annotation spring-data-redissimilaire sous le package @Cacheable.

Annotation des autorisations

Annotation personnalisée

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

La portée de l'annotation est sur la classe ou la méthode

Classe d'intercepteur

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;
    }
}

La logique de la vérification des autorisations est que vous pouvez accéder si vous en avez l'autorisation, et l'accès n'est pas autorisé si vous ne le faites pas. L'essence est en fait un intercepteur. Nous devons d'abord obtenir l'annotation, puis obtenir le champ sur l'annotation pour vérification, la vérification est passée et le retour est passé true, sinon il est retournéfalse

tester

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

Cette méthode nécessite une vérification des autorisations, une PermissionCheckannotation est donc ajoutée .

Annotation du cache

Annotation personnalisée

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

Les annotations peuvent être utilisées sur des méthodes ou des classes, mais les annotations de mise en cache sont généralement utilisées sur des méthodes. Faites attention à la pile technologique Java des numéros publics et répondez au printemps en arrière-plan pour obtenir les didacticiels Spring les plus complets que j'ai compilés.

section

@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;
    }
}

Étant donné que l'annotation mise en cache doit avoir une valeur de retour avant que la méthode ne soit exécutée, cette annotation n'est pas traitée par l'intercepteur, mais l'annotation est traitée avant que la méthode ne soit exécutée à l'aide de l'aspect.

Si l'annotation n'a pas de valeur de retour, elle renverra la valeur dans la méthode

tester

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

Je recommande d'en lire plus sur mon blog:

1. Une série de didacticiels sur Java JVM, les collections, le multithreading et les nouvelles fonctionnalités

2. Spring MVC, Spring Boot, série de tutoriels Spring Cloud

3. Tutoriel sur les outils de la série Maven, Git, Eclipse, Intellij IDEA

4. Les dernières questions d'entretien des principaux fabricants tels que Java, le back-end, l'architecture et Alibaba

Sentez-vous bien, n'oubliez pas d'aimer + d'avancer!

Enfin, faites attention au compte officiel WeChat du chef de file de la pile: pile technologique Java, réponse: Bien-être, vous pouvez obtenir une copie gratuite des dernières questions d'entretien Java que j'ai compilées pour 2020. Il est vraiment complet (y compris les réponses), sans aucune routine.

Je suppose que tu aimes

Origine blog.csdn.net/youanyyou/article/details/108397147
conseillé
Classement