Java——注解(Annotation)
1. 简介
官方解释:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。
注解的定义:
通俗的来讲,注解就如同标签。一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。
注解的本质就是一个继承了 Annotation 接口的接口,下面是注解 @Override 的定义,其实它本质上就是:
public interface Override extends Annotation{
}
解析一个类或者方法的注解一般有两种方式:
- 编译期直接的扫描
- 运行期反射
如上面所说的注解@Override,一旦编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的方法签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。
Java中内置的几个注解:
常见的作用:
- 生成说明文档,@see @param @return 等
- 编译时检查格式,比如说@Override,放在方法前,编译时会检查格式
- 替代文件的配置功能,比如Spring中的基于注解配置
- 反射中,Class, Method, Field 等函数有许多注解相关接口,通过反射拿到Class, Method, Field类,就能够通过getAnnotation(Class)拿到我们想要的注解并取值。
2. 创建一个注解
一般通过 @interface 关键字来定义,比如说创建一个Annotation_1注解
public @interface Annotation_1 {
}
如何使用它呢?
@Annotation_1
public class Test {
}
3. 元注解
元注解可以理解为是用于修饰注解的注解,一般用在定义注解上,JAVA 中有以下几个元注解:
- @Target:注解的作用目标(指明用于修饰方法还是修饰类),以下是常用取值
- @Retention:注解的生命周期,以下是常用取值
- @Documented:注解是否应当被包含在 JavaDoc 文档中
- @Inherited:是否允许子类继承该注解,父类被注解,子类会继承父类的注解
@Repeatable:java1.8中引入,是可重复的意思,注解的值可取多个
举个例子:@interface Persons {
Person[] value();
}
//@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。
@Repeatable(Persons.class)
@interface Person{String role default "";
}
@Person(role=”artist”)
@Person(role=”coder”)
@Person(role=”PM”)
public class SuperMan{
}
容器注解:
@interface Persons {
Person[] value();
}
相当于这个注解里存放了好几个不一样的注解,一个类或方法只要被这个容器注解给注解了,就相当于被好几个不一样的注解给注解了
举个例子:以下就是@Override 注解的定义
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
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() +"]");
}
}
}
}
运行结果
字段:[username],描述:[用户名],长度[12]
5.应用:实现登录校验
使用自定义注解+拦截器来实现登录校验
使用springboot拦截器实现这样一个功能,如果方法上加了@LoginRequired,则提示用户该接口需要登录才能访问,否则不需要登录。
(1)首先定义一个LoginRequired注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}
(2)然后写两个简单的接口,访问sourceA,sourceB资源
@RestController
public class IndexController {
@GetMapping("/sourceA")
public String sourceA(){
return "你正在访问sourceA资源";
}
@GetMapping("/sourceB")
public String sourceB(){
return "你正在访问sourceB资源";
}
}
(3)没添加拦截器之前成功访问
(4)实现spring的HandlerInterceptor 类先实现拦截器,但不拦截,只是简单打印日志,如下:
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 {
}
}
(5)实现spring类WebMvcConfigurer,创建配置类把拦截器添加到拦截器链中
@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**");
}
}
(6)在sourceB方法上添加我们的登录注解@LoginRequired
@RestController
public class IndexController {
@GetMapping("/sourceA")
public String sourceA(){
return "你正在访问sourceA资源";
}
@LoginRequired
@GetMapping("/sourceB")
public String sourceB(){
return "你正在访问sourceB资源";
}
}
(7)简单实现登录拦截逻辑
@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;
}
运行成功,访问sourceB时需要登录了,访问sourceA则不用登录.
还没有评论,来说两句吧...