追溯mybatis注解的基础:java注解的发展历程
1、mybatis注解背景介绍
在早期版本,mybatis配置信息是基于xml,sql映射语句也是定义在xml中的,而到了mybatis 3提供了基于注解的配置和映射。
相对于xml文件来说,mybatis注解更加简洁。当然,注解也并不非只有功没有过,这种情况下xml更胜一筹:MyBatis XML配置对抗MyBatis注解的一大杀器:SQL片段,抽取可重用的SQL语句。
mybatis注解,说白了,就是将mybatis的数据库配置信息和sql映射信息不再放在xml文件里面,而是直接放在了mapper里面。
mybatis注解被广泛使用的背景是java注解功能的日益强大。java注解从代码注释演变而来,一步一步发展成强大的元数据配置工具。本文将简述一下java注解的发展历程,体会注释和注解之间的内在联系。
2、java注解的原始时代:javadoc注释
注解,又称标注,英文是:Annotation,是jdk5.0引入的一种注释机制。注解本质就是注释。说到注释,大家应该不陌生,见下面的例子:
/*** Demo实例
* @author www.mybatis.cn
* @version 1.0
*/
public class Demo
{
}
上面的注释中出现了两个标注:@author和@version,这两者都可以通过javadoc来提取出来形成类的说明文档。
javadoc是Sun公司提供的一个技术,它从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档。
3、java注解的活跃时代:百花齐放
自从jdk 5.0引入注解机制后,java语言中的类、方法、变量、参数和包等都可以被标注,花样繁多,百花齐放。
和javadoc不同,java注解可以通过反射获取注解内容。
3.1、java注解的分类
横看成岭侧成峰,远近高低各不同。java注解按照不同的划分标准有多种情况。
java注解按运行机制分:
(1)源码注解:只在源码中存在,编译后不存在。
(2)编译注解:源码和编译后的class文件都存在,例如:@Override,@Deprecated,@SuppressWarnings
(3)运行时注解:能在程序运行时起作用,例如:spring的依赖注入
java注解按来源分:
(1)来自jdk的注解
(2)第三方的注解
(3)自定义的注解
3.2、元注解
了解java注解,首先要从元注解入手。元注解是用于修饰注解的注解,通常用在注解的定义上,例如,Override注解的定义:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override
{
}
这是@Override 注解的定义,你可以看到其中的 @Target,@Retention 两个注解就是我们所谓的元注解。
元注解一般用于指定某个注解生命周期以及作用目标等信息。
java中有以下几个元注解:
(1)@Target:注解的作用目标
(2)@Retention:注解的生命周期
(3)@Documented:注解是否应当被包含在 JavaDoc 文档中
(4)@Inherited:是否允许子类继承该注解
其中,@Target 用于指明被修饰的注解最终可以作用的目标是谁,也就是指明,你的注解到底是用来修饰方法的,修饰类的,还是用来修饰字段属性的。
3.3、@Target注解介绍
@Target注解的定义如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target
{
ElementType[] value();
}
我们可以通过以下的方式来为这个 value 传值:
@Target(value = {ElementType.FIELD})
注意:此处数组的写作手法,下面稍微扩展一点。空数组的写法是:String urlPrefixes = {};不要写成:String urlPrefixes = new String[]{""};
被这个 @Target 注解修饰的注解将只能作用在成员字段上,不能用于修饰方法或者类。其中,ElementType 是一个枚举类型,其值如下所列:
ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
ElementType.FIELD:允许作用在属性字段上
ElementType.METHOD:允许作用在方法上
ElementType.PARAMETER:允许作用在方法参数上
ElementType.CONSTRUCTOR:允许作用在构造器上
ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
ElementType.ANNOTATION_TYPE:允许作用在注解上
ElementType.PACKAGE:允许作用在包上
3.4、@Retention注解介绍
@Retention 用于指明当前注解的生命周期,它的基本定义如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention
{
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
同样的,它也有一个 value 属性:
@Retention(value = RetentionPolicy.RUNTIME)
这里的 RetentionPolicy 是一个枚举类型,它有以下几个枚举值可取:
RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
RetentionPolicy.RUNTIME:永久保存,可以反射获取
@Retention 注解指定了被修饰的注解的生命周期。注解的生命周期有三种:注解驻留在源文件阶段,字节码文件阶段和内存字节码阶段。
(1)注解被保留到源文件阶段
当javac把.java源文件编译成.class时,就将相应的注解去掉。这种注解的生命周期就维持到源文件阶段。
(2)注解被保留到字节码文件阶段
在JVM通过ClassLoader向内存中加载字节码文件时候,JVM会去掉相应的注解。这种注解的生命周期就维持到字节码文件阶段。
注意:生命周期到源文件阶段和字节码文件阶段的注解,由于JVM执行内存中的字节码时候,相应的注解已经被Javac或者JVM去除,所以无法使用反射来访问相应的注解。
(3)注解被保留到内存中的字节码阶段
JVM运行内存的字节码时候,仍然可能会保留并且执行的某些注解。这种注解的生命周期就维持到内存字节码阶段。这个阶段,程序可以通过反射访问生命周期到内存字节码阶段的注解。
注解的生命周期三个阶段简单表示为:java源文件-->class文件-->内存中的字节码
3.5、@Inherited注解介绍
@Inherited 注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。
3.6、自定义注解:
注解其实就是一种标记,可以在程序代码中的关键节点(类、方法、变量、参数、包)上打上某些标记,然后程序在编译时或运行时可以检测到这些标记从而执行一些特殊操作。
使用注解能极大地提升开发效率,因此自定义注解是一个高级开发者必备的技能。自定义注解使用的基本流程:
第一步,定义注解——相当于定义标记;
第二步,配置注解——把标记打在需要用到的程序代码中;
第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。
见下面的应用场景,一本书分为封面设计者和内容作者。
Author为注解,用来表示作者的名字和年龄。Book为普通的类,代表书这个实体。可以通过给Book增加Author注解来表示封面设计者和内容作者。
@Target(
{ ElementType.TYPE, ElementType.METHOD })
//表示注解的作用对象,ElementType.TYPE表示类,ElementType.METHOD表示方法
@Retention(RetentionPolicy.RUNTIME)
//表示注解的保留机制,RetentionPolicy.RUNTIME表示运行时注解
@Inherited
//表示该注解可继承
@Documented
//表示该注解可生成文档
public @interface Author
{
String name();
//注解成员,如果注解只有一个成员,则成员名必须为value(),成员类型只能为原始类型
int age() default 0;
//注解成员,默认值为0
}
@Author(name = "Bill", age = 35)
//使用自定义注解,有默认值的成员可以不用赋值,其余成员都要赋值
public class Book
{
@Author(name = "Tom", age = 30)
public void cover()
{
}
@Author(name = "Bill", age = 35)
public void content()
{
}
}
public class Test
{
public static void main(String[] args) throws ClassNotFoundException
{
Class c = Class.forName("cn.mybatis.annotations.Book");
//使用类加载器加载类
//1、找到类上的注解
if (c.isAnnotationPresent(Author.class))
{ //判断类是否被指定注解注解
Author author = (Author) c.getAnnotation(Author.class);
//拿到类上的指定注解实例
System.out.println("作者年龄:" + author.age());
}
//2、找到方法上的注解
Method[] ms = c.getMethods();
for (Method m : ms)
{
if (m.isAnnotationPresent(Author.class))
{ //判断方法是否被指定注解注解
Author author = m.getAnnotation(Author.class);
//拿到类上的指定注解实例
System.out.println(m.getName() + "的作者年龄:" + author.age());
}
}
//3、找到方法上的注解
for (Method m : ms)
{
Annotation[] as = m.getAnnotations();
//拿到类上的注解集合
for (Annotation a : as)
{
if (a instanceof Author)
{ //判断指定注解
Author author = (Author) a;
System.out.println(m.getName() + "作者年龄:" + author.age());
}
}
}
}
}
4、mybatis注解和java注解小结
java注解,本质就是注释,注释谁不会写呢。只不过,注解可以在编译期,运行期能够被利用。
mybatis注解,就是将mybatis的数据库配置信息和sql映射信息不再放在xml文件里面,而是直接放在了mapper里面,跟mapper紧密的结合到一起了。