1-优质博客

2. 注解概述

  1. Annotation是从JDK5.0开始引入的新技术,注解有检查和约束的作用
  2. Annotation的作用:
    1. 不是程序本身,可以对程序作出解释。(这一点和注释(comment)没什么区别)
    2. 可以被其他程序(比如:编译器等)读取
  3. Annotation的格式:
    1. 注解是以”@注释名”在代码中存在的,还可以添加一些参数值,例如:@Suppress Warnings(value=”unchecked”).
  4. Annotation在哪里使用?
    1. 可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问

      3. 元注解

      元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他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):

    1. @Target(ElementType.FIELD)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. public @interface MyField {
    4. String description();
    5. int length();
    6. }
    1. public class MyFieldTest {
    2. //使用我们的自定义注解
    3. @MyField(description = "用户名", length = 12)
    4. private String username;
    5. @Test
    6. public void testMyField(){
    7. // 获取类模板
    8. Class c = MyFieldTest.class;
    9. // 获取所有字段
    10. for(Field f : c.getDeclaredFields()){
    11. // 判断这个字段是否有MyField注解
    12. if(f.isAnnotationPresent(MyField.class)){
    13. MyField annotation = f.getAnnotation(MyField.class);
    14. System.out.println("字段:[" + f.getName() + "], 描述:[" + annotation.description() + "], 长度:[" + annotation.length() +"]");
    15. }
    16. }
    17. }
    18. }

    5.2.1 应用场景一:自定义注解+拦截器 实现登录校验

    自定义注解@LoginRequired,提示用户该接口需要登录才能访问,否则不需要登录。 ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LoginRequired {

}

  1. ```java
  2. @RestController
  3. public class IndexController {
  4. @GetMapping("/sourceA")
  5. public String sourceA(){
  6. return "你正在访问sourceA资源";
  7. }
  8. @GetMapping("/sourceB")
  9. public String sourceB(){
  10. return "你正在访问sourceB资源";
  11. }
  12. }
  1. public class SourceAccessInterceptor implements HandlerInterceptor {
  2. @Override
  3. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  4. System.out.println("进入拦截器了");
  5. return true;
  6. }
  7. @Override
  8. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  9. }
  10. @Override
  11. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  12. }
  13. }
  1. @Configuration
  2. public class InterceptorTrainConfigurer implements WebMvcConfigurer {
  3. @Override
  4. public void addInterceptors(InterceptorRegistry registry) {
  5. registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**");
  6. }
  7. }
  1. @RestController
  2. public class IndexController {
  3. @GetMapping("/sourceA")
  4. public String sourceA(){
  5. return "你正在访问sourceA资源";
  6. }
  7. @LoginRequired
  8. @GetMapping("/sourceB")
  9. public String sourceB(){
  10. return "你正在访问sourceB资源";
  11. }
  12. }
  1. @Override
  2. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  3. System.out.println("进入拦截器了");
  4. // 反射获取方法上的LoginRequred注解
  5. HandlerMethod handlerMethod = (HandlerMethod)handler;
  6. LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
  7. if(loginRequired == null){
  8. return true;
  9. }
  10. // 有LoginRequired注解说明需要登录,提示用户登录
  11. response.setContentType("application/json; charset=utf-8");
  12. response.getWriter().print("你访问的资源需要登录");
  13. return false;
  14. }

image.png

5.2.2 应用场景二:自定义注解+AOP 实现日志打印

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-aop</artifactId>
  4. </dependency>
  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface MyLog {
  4. }
  1. @Aspect // 1.表明这是一个切面类
  2. @Component
  3. public class MyLogAspect {
  4. // 2. PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名
  5. // 切面最主要的就是切点,所有的故事都围绕切点发生
  6. // logPointCut()代表切点名称
  7. @Pointcut("@annotation(me.zebin.demo.annotationdemo.aoplog.MyLog)")
  8. public void logPointCut(){};
  9. // 3. 环绕通知
  10. @Around("logPointCut()")
  11. public void logAround(ProceedingJoinPoint joinPoint){
  12. // 获取方法名称
  13. String methodName = joinPoint.getSignature().getName();
  14. // 获取入参
  15. Object[] param = joinPoint.getArgs();
  16. StringBuilder sb = new StringBuilder();
  17. for(Object o : param){
  18. sb.append(o + "; ");
  19. }
  20. System.out.println("进入[" + methodName + "]方法,参数为:" + sb.toString());
  21. // 继续执行方法
  22. try {
  23. joinPoint.proceed();
  24. } catch (Throwable throwable) {
  25. throwable.printStackTrace();
  26. }
  27. System.out.println(methodName + "方法执行结束");
  28. }
  29. }
  1. @MyLog
  2. @GetMapping("/sourceC/{source_name}")
  3. public String sourceC(@PathVariable("source_name") String sourceName){
  4. return "你正在访问sourceC资源";
  5. }

image.png