Пользовательские аннотации Java и сценарии использования

Java

Пользовательские аннотации Java обычно используются в следующих сценариях: пользовательские аннотации + перехватчики или АОП, использование пользовательских аннотаций для самостоятельного проектирования фреймворка, что делает код очень элегантным. Эта статья начнется с основных концепций пользовательских аннотаций, а затем начнется настоящая битва, написание небольших фрагментов кода для реализации пользовательских аннотаций + перехватчиков, пользовательских аннотаций + АОП.

1. Что такое аннотация

Что такое аннотации Java, следующее цитируется из Википедии

Аннотации Java, также известные как аннотации Java, представляют собой метаданные специального синтаксиса, которые JDK5.0 поддерживает добавление в исходный код.
Классы, методы, переменные, параметры и пакеты в языке Java могут быть аннотированы. В отличие от Javadoc, аннотации Java могут получать содержимое аннотации посредством отражения. Когда компилятор создает файлы классов, аннотации могут быть встроены в байт-код. Виртуальная машина Java может сохранять содержимое аннотаций и получать их во время выполнения. Конечно, он также поддерживает пользовательские аннотации Java.

2. Схема системы аннотаций

Мета-аннотации:java.lang.annotationМета-аннотации представлены в , которые можно использовать для определения ваших собственных аннотаций. В основном используйте аннотации Target и Retention

元注解
Класс обработки аннотаций: поскольку аннотации определены выше, должен быть способ получить определенные нами аннотации.java.lang.reflect.AnnotationElementИнтерфейсы обеспечивают эту функциональность. Обработка аннотаций осуществляется посредством отражения Java. Как показано ниже, классы Class, Method и Field, связанные с отражением, реализуют интерфейс AnnotationElement.
反射处理
AnnotationElement接口方法
Следовательно, пока мы получаем классы Class, Method и Field посредством отражения, мы можем получить желаемую аннотацию и получить значение через getAnnotation(Class).

3. Общие мета-аннотации

Target: описывает область объекта, измененного аннотацией, значение находится вjava.lang.annotation.ElementTypeОбычно используемые определения включают:

  • МЕТОД: используется для описания метода
  • ПАКЕТ: используется для описания пакета
  • ПАРАМЕТР: используется для описания переменных метода.
  • TYPE: используется для описания класса, интерфейса или типа перечисления.

Retention: указывает продолжительность хранения аннотации. стоимость вjava.lang.annotation.RetentionPolicy, значение:

  • ИСТОЧНИК: Действителен в исходных файлах, игнорируется при компиляции
  • CLASS: скомпилирован с исходным файлом в файле класса, игнорируется во время выполнения
  • ВРЕМЯ ВЫПОЛНЕНИЯ: Действительно во время выполнения

определяется только какRetentionPolicy.RUNTIME, мы можем получить аннотации через отражение аннотаций. Итак, предположим, мы хотим настроить аннотацию, которая используется в поле и может быть получена посредством отражения, Функция состоит в том, чтобы описать длину и функцию поля. Его можно определить следующим образом, см. кодмой GitHub.

@Target(ElementType.FIELD)  //  注解用于字段上
@Retention(RetentionPolicy.RUNTIME)  // 保留到运行时,可通过注解获取
public @interface MyField {
    String description();
    int length();
}
4. Пример — отражение для получения аннотаций

Сначала определите аннотацию:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {
    String description();
    int length();
}

Получить аннотации через отражение

public class MyFieldTest {

    //使用我们的自定义注解
    @MyField(description = "用户名", length = 12)
    private String username;

    @Test
    public void testMyField(){

        // 获取类模板
        Class c = MyFieldTest.class;

        // 获取所有字段
        for(Field f : c.getDeclaredFields()){
            // 判断这个字段是否有MyField注解
            if(f.isAnnotationPresent(MyField.class)){
                MyField annotation = f.getAnnotation(MyField.class);
                System.out.println("字段:[" + f.getName() + "], 描述:[" + annotation.description() + "], 长度:[" + annotation.length() +"]");
            }
        }

    }
}

результат операции

运行结果

Сценарий приложения 1: пользовательская аннотация + перехватчик для проверки входа

Далее мы используем перехватчик springboot для реализации такой функции.Если в метод добавлен @LoginRequired, пользователю предлагается, что интерфейс должен быть авторизован для доступа, в противном случае вход не требуется. Сначала определите аннотацию LoginRequired

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

Затем напишите два простых интерфейса для доступа к ресурсам sourceA, sourceB.

@RestController
public class IndexController {

    @GetMapping("/sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }

    @GetMapping("/sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}

Успешный доступ до добавления перехватчика

sourceB
Класс HandlerivereClector, который реализует впервые впервые реализует перехватчик, но не перехват, но просто печатает журнал следующим образом:

public class SourceAccessInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进入拦截器了");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

Реализуйте класс Spring WebMvcConfigurer, создайте класс конфигурации, чтобы добавить перехватчик в цепочку перехватчиков.

@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**");
    }
}

Перехват успешен следующим образом

拦截了
Добавьте нашу аннотацию для входа @LoginRequired в метод sourceB.

@RestController
public class IndexController {

    @GetMapping("/sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }

    @LoginRequired
    @GetMapping("/sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}

Простая реализация логики перехвата логина

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进入拦截器了");

        // 反射获取方法上的LoginRequred注解
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
        if(loginRequired == null){
            return true;
        }

        // 有LoginRequired注解说明需要登录,提示用户登录
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().print("你访问的资源需要登录");
        return false;
    }

Операция выполнена успешно. Вам необходимо войти в систему, чтобы получить доступ к источнику B, но вам не нужно входить в систему, чтобы получить доступ к источнику A. Полный код см.мой GitHub

sourceA
sourceB

Сценарий приложения 2: пользовательская аннотация + АОП для реализации печати журнала

Сначала импортируйте зависимости, требуемые аспектом

<dependency>
      <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Определить аннотацию @MyLog

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

Определите класс аспекта, см. следующие комментарии к коду для понимания:

@Aspect // 1.表明这是一个切面类
@Component
public class MyLogAspect {

    // 2. PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名
    // 切面最主要的就是切点,所有的故事都围绕切点发生
    // logPointCut()代表切点名称
    @Pointcut("@annotation(me.zebin.demo.annotationdemo.aoplog.MyLog)")
    public void logPointCut(){};

    // 3. 环绕通知
    @Around("logPointCut()")
    public void logAround(ProceedingJoinPoint joinPoint){
        // 获取方法名称
        String methodName = joinPoint.getSignature().getName();
        // 获取入参
        Object[] param = joinPoint.getArgs();

        StringBuilder sb = new StringBuilder();
        for(Object o : param){
            sb.append(o + "; ");
        }
        System.out.println("进入[" + methodName + "]方法,参数为:" + sb.toString());

        // 继续执行方法
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(methodName + "方法执行结束");

    }
}

В IndexController на шаге 2 напишите sourceC для тестирования, а также нашу пользовательскую аннотацию:

    @MyLog
    @GetMapping("/sourceC/{source_name}")
    public String sourceC(@PathVariable("source_name") String sourceName){
        return "你正在访问sourceC资源";
    }

Запустите веб-проект springboot и введите адрес доступа

访问项目
切面成功

Друзья, которым нужна помощь или которым нравятся мои статьи, могут обратить внимание на мой публичный аккаунт «Donggua Donggua» и найти себя в WeChat, если вы готовы попасться на удочку, хахаха.