理解Java注解实现机制
前言
在Spring 2.0及早期时代,Java 还没引入注解,这个时候如果我们要在 Spring 中声明一个 Bean,我们只能通过 XML 配置的方式,Web项目开发都是通过配置文件 xml来实现 Bean 的依赖注入,有多少个Bean,就在xml配置中加多少个,这样一来在 Bean 的数量越来越多的时候,xml的配置也就会越来越复杂,显得格外的冗余,Spring 2.0 在xml配置文件上做了一定的优化,让配置看起来越来越简单。
而在后来的 Spring 3.0 时代,可以使用 Spring 提供的 Java 注解来取代曾经 xml 配置上的所产生的问题,Spring 使得项目开发配置变得简单了许多
一,什么是注解?
.注解其实就是代码里的特殊标记 ,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下 ,在源文件中嵌入一些补充信息。代码分析工具,开发工具和部署工具可以通过这些补充信息进行验证或者进行部署
二, 注解有何用?
1,注解的出现极大的简化了jdk1.5之前 使用xml配置的方式,代替JavaEE旧版中所遗留的繁冗代码和XML配置等
2,生产文档注解,标明该模块类开发者,模块版本等信息。
3,在编译时进行格式检查(Jdk内置的三个基本注解)
4,跟踪代码依赖性,实现替代配置文件等功能
示例
xml配置方式
public class DemoService{
}
<bean id="demoService" class="com.lulu.DemoService"/>
注解配置方式
@Service
public class DemoService{
}
三,注解重要么?
未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上是基于注解的,现在的springboot是完全注解开发,注解是一种趋势,一定程度上可以说:框架=注解+反射+设计模式。
四,如何自定义注解?
1:
在定义注解之前我们可以看看注解的源码是怎样定义的
我们可以通过idea的快捷键选中注解 按住Crtl同时点击鼠标左键查看注解源码
如 选中select()上的 @Override
@Override
public List<Admin> select() {
return adminMapper.select();
}
}
2:
然后我们就看到了源码对@Override的定义,可以发现这和接口的定义很像,都有通过interface进行声明,只不过interface多了一个@
还有两个对注解的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
3:
然后我们可以参照此注解自定义注解,首先使用@interface关键字(自定义的注解自动继承了java.lang.annotation.Annotation接口),我们可以给该注解加入一个成员参数属性 value ,注意,虽然value有一对括号,和类,接口的方法定义很像 但它是注解的属性不是方法
/**
* @author Tony-wang
* @create 2020-09-17-16:05
*/
public @interface MyAnnotation {
String value()
}
4:
然后我们就可以在其它类或方法上使用自定义注解了
注意 声明注解成员后 如果没有指定默认值default就要 在使用注解时 显示的赋值 ,如果不声明内部成员,那么该注解仅起到一个标识作用,如@Override就没有内部成员
@MyAnnotation("hello")
public class Person{
五:元注解
1:什么是元注解呢?
元注解 是jdk中修饰其他注解的注解,jdk1.5中提供了4种标准的meta-annotation类型 分别是 Retention Target Document Inherited
Retention:只能用于修饰一个Annotation定义,用于指定该Annotation的生命周期,该元注解包含一个RetentionPolicy类型的成员变量,使用@Rentention必须为该value赋值
RetentionPolicy有三个状态
RetentionPolicy.SOURCE:在源文件中有效(编译器直接丢弃这种策略的注释)
RetentionPolicy.CLASS:在class文件中有效,当运行java程序时,jvm不会保留注释,这是默认值
RetentionPolicy.RUNTIME:在运行时有效(即运行中保留),当运行java程序时,jvm会保留注释,程序可以通过反射获取该注释 *
Target 用于修饰该注解可以在哪些地方进行使用 指定类 中使用还是接口使用还是局部变量进行使用
如 该注解就只能在属性和方法中使用
@Target({FIELD,METHOD})//表示该注解只能使用在属性和方法上
@Retention(RetentionPolicy.RUNTIME)//运行时有效,被jvm类加载器加载到内存后 可以通过反射获取注解信息
public @ interface MyAnnotation{
Document :用于指定该注解会被javadoc 工具提取为文档(很少用)
Inherited:被它修饰的注解具有基础性(很少用)
Java 1.8中注解增强
元注解@Repeatable
元注解@Repeatable是JDK1.8新加入的,它表示在同一个位置重复相同的注解。在没有该注解前,一般是无法在同一个类型上使用相同的注解的
//Java8前无法这样使用
@FilterPath("/web/update")
@FilterPath("/web/add")
public class A {}
但在Java8新增了@Repeatable注解后就可以采用如下的方式定义并使用了
//使用Java1.8新增@Repeatable元注解
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)//参数指明接收的注解class
public @interface FilterPath {
String value();
}
//使用案例
@FilterPath("/web/add")
@FilterPath("/web/delete")
class AA{ }
JDK1.8新增两个枚举成员
在Java8中 ElementType 新增两个枚举成员,TYPE_PARAMETER 和 TYPE_USE ,在Java1.8前注解只能标注在一个声明(如字段、类、方法)上,Java8后,新增的TYPE_PARAMETER可以用于标注类型参数,而TYPE_USE则可以用于标注任意类型(不包括class)。如下TYPE_USE修饰泛型类型,异常类型,强转类型所示
@Target({FIELD,METHOD,TYPE_PARAMETER,TYPE_USE})//
@Retention(RetentionPolicy.RUNTIME)//运行时有效,被jvm类加载器加载到内存后 可以通过反射获取注解信息
public @ interface MyAnnotation{
string value() default "HEELO 靓仔!"
}
class Generic<@MyAnnotation T>{ //注解修饰泛型类型
public void show() throws @MyAnnotation RuntimeException{//注解修饰异常类型
ArrayList<@MyAnnotation String> list = new ArrayList<>();
int num = (@MyAnnotation int ) 10L;//对强转类型进行修饰
}