一、来源是什么
从JDK1.5之后,开始支持注解。
注解的出现,提供配置的功能。往往使用XML来做配置的功能。但是XML意味着要读取文件,编写比较麻烦。
而很多场景下,class需要的是简单的配置,而且方便地使用。
注解在编译中,嵌入到字节码上。使用java的反射机制,获取到注解。这个机制,从语言层面进行配置支持,极大方便了
程序开发,并且被spring、dubbo、mybatis、netty等各大框架应用,在反射,动态代理,服务发现,依赖注入等各方面,
有广泛应用。减少了很多重复的模板代码,降低耦合性,开发和扩展。作为一个高级工程师,是必须要熟悉和应用的。
二、原理是什么
要使用注解,就要熟悉元注解(注解的注解)。
元注解有4个,
@Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
标识这个注解怎么保存
SOURCE 表示只在代码中,意味着只是给人看;
CLASS 表示会编译到class文件中,但是运行时看不到;
RUNTIME 表示运行时能够发现。,反射只有在这种模式才能获取到注解。
@Documented
生产javadoc时,带上注解信息
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@Targe
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
表示注解的范围,
比如TYPE,表示注解在class、interface、enum上,
FIELD,表示注解在成员变量上等等。
@Inherited
标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
Inherited只能注解在注解类上。
继承的规则:
1.子类继承父类的注解,包括类注解、method注解、Field注解
2.子类重写父类的继承,都会失去注解;
3.注解的继承不能应用在接口上
3个内置注解:
@Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
从 Java 7 开始,额外添加了 3 个注解:
@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
三、如何自定义注解?
自定义注解,就是使用4个元注解了。
下面是例子
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Deprecated
@Inherited
@MyAnnotation("11")
public @interface MyAnnotation {
String value() default "";
int code() default 10;
Class<?> dd() default Integer.class;
}
注解要注意的点:
注解的配置,是依赖自定义成员变量。
成员变量的定义,比较奇特。
是一个无参数的方法,
方法名就是成员变量名;
返回值就是成员变量的数据类型;
可以设置默认值,使用default value;
数据类型只能是基础数据类型和String、class、enum,以及这些类型的数组类型
注解继承性的测试:
/**
* 测试注解标记在父类的Inherited
*/
@MyAnnotation
public abstract class InheritedTest{
@MyAnnotation
public int liu = 0;
@MyAnnotation
public int liu1 = 0;
public static void main(String[] args) {
try {
Class inheritedTest1 = Class.forName("www.hjq.cn.annotation.study.InheritedTest1");
//子类的继承
if (inheritedTest1.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("子类继承父类的注解");
} else {
System.out.println("子类不继承父类的注解");
}
Method methodFun1 = inheritedTest1.getMethod("fun1");
if (methodFun1.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("子类重写方法继承父类方法的注解");
} else {
System.out.println("子类重写方法不继承父类方法的注解");
}
Method methodFun2 = inheritedTest1.getMethod("fun2");
if (methodFun2.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("子类继承的方法继承父类方法的注解");
} else {
System.out.println("子类继承的方法不继承父类方法的注解");
}
Method methodFun3 = inheritedTest1.getMethod("fun3");
if (methodFun3.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("子类实现的抽象方法继承父类抽象方法的注解");
} else {
System.out.println("子类实现的抽象方法不继承父类抽象方法的注解");
}
Field field = InheritedTest1.class.getField("liu");
// field.setAccessible(true);
// System.out.println(field);
if (field.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("子类继承的field继承父类field的注解");
} else {
System.out.println("子类继承的field不继承父类field的注解");
}
Field field1 = InheritedTest1.class.getField("liu1");
// field.setAccessible(true);
// System.out.println(field);
if (field1.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("子类重写的field继承父类field的注解");
} else {
System.out.println("子类重写的field不继承父类field的注解");
}
} catch (Exception e) {
e.printStackTrace();
}
}
@MyAnnotation("111")
public void fun1() {
System.out.println(111);
}
@MyAnnotation("222")
public void fun2() {
System.out.println(222);
}
@MyAnnotation("222")
public abstract void fun3() ;
}
class InheritedTest1 extends InheritedTest {
public int liu1 = 0;
@Override
public void fun1() {
super.fun1();
}
@Override
public void fun3() {
}
}
结果:
子类继承父类的注解
子类重写方法不继承父类方法的注解
子类继承的方法继承父类方法的注解
子类实现的抽象方法不继承父类抽象方法的注解
子类继承的field继承父类field的注解
子类重写的field不继承父类field的注解
/**
* 测试注解标记在接口的Inherited
*/
public class InheritedInterferTest implements I1{
public static void main(String[] args) {
try {
Class InheritedTest1 = Class.forName("www.hjq.cn.annotation.study.InheritedInterferTest");
if (InheritedTest1.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("类继承接口的注解");
} else {
System.out.println("类不继承接口的注解");
}
Method methodFun1 = InheritedTest1.getMethod("i1");
if (methodFun1.isAnnotationPresent(MyAnnotation.class)) {
System.out.println("类方法继承接口方法的注解");
} else {
System.out.println("类方法不继承接口方法的注解");
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
// @MyAnnotation
public void i1() {
}
}
@MyAnnotation
interface I1{
@MyAnnotation
void i1();
}
结果:
类不继承接口的注解
类方法不继承接口方法的注解
四、有哪些广泛的应用
spring上常用的注解有
@Component
@Controller
@Service
@Repository
@Scope 主要作用是解决创建的对象单实例还是多实例
@Autowired和@Resource是用来修饰字段、构造函数或者设置方法,并做注入的
@Bean
@Configuration
@ComponentScan
@Aspect 声明一个切面
@After 在方法执行之后执行(方法上) @Before 在方法执行之前执行(方法上) @Around 在方法执行之前与之后执行(方法上)
@PointCut 声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
@Value 为属性注入值
@Profile
@EnableAsync
@Async
@EnableWebMvc
好多,要好好学习
关键还是自己,需要用到的时候,灵活使用。懂得自定义注解,配合上反射,具备极大的威力,就看我们实际上应用了。