1、Java注解的含义?

# 1、Java注解的含义?

Java注解(Annotations)是Java 5之后引入的特性,用于为Java代码提供元数据。这些元数据本身不直接影响代码的逻辑功能,但可以被编译器、开发工具或框架用于生成代码、执行测试、执行静态分析等。

Java注解有以下几个主要的用途:

  • 1、 编译器指导:可以告诉编译器应该如何处理注解在其上的代码。例如,@Override注解告诉编译器下面的方法是重写父类的方法。如果没有在父类中找到该方法,则编译器会报错。

  • 2.、编译时代码生成:一些工具可以读取注解,然后在编译时生成额外的代码。例如,Lombok库使用注解生成getter、setter和构造函数等。

  • 3、运行时代码行为:在运行时,可以通过反射读取注解,并根据这些注解改变代码的行为。很多框架,如Spring和Hibernate,都使用注解来配置或修改代码的行为。

  • 4、 文档生成:注解也可以用于生成文档。

Java提供了一些内置的注解,如@Override@Deprecated@SuppressWarnings等。但你也可以创建自己的自定义注解。

创建自定义注解的基本语法如下:

public @interface MyAnnotation {
    String value() default "";
    int count() default 0;
}

你可以在类、方法、变量等上使用这个注解:

@MyAnnotation(value="test", count=5)
public class MyClass {
    // ... 
 }

要读取这些注解,通常会使用Java的反射API。
最后,注解本身不会对代码的功能产生任何直接效果。它们只是标记或元数据,必须配合其他工具或代码来实现实际功能。

# 2、Java有哪些元注解,分别有什么作用?

在Java中,元注解(Meta-Annotations)是用于注解其他注解的注解。
它们用于对注解进行更详细的配置和说明。Java定义了几个元注解,分别具有不同的作用。以下是一些常见的元注解及其作用:

①、 @Retention:指定被注解的注解在编译后是否会被保留到运行时。它有以下取值:

  • RetentionPolicy.SOURCE:只在源代码中保留,不会出现在编译后的字节码中。
  • RetentionPolicy.CLASS:保留到编译后的字节码中,但不会加载到运行时。
  • RetentionPolicy.RUNTIME:保留到运行时,可以通过反射读取。 一般我们自定义注解都使用RetentionPolicy.RUNTIME

②、 @Target:指定被注解的注解可以应用于哪些元素(类、方法、字段等)。它的取值包括:

  • ElementType.TYPE:类、接口、枚举等。
  • ElementType.METHOD:方法。
  • ElementType.FIELD:字段。
  • 等...

③、 @Documented:用于标记其他注解是否应该被包含在生成的Java文档中。

④、 @Inherited:表示被标记的注解可以被子类继承。默认情况下,注解是不会被子类继承的。

⑤、 @Repeatable:允许同一个元素多次应用同一个注解。

这些元注解通常不会直接影响程序的逻辑,而是用于提供注解的元数据信息,以便编译器、工具或框架进行相应的处理。
可以通过将元注解应用到自定义注解上来对自己的注解进行更详细的配置和说明。

# 3、自定义一个简单的注解实现对入参的校验?

下面的例子是使用代理+注解实现对方法入参的非空校验

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;

public class TestAnnotation {


    public static void main(String[] args) {
        TestInterface proxy = ProxyUtil.createProxy(new TestInterfaceImpl());
        proxy.ppt("");
    }


}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface NotEmpty {
}



interface TestInterface{

    void ppt(@NotEmpty String a);
}

class TestInterfaceImpl implements TestInterface{

    @Override
    public void ppt(@NotEmpty String a) {
        System.out.println(a);
    }
}






class ProxyUtil {

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    validateParameters(method, args);
                    return method.invoke(target, args);
                });
    }

    private static void validateParameters(Method method, Object[] args){
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            // 判断NotEmpty注解 是否在方法参数上标注
            if (parameters[i].isAnnotationPresent(NotEmpty.class)) {
                if (args[i] instanceof String && ((String) args[i]).isEmpty()) {
                    throw new RuntimeException("方法:"+method.getName()+" 的参数" + parameters[i].getName() + " 不能为空!");
                }
            }
        }
    }
}

运行结果:

Exception in thread "main" java.lang.RuntimeException: 方法:ppt 的参数a 不能为空!
	at com.kinggm.test.ProxyUtil.validateParameters(TestAnnotation.java:66)
	at com.kinggm.test.ProxyUtil.lambda$createProxy$0(TestAnnotation.java:56)
	at com.kinggm.test.$Proxy0.ppt(Unknown Source)
	at com.kinggm.test.TestAnnotation.main(TestAnnotation.java:16)

总结:
注解是我们在框架中最常用到的功能,知道其基本的运行原理,有助于为我们后期对框架的学习和运用注解实现更高级的功能打下基础。