Пользовательские аннотации 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.
Следовательно, пока мы получаем классы 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资源";
}
}
Успешный доступ до добавления перехватчика
Класс 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
Сценарий приложения 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, если вы готовы попасться на удочку, хахаха.