1. 自定义注解
至于已有的注解,比如:JPA、EJB、Spring零配置等等怎么使用相信各位读者都能掌握,这里主要是说如何自定义自己的注解,自己使用自定义的注解。
我们先用一个简单的例子来看
package annotation;
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;
/** * 注解 * * @author Administrator */ @Retention(RetentionPolicy.RUNTIME) public @interface Test {
String isAop() default "false";
} |
用关键字@interface定义一个注解标记,使用@interface关键字实际上的意思就是该接口继承自java.lang.annotation.Annotation接口。
String isAop() default "false"; |
这句话代表的意思就是在Test注解中可以含有属性名为isAop,此属性的类型是字符串类型。客户端使用的时候可以根据需要自己指定相关的属性值。如果客户端不指定值,默认值是false
@Retention(RetentionPolicy.RUNTIME) |
这句话代表着将使用该注解类的信息值保持到真正的客户端运行时环境。
下面我们就来看看客户端的使用
package use;
import annotation.Test; import annotation.TestImpl.TestProcess;
@Test(isAop = "true") public class UseTest {
/** * @param args * @throws ClassNotFoundException */ public static void main(String[] args) throws ClassNotFoundException {
TestProcess.process("use.UseTest");
}
} |
在客户端调用中在类UseTest上使用了@Test(isAop = "true")注解。仅仅定义了注解就像《三国杀》里,刘备是主公,他有“激将”的主公计,下了个命令:“蜀将何在?”,刘备的这句话太抽象了,蜀将相当于一个注解。在场的所有蜀将就像加了此注解的类,都会受到这句话的影响。具体替不替刘备出杀,~~~~个人表现不同(得先看看自己的身份啊)反贼的表现是:“这个真没有”;忠臣的表现是,先看看手上有杀吗?有,出击吧!没有就说:“这个……真没有!”;内奸的反应是:“唉,先保命还是装一装忠臣?比较纠结!”。这里的身份就好像是注解的属性的不同值。具体的处理就相当于针对注解的处理实现类。注解的具体实现类就是处理注解的业务逻辑,它需要Java的反射机制来处理客户目标类的具体注解,我们就来看看这个注解处理实现类。
package annotation.TestImpl;
import java.lang.annotation.Annotation;
public class TestProcess {
public static void process(String str) throws ClassNotFoundException {
Class clazz = Class.forName(str);
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) { System.out.println(annotation); } }
} |
以上的处理逻辑很简单,就是根据一个字符串类名找到类。获得类的所有注解,所有注解是一个对象数组。遍历注解数组,输出相关注解信息。运行以上的程序结果如下
@annotation.Test(isAop=true) |
如果我们使用注解的客户端代码替换一下
@Test public class UseTest { …………………… } |
对于注解的isAop并不特别指定。运行效果如下
@annotation.Test(isAop=false) |
可以看到使用的是默认值false。如果注解中Annotation并没有指定默认值,而在客户端使用中也没指定值,那么不会通过编译。
@Retention(RetentionPolicy.XXXXXX)上面说到了是保留注解的有效期。
//会将注解保留到编译后的class中,加载类的时候不生效 @Retention(RetentionPolicy.CLASS) //仅仅在代码保留,编译后的class不会保留 @Retention(RetentionPolicy.SOURCE) //在编译后的class会有,通过反射也会获得注解信息 @Retention(RetentionPolicy.RUNTIME) |
比如Override注解的源码
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { } |
因为复写在使用者使用的时候就可以看出来,所以没必要保留到运行期。
比如SuppressWarnings注解的源码
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); } |
以上的不提示警告的注解也是在编译一个类前检查即可。
以上还使用了@Target注解,它代表着注解的修饰范围,类型是java.lang.annotation.ElementType枚举类型。
public enum ElementType { TYPE,//可以修饰类、接口、枚举、注解 FIELD,//可以修饰成员变量 METHOD,//可以修饰方法 PARAMETER,//可以修饰参数 CONSTRUCTOR,//可以修饰构造方法 LOCAL_VARIABLE,//可以修饰局部变量 ANNOTATION_TYPE,// 可以修饰Annotation PACKAGE//可以修饰包 } |
看Deprecated源码
@Documented @Retention(RetentionPolicy.RUNTIME) public @interface Deprecated { } |
Deprecated代表已过时的意思,这是一个保持到运行期的注解,在运行期可以通过反射获取此注解。这里还使用了@Documented这个元注解。它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。
还有一个注解就是Inherited,如果使用了该标记,那么使用该注解的子类也会继承该注解的特性。
2. 实例
很多人有疑问,你说这些有什么用?对于开发有什么促进吗?EJB注解、JPA注解我们会使用完成业务不就得了。下面再来看一个程式例子
注解Pojo
package annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Pojo { String table(); } |
注解Colum
package annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Colum { String columName(); boolean isNULL() default true; String Type() default ""; } |
处理注解的逻辑实现类
package annotation.TestImpl;
import java.lang.annotation.Annotation; import java.lang.reflect.Field; import annotation.Colum; import annotation.Pojo;
public class MyJPAProcess {
public static void process(Object object) throws ClassNotFoundException {
// 所有注解 Annotation[] annotationsClass = object.getClass().getAnnotations();
Field[] fields = object.getClass().getDeclaredFields();
// 遍历注解元素 for (Annotation annotation : annotationsClass) { if (annotation instanceof Pojo) { System.out.println("--------处理POJO实体--------"); Pojo pojo = (Pojo) annotation; System.out.println("存储表名:" + pojo.table()); }
}
for (Field field : fields) {
//field.setAccessible(true);
Annotation[] annotationsMethos = field.getAnnotations();
// 遍历注解元素 for (Annotation annotation : annotationsMethos) { if (annotation instanceof Colum) { System.out.println("--------处理Colum字段--------"); Colum colum = (Colum) annotation; System.out.println("字段名:" + colum.columName()); System.out.println("字段类型:" + colum.Type()); System.out.println("是否能为空:" + colum.isNULL()); } } }
}
} |
以上是定义了简单的2个注解Pojo和Colum。之后为这2个注解提供了逻辑处理。下面我们来看看客户端是怎么使用的吧。
一般的实体类
package use;
import annotation.Colum; import annotation.Pojo;
@Pojo(table = "person") public class PersionPOJO {
public PersionPOJO() { super(); }
@Colum(columName = "id", isNULL = false, Type = "varchar") private String id;
@Colum(columName = "name", isNULL = false, Type = "varchar") private String name;
@Colum(columName = "age", isNULL = true, Type = "int") private int age;
//省略set/get
} |
这是一个普通的实体对象+自定义的注解。针对于这个POJO我们再定义一个DAO类操作这个POJO类。
package use;
import annotation.TestImpl.MyJPAProcess;
public class PersionDAO {
public void save(PersionPOJO persionPOJO) throws ClassNotFoundException { MyJPAProcess.process(persionPOJO); }
public static void main(String[] args) throws ClassNotFoundException {
PersionDAO persionDAO = new PersionDAO(); PersionPOJO persionPOJO = new PersionPOJO(); persionDAO.save(persionPOJO); } } |
上面程序就定义了一个保存方法,测试一下,调用这个save方法。控制台输出如下信息。
--------处理POJO实体-------- 存储表名:person --------处理Colum字段-------- 字段名:id 字段类型:varchar 是否能为空:false --------处理Colum字段-------- 字段名:name 字段类型:varchar 是否能为空:false --------处理Colum字段-------- 字段名:age 字段类型:int 是否能为空:true |
DAO中调用了注解逻辑实现类的方法。这个时候我们稍微修改一下注解逻辑实现类的方法,配合一个ORM中间件,就可以将数据保存到数据库了。