1 【Android高级开发必备技能】注解

1 【Android高级开发必备技能】注解_第1张图片

Java 注解是JDK5.0引入的注释机制,可以被使用在类,方法,参数等地方中,并且可以通过Java的反射机制获取注解中的内容,注解相当于标签,可以标识方法,类或属性具有某些特征,在编译器生成的类文件时,可以被嵌入到字节码中。另外用户可以自定义注解,完成定制化的开发,尤其是在利用springboot进行项目开发时,我们会经常使用注解管理spring容器的bean,从而大大提高了开发的效率。

1.1 基本语法

@Target( ElementType.FIELD, ElementType.Method )
@Retention( RetentionPolicy.RUNTIME )
@Inherited
@Documented
public @interface MyField {
	String description();


	String author();


	int age() default 18;
}
public class MyFieldTest {
	/* 使用我们的自定义注解 */
	@MyField( description = "自定义注解", author = "yutian" )
	private String username;
}

1.2 元注解

元注解

说明

参数取值

@Target

表示该注解可以用在什么地方

ElementType参数

@Retention

表示要在什么级别保存该注解信息

RetentionPolicy参数

@Documented

将此注解包含在Javadoc中

无参数

@Inherited

允许子类继承父类中的注解

无参数

package java.lang.annotation;

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */

    FIELD,              /* 字段声明(包括枚举常量)  */

    METHOD,             /* 方法声明  */

    PARAMETER,          /* 参数声明  */

    CONSTRUCTOR,        /* 构造方法声明  */

    LOCAL_VARIABLE,     /* 局部变量声明  */

    ANNOTATION_TYPE,    /* 注释类型声明  */

    PACKAGE             /* 包声明  */
}


package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,   /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */

    CLASS,    /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */

    RUNTIME   /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}

1.3 注解处理器

在上面主要介绍了注解和自定义注解相关的知识点。对于注解,如果没有通过注解处理器来对注解进行处理,那么注解的作用和注释就没有什么区别了。所以这里主要总结两种常见的实现java注解处理器的方式:

  • 通过反射实现的运行时注解处理器
  • 通过apt工具实现的编译时注解处理器

1.4 编译时注解处理器(Class范围)

APT(Annotation Process Tool),**是一种在代码编译时处理注解,注解作用范围在Class,**按照一定的规则,生成相应的java文件,多用于对自定义注解的处理。

1.5 运行时注解处理器(RUNTIME范围)

只有定义为RetentionPolicy.RUNTIME时,我们才能通过注解反射获取到注解(最常用的特性)。

Spring框架中注解大量运用运行时注解处理器

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
    int value();
}


public class InjectUtils {
    public static void inject(Activity activity) {
        //1、 获取指定类上申明的所有字段
        Field[] declaredFields = activity.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            // 2、获取每次字段中包含BindView注解的字段。
            BindView annotation = field.getAnnotation(BindView.class);
			//多个注解时可使用此方法获取所有注解
            //Annotation[] anns = field.getDeclaredAnnotations();
            
            if (annotation != null) {
                // 3、通过获取注解指定的value,初始化View
                int value = annotation.value();
                View view = activity.findViewById(value);
                try {
                    field.set(activity, view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

示例

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
    boolean isRequired() default true;
}


@RestController
public class IndexController {

    @GetMapping("/sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }
    
    @LoginRequired
    @GetMapping("/sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}


public class SourceAccessInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(...,Object handler) throws Exception {
        System.out.println("进入拦截器了");
        
          // 反射获取方法上的LoginRequred注解
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
        
        if(loginRequired != null && loginRequired.isRequired()){
        //需要登录,提示用户登录
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().print("你访问的资源需要登录");
        return false;
        }
        return true;
    }

    @Override
    public void postHandle(...) throws Exception {

    }

    @Override
    public void afterCompletion(...) throws Exception {

    }
}

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelUpdate {
}


// 1.表明这是一个切面类
@Aspect
@Component
public class MyLogAspect {

  // 2. PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名
  // 切面最主要的就是切点,所有的故事都围绕切点发生
  // logPointCut()代表切点名称
  @Pointcut("@annotation(com.easycode.mmall.annotation.ModelUpdate)")
  public void logPointCut(){};

  // 3. 前置通知
  @Before("logPointCut()")
  public void logAround(JoinPoint joinPoint){
    // 获取方法名称
    String methodName = joinPoint.getSignature().getName();
    // 获取入参
    Object[] param = joinPoint.getArgs();

    StringBuilder sb = new StringBuilder();
    for(Object arg : param){
      sb.append(arg + "; ");
    }
    System.out.println("进入[" + methodName + "]方法,参数为:" + sb.toString());
  }
}

总结

  • 对于注解,如果没有通过注解处理器来对注解进行处理,那么注解的作用和注释就没有什么区别了
  • 运行时注解处理器在平时使用时更多
  • 运行时注解处理器核心思想是通过反射获取到注解,然后进行相应的处理

你可能感兴趣的:(Android高级面试必备,android高级面试必备,注解)