1-优质博客
2. 注解概述
- Annotation是从JDK5.0开始引入的新技术,注解有检查和约束的作用
- Annotation的作用:
- 不是程序本身,可以对程序作出解释。(这一点和注释(comment)没什么区别)
- 可以被其他程序(比如:编译器等)读取
- Annotation的格式:
- 注解是以”@注释名”在代码中存在的,还可以添加一些参数值,例如:@Suppress Warnings(value=”unchecked”).
- Annotation在哪里使用?
注解名称 | 作用 | 值 |
---|---|---|
@Target | 用于描述注解的使用范围(即:被描述的注解可以用在什么地方 | ElementType.TYPE:类、接口、注解、枚举 ElementType.FIELD:字段、枚举常量 ElementType.METHOD:方法 ElementType.PARAMETER:形式参数 ElementType.CONSTRUCTOR:构造方法 ElementType.LOCAL_VARIABLE:局部变量 ElementType.ANNOTATION_TYPE:注解 ElementType.PACKAGE:包 ElementType.TYPE_PARAMETER:类型参数 ElementType.TYPE_USE:类型使用 |
@Retention | 表示需要在什么级别保存该注释信息,用于描述注解的生命周期 | RetentionPolicy.SOURCE:注解只保留在源码中,在编译时会被编译器丢弃 RetentionPolicy.CLASS:(默认的保留策略) 注解会被保留在Class文件中,但不 会被加载到虚拟机中,运行时无法获得 RetentionPolicy.RUNTIME:注解会被保留在Class文件中,且会被加载到虚拟机中,可以在运行时获得 [ |
](https://blog.csdn.net/Goodbye_Youth/article/details/84314928) |
| @Documented | 说明该注解将被包含在javadoc中 | 用于将注解包含在javadoc中
默认情况下,javadoc是不包括注解的,但如果使用了@Documented注解,则相关注解类型信息会被包含在生成的文档中 |
| @Inherited | 说明子类可以继承父类中的该注解 |
|
| @Repeatable | 用于声明标记的注解为可重复类型注解,可以在同一个地方多次使用 |
|
4. 内置注解
- @Override:定义在java.lang.Override中,此注解只适用于修辞方法,表示一个方法声明打算重写超类中的另一种方法声明
- @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择
@SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。
- 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性使用就好了。
- @SuppressWarnings(“all”)
- @SuppressWarnings(“unchecked”)
- @SuppressWarnings(“value={“unchecked”,”deprecation”}”)
- 等等
- 平时写代码不建议镇压所有警告,因为警告可以帮我们识别错误
5. 自定义注解
使用@interface定义注解时,自动继承了java.lang.annotation.Annotation接口,
一般使用场景为:自定义注解+拦截器或者AOP,使用自定义注解来自己设计框架,使得代码看起来非常优雅。5.1 特性:
@interface用来声明一个注解,格式:public @interface 注解名{定义内容}
- 其中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能是基本类型Class,String,enum)
- 可以通过default声明参数的默认值,如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
注解只有一个值,我们可以定义为Value,使用时Value可以省略不写,只有Value可以省略
5.2 使用(自定义注解+拦截器或者AOP):
@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() +"]");
}
}
}
}
5.2.1 应用场景一:自定义注解+拦截器 实现登录校验
自定义注解@LoginRequired,提示用户该接口需要登录才能访问,否则不需要登录。 ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LoginRequired {
}
```java
@RestController
public class IndexController {
@GetMapping("/sourceA")
public String sourceA(){
return "你正在访问sourceA资源";
}
@GetMapping("/sourceB")
public String sourceB(){
return "你正在访问sourceB资源";
}
}
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 {
}
}
@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**");
}
}
@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;
}
5.2.2 应用场景二:自定义注解+AOP 实现日志打印
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@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 + "方法执行结束");
}
}
@MyLog
@GetMapping("/sourceC/{source_name}")
public String sourceC(@PathVariable("source_name") String sourceName){
return "你正在访问sourceC资源";
}