JAVA 注解的几大作用及使用方法详解

一、注解的定义:
注解(Annotation) 为向代码中添加数据提供了一种形式化的方法。
Annatation(注解)是一个接口,程序可以通过反射来获取指定程序中元素的 Annotation
对象,然后通过该 Annotation 对象来获取注解中的元数据信息

常见的作用有以下几种:
1.生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see @param @return 等;
2.跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
3.在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;

二、注解的分类

1)按照运行机制划分:
【源码注解→编译时注解→运行时注解】

源码注解:只在源码中存在,编译成.class文件就不存在了。

编译时注解:在源码和.class文件中都存在。像前面的@Override、@Deprecated、@SuppressWarnings,他们都属于编译时注解。

运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。像@Autowired自动注入的这样一种注解就属于运行时注解,它会在程序运行的时候把你的成员变量自动的注入进来。

2)按照来源划分:
【来自JDK的注解——来自第三方的注解——自定义注解】

3)元注解:
元注解是给注解进行注解,可以理解为注解的注解就是元注解。

三、自定义注解
我们分四步来解析自定义注解:
自定义注解的语法要求:

@Target({ElementType.METHOD,ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Inherited

@Documented

public @interface Description {

    String desc();

    String author();

    int age() default 18;

}

首先我们要明确这不是一个接口,它是使用 @interface 关键字定义的一个注解。

然后我们看下面的几个方法,String desc();虽然它很类似于接口里面的方法,其实它在注解里面只是一个成员变量(成员以无参无异常的方式声明),int age() default 18;(成员变量可以用default指定一个默认值的)。

最后我们要知道:
①、成员类型是受限制的,合法的类型包括基本的数据类型以及String、Class、Annotation、Enumeration等。
②、如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=)。
③、注解类可以没有成员,没有成员的注解称为标识注解。元注解:

有没有发现上面那段代码有一个没有说呢?没错,它们就是我们所说的元注解:

@Target({ElementType.METHOD,ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Inherited

@Documented

第一行:@Target
定义注解作用域(可能出现在Java程序中的语法位置),可以是成员变量、方法、构造器、类、包、接口等,包括:
METHOD(方法声明),CONSTRUCTOR(构造方法声明),FIELD(字段声明),LOCAL VARIABLE(局部变量声明),METHOD(方法声明),PACKAGE(包声明),PARAMETER(参数声明),TYPE(类接口)

第二行:@Retention
定义注解保留时间,SOURCE(只在源码显示,编译时丢弃),CLASS(编译时记录到class中,运行时忽略),RUNTIME(运行时存在,可以通过反射读取)

第三行:@Inherited
标记性注解,阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该class 的子类。

第四行:@Documented
使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来

使用自定义注解:
使用注解的语法:
@<注解名>(<成员名1>=<成员值1>,<成员名1>=<成员值1>,…)

案例:

@Description(desc="i am Color",author="boy",age=18)
public String Color() {
    return "red";
}

这里的Description是我们刚才在自定义注解语法要求里面定义的注解,然后我们可以给它的每一个成员变量赋值,注意数据类型。值得注意的是,因为我们前面定义的作用域是在方法和类接口上,所以这个注解在Color()方法上使用是没问题的。

解析注解
概念:
通过反射获取类 、方法、成员变量运行时注解信息,从而实现动态控制程序运行的逻辑。

准备工作:

spring boot日志注解的应用:

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
	String module() default "";
}

使用注解:

//@LogAnnotation 通过自定义注解添加数据
    @LogAnnotation(module = "上传头像")
    @ResponseBody
    @PostMapping("/douploadavatar")
    public void douploadavatar(String headimgurl) {

        User user   =  UserUtil.getCurrentUser();
        userMapper.changeHeadimgurl(user.getId(),headimgurl);
        user.setHeadimgurl(headimgurl);

        //输出日志
        log.debug("{}修改了头像", user.getUsername());

    }

通过定义切面的方式来定义注解处理器

@Aspect
@Component
public class LogAdvice {

	@Autowired
	private SysLogService logService;

    //value传递的值是符合切入的条件,可以使用@Pointcut显式声明,也可以直接使用@annotation声明
	@Around(value = "@annotation(com.web.admin.annotation.LogAnnotation)")
	public Object logSave(ProceedingJoinPoint joinPoint) throws Throwable {

		SysLogs sysLogs = new SysLogs();

		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

		String module   = null;

		//通过解释注解获取数据(反射)
		LogAnnotation logAnnotation = methodSignature.getMethod().getDeclaredAnnotation(LogAnnotation.class);
		module = logAnnotation.module();


		if (StringUtils.isEmpty(module)) {
			throw new RuntimeException("没有指定日志module");
		}

		sysLogs.setModule(module);

		try {

			Object object = joinPoint.proceed();
			sysLogs.setFlag(true);
			logService.save(sysLogs);
			return object;

		} catch (Exception e) {

			sysLogs.setFlag(false);
			sysLogs.setRemark(e.getMessage());
			logService.save(sysLogs);
			throw e;

		}

	}
}

定义和使用注解

import java.lang.annotation.*;
import java.lang.reflect.Field;

/**
 * @Author: thunder
 * @Date: 2020/8/4 10:12
 */
//注解(Annotation) 为向代码中添加数据提供了一种形式化的方法。
//1、定义注解
//元注解定义作用范围(定义可能出现在Java程序中的语法位置),可以是成员变量、方法、构造器、类、包、接口等。
@Target(ElementType.FIELD)
//定义注解的保留时间,SOURCE(只在源码显示,编译时丢弃),CLASS(编译时记录到class中,运行时忽略),RUNTIME(运行时存在,可以通过反射读取)
@Retention(RetentionPolicy.RUNTIME)
//标记性注解,阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该class 的子类
//@Inherited
//使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来
@Documented
@interface FruitProvider {
    /**供应商编号*/
    public int id() default -1;
    /*** 供应商名称*/
    public String name() default "";
    /** * 供应商地址*/
    public String address() default "";
}

//2、使用注解
class Apple {
    //提供了一种向程序中添加数据的形式化方法
    //使用注解,作用于成员变量
    @FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路")
    private String appleProvider;

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
    public String getAppleProvider() {
        return appleProvider;
    }
}

//3、注解处理器
public class FruitInfoUtil {

    public static void getFruitInfo(Class<?> clazz) {
        String strFruitProvicer = "供应商信息:";
        //通过反射获取类所有成员变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //获取使用了FruitProvider注解的成员变量
            if (field.isAnnotationPresent(FruitProvider.class)) {
                //获取Annotation对象
                FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class);
                //提取注解对象元数据
                strFruitProvicer = " 供应商编号:" + fruitProvider.id() + " 供应商名称:" + fruitProvider.name() + " 供应商地址:"+ fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }

    public static void main(String[] args) {
        FruitInfoUtil.getFruitInfo(Apple.class);
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值