一、概念
Java注解是一种元数据形式,可以被添加到Java代码中的各种元素(类、方法、字段等)上,以提供关于这些元素的额外信息。注解是在Java 5中引入的一项特性,它们不直接影响代码的执行,而是提供了一种机制来对代码进行标记和解释。
通常以@符号开头,放到注解目标的前面。Java提供了一些内置的注解。
@Override:该注解代表重写父类的方法
@Deprecated:使用@Deprecate的方法会出现一条删除线,表示该方法过时了,但是不影响使用
@SuppressWarning:指示编译器去忽略注解中声明的警告
@Retention:标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问
@Documented:标记这些注解是否包含在用户文档中
@Target:标记这个注解应该是哪种 Java 成员。
@Inherited:标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
从 Java 7 开始,额外添加了 3 个注解:
@SafeVarargs:忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告
@FunctionalInterface:Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable:Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
二、注解的作用
1. 提供元数据信息
注解为代码元素提供了额外的元数据信息。这些信息可以用于在编译时或运行时进行处理,以实现各种功能。例如,@Override
注解用于标记方法覆盖父类的行为,这在编译时会进行检查。
2.编译时检查
使用注解可以在编译时进行静态检查,从而帮助捕获一些常见的错误。例如,使用@Deprecated
注解标记已过时的方法或类可以在编译时产生警告,提醒开发人员使用更合适的替代方案。
3.自动生成代码
注解可以用于生成一些重复性的代码,从而减少开发人员的工作量。通过编写自定义的注解处理器,可以在编译时根据注解信息生成额外的代码。这在一些框架和库中被广泛使用,以提供更便捷的开发方式。
4. 运行时处理
通过使用反射机制,可以在运行时处理注解信息。这样可以实现一些动态的行为,例如根据注解信息加载特定的配置文件或执行特定的逻辑。
三、自定义注解
创建自定义的注解,我们需要使用到下面几个注解,这些作用于注解上的注解我们称为元注解。
1.Target注解
标记这个注解应该是哪种 Java 成员使用。
TYPE:类、接口(包括注释类型)或枚举声明
FIELD:字段声明(包括枚举常量)
METHOD: 方法声明
PARAMETER: 参数声明
CONSTRUCTOR: 构造方法声明
LOCAL_VARIABLE: 局部变量声明
ANNOTATION_TYPE:释类型声明
PACKAGE:包声明
2.Retention注解
标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
source:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略。
class:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期。
runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。
3.@Document注解
@Document注解用于指定被修饰的注解可以被javadoc工具提取成文档。定义注解类时使用@Document注解进行修饰,则所有使用该注解修饰的程序元素的API文档中将会包含该注解说明
4.@Inherited注解
@Inherited注解指定注解具有继承性,如果某个注解使用@Inherited进行修饰,则该类使用该注解时,其子类将自动被修饰
5.@Repeatable注解
@Repeatable注解是Java8新增的注解,用于开发重复注解。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author qinxun
* @date 2023-06-08
* @Descripion: 自定义注解
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// 定义一个名为“name”的注解元素,默认为空字符串
String name() default "";
// 定义一个名为"value"的注解元素,默认值为0
int value() default 0;
}
我们创建一个注解测试类,获取使用自定义注解的信息。
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* @author qinxun
* @date 2023-06-08
* @Descripion: 自定义注解测试
*/
@MyAnnotation(name = "class", value = 10)
public class AnnotationTest {
public static void main(String[] args) {
// 获取类上的自定义注解数据
MyAnnotation myAnnotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println("name:" + myAnnotation.name() + ",value=" + myAnnotation.value());
// 获取方法上的自定义注解数据
Class<AnnotationTest> aClass = AnnotationTest.class;
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods) {
Annotation[] annotations = method.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
MyAnnotation testAnnotation = (MyAnnotation) annotation;
System.out.println("name:" + testAnnotation.name() + ",value=" + testAnnotation.value());
}
}
}
@MyAnnotation(name = "method", value = 5)
private static void show() {
System.out.println("方法");
}
}
执行程序结果:
name:class,value=10
name:method,value=5
四、自定义注解在web里的使用
在web开发中,权限控制非常重要,所以有些接口会限制必须登录之后才能访问,但是个别接口并没有这种限制。
1.我们自定义一个注解,用于标记需要登录后才能访问。
import java.lang.annotation.*;
/**
* @author qinxun
* @date 2023-06-08
* @Descripion: 自定义注解标记访问方法需要登录
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NeedLoginAnnotation {
}
2.创建一个拦截器,用于处理我们的自定义登录注解
import com.example.quartzdemo.annotation.NeedLoginAnnotation;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author qinxun
* @date 2023-06-08
* @Descripion: 自定义登录拦截器
*/
public class LoginFilter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) {
if (obj instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) obj;
// 判断方法是否使用了自定义登录注解
NeedLoginAnnotation needLoginAnnotation = handlerMethod.getMethod().getDeclaredAnnotation(NeedLoginAnnotation.class);
if (needLoginAnnotation != null) {
System.out.println("该接口需要登录后才能访问");
// 判断是否登录的逻辑 如果是登录状态就放行..
return false;
}
} else if (obj instanceof ResourceHttpRequestHandler) {
return true;
}
// 放行
return true;
}
}
3.把我们创建的拦截器添加到web配置中
import com.example.quartzdemo.filter.LoginFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author qinxun
* @date 2023-06-08
* @Descripion: web配置
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加自定义拦截器
registry.addInterceptor(new LoginFilter())
.addPathPatterns()
.excludePathPatterns("/","/login");
}
}
4.创建控制层进行测试
import com.example.quartzdemo.annotation.NeedLoginAnnotation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author qinxun
* @date 2023-06-08
* @Descripion: 自定义注解测试
*/
@RestController
public class AnnotationController {
/**
* 新增 加上了需要登录才能访问的注解
*/
@RequestMapping("/save")
@NeedLoginAnnotation
public String toSave() {
System.out.println("新增操作");
return "新增";
}
/**
* 查询
*/
@GetMapping("/find")
public String toFind() {
System.out.println("查询操作");
return "查询";
}
}
在新增操作的时候我们标记了需要登录才能访问的注解,在查询的接口我们没有添加这个注解。
控制台返回
该接口需要登录后才能访问
这样我们就实现了标记自定义登录注解的方法,如果没有登录的情况下是不能访问的。
我们在访问查询的接口,因为查询的接口没有添加自定义的登录注解,所以拦截器是放行的,是可以访问这个方法。