java基础或面试问题javaweb开发java深度学习

Java:Annotation(注解)--原理到案例

2017-02-16  本文已影响4244人  zlcook
图片取自文末推荐文章

本文章涉及代码已放到github上annotation-study

1.Annotation为何而来

What:Annotation干嘛的

注解的使用案例

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
}

Why:为什么要提供元数据支持


知道元数据在编程中的重要性和提供元数据的方法Annotation了,那么就来学习Annotation吧。

2.基本Annotation

@SuppressWarnings(value="unchecked")
public class SuppressWarningTest{
   public static void main(String[] args)
   {
       List<String> myList = new ArrayList();
   }
}
@SuppressWarnings("deprecation")   //取消过时警告
    public HibernateTemplate getHt() {
        return ht;
    }
List l2 = new ArrayList<Number>();
List<String> ls = l2;    
@Functionlnterface
public interface FunInterface{
  static void foo(){
   System.out.println("foo类方法");
  }
  default void bar(){
   System.out.println("bar默认方法");
  }
  void test();//只定义一个抽象方法,默认public
}

3.JDK的元Annotation

@Retention(英文:保留)

//定义下面的Testable Annotation保留到运行时,也可以使用value=RetentionPolicy.RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface Testable{}

@Target ( 目标)

用于指定被修饰的Annotation能用于修饰哪些程序单元,只能修饰Annotation定义。它包含一个名为value的成员变量,取值如下:

@Target(ElementType.FIELD)
public @interface ActionListenerFor{}

@Documented

@Inherited

5.自定义注解

5.1一个简单的注解

//定义一个简单的注解Test
public @interface Test{}

默认情况下,Annotation可以修饰任何程序元素:类、接口、方法等。

@Test
public class MyClass{

}

5.2带成员变量的注解

//定义带成员变量注解MyTag
@Rentention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTag{
  //定义两个成员变量,以方法的形式定义
  String name();
  int age() default 32;
}

//使用
public class Test{
  @MyTag(name="liang")
  public void info(){}
}

5.3结论

5.4提取Annotation信息

获取Annotation

MyTag注解处理器

public class MyTagAnnotationProcessor {
    public static void process(String className) throws ClassNotFoundException{
        try {
             Class clazz =Class.forName(className);
             Annotation[] aArray= clazz.getMethod("info").getAnnotations();
             for( Annotation an :aArray){
                 System.out.println(an);//打印注解
                 if( an instanceof MyTag){
                     MyTag tag = (MyTag) an;
                     System.out.println("tag.name():"+tag.name());
                     System.out.println("tag.age():"+tag.age());
                 }
             }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

场景测试

public static void main(String[] args) {
        try {
            MyTagAnnotationProcessor.process("annotation.Test");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

测试结果

@annotation.MyTag(age=25, name=liang)
tag.name():liang
tag.age():25

6.使用Annotation示例

7.Java8新增的重复注解

//代码错误,不可以使用相同注解在一个程序元素上。
 @MyTag(name="liang")
@MyTag(name="huan")
public void info(){
 }

7.1 java8之前实现思路

操作步骤2步:1编写需要重复的注解@MyTag,上面定义过了。2.编写”容器“注解DupMyTag 。

@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.METHOD)
public @interface DupMyTag {
    //成员变量为MyTag数组类型
    MyTag[] value();
}
//代码正确,换个思路实现,在同一个程序元素上使用了多个相同的注解MyTag
 @DupMyTag ({ @MyTag(name="liang"),@MyTag(name="huan",age=18)})
public void info(){
 }

打印注解输出内容如下:

@annotation.DupMyTag(value=[@annotation.MyTag(age=25, name=liang), @annotation.MyTag(age=18, name=huan)])

**结论:通过新定义一个容器注解,来实现使用多个相同注解的目的,只是书写形式不能达到期待效果而已,要想书写形式能达到期待效果需要使用java8之后的@Repeatable元注解。
**

注:”容器“注解的保留期Retention必须比它所包含注解的保留期更长,否则编译报错

7.2 java8之后

操作步骤2步:1编写需要重复的注解@MyTag,如下。2.编写”容器“注解DupMyTag ,上面定义过了

//定义带成员变量注解MyTag
@Repeatable(DupMyTag.class)
@Rentention(RetentionPolicy.RUNTIME)
@Method(ElementType.METHOD)
public @interface MyTag{
  //定义两个成员变量,以方法的形式定义
  String name();
  int age() default 32;
}
@MyTag(name="liang")
@MyTag(name="huan",age =18)
public void info(){
}
//两种形式都可以
@DupMyTag ({ @MyTag(name="liang"),@MyTag(name="huan",age=18)})
public void info(){
}
@annotation.MyTag(age=25, name=liang)
@annotation.MyTag(age=18, name=huan)
@annotation.DupMyTag(value=[@annotation.MyTag(age=25, name=liang), @annotation.MyTag(age=18, name=huan)])

8. Java8新增的Type Annotation注解

8.1 介绍

8.2 案例

@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotNull {
    String value() default "";
}
//implements实现接口中使用Type Annotation
public class Test implements @NotNull(value="Serializable") Serializable{
    
        //泛型中使用Type Annotation  、   抛出异常中使用Type Annotation
    public  void foo(List<@NotNull String> list) throws @NotNull(value="ClassNotFoundException") ClassNotFoundException {
        //创建对象中使用Type Annotation
        Object obj =new @NotNull String("annotation.Test");
        //强制类型转换中使用Type Annotation
        String str = (@NotNull String) obj;
    }
}
/*
类说明 NotNull注解处理器,只处理了implements实现接口出注解、throws声明抛出异常出的注解。
*/
public class NotNullAnnotationProcessor {
    
    public static void process(String className) throws ClassNotFoundException{
        try {
            Class clazz =Class.forName(className);
            //获取类继承的、带注解的接口
            AnnotatedType[] aInterfaces =clazz.getAnnotatedInterfaces();
            print(aInterfaces);
            
            Method method = clazz.getMethod("foo");
            //获取方法上抛出的带注解的异常
            AnnotatedType[] aExceptions =method.getAnnotatedExceptionTypes();
            print(aExceptions);
            
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
    /**
     * 打印带注解类型
     * @param array
     */
    public static void print(AnnotatedType[] array){
        for( AnnotatedType at : array){
            Type type =at.getType();//获取基础类型
            Annotation[] ans =at.getAnnotations();//获取注解
            //打印类型
            System.out.println(type);
            //打印注解
            for( Annotation an : ans){
                System.out.println(an);
            }
            System.out.println("------------");
        }
    }
}

打印结果

interface java.io.Serializable
@annotation.NotNull(value=Serializable)
------------
class java.lang.ClassNotFoundException
@annotation.NotNull(value=ClassNotFoundException)
------------

9. 编译时处理Annotation

9.1 需求

9.2可用api

9.3 apt和jsr269的作用

JSR269描述

9.4实现

9.5 使用apt实现

使用apt实现编译时处理Annotation

9.6 使用JSR269实现


9.6.1 注解处理器程序annotation-processor

修饰id注解

package com.zlcook.processor.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//修饰id注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Id {
    String column();    //该id属性对应表中的列名
    String type();      //id属性类型
    String generator(); //使用的策略
}

修饰属性注解

package com.zlcook.processor.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//修饰属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Property {
    String column();    //该属性对应表中的列名
    String type();      //id属性类型
}

修饰实体类注解

package com.zlcook.processor.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//修饰实体类注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Persistent {
    String table();       //数据库中表名
}

package com.zlcook.processor;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import com.zlcook.processor.annotation.Id;
import com.zlcook.processor.annotation.Persistent;
import com.zlcook.processor.annotation.Property;

/**
* 类说明:hiberante注解处理器,用于根据实体bean的注解生成*.hbm.xml文件,处理时期在编译阶段。
*/
public class HibernateAnnotationProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        // TODO Auto-generated method stub
        super.init(processingEnv);
        System.out.println("HibernateAnnotationProcessor注解处理器初始化完成..............");
    }
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        
        //定义一个文件输出流,用于生成额外的文件
        PrintStream ps = null;
        try{
            //遍历每个被@Persistent修饰的class文件,使用RoundEnvironment来获取Annotation信息
            for( Element t : roundEnv.getElementsAnnotatedWith(Persistent.class)){
                //获取正在处理的类名
                Name clazzName = t.getSimpleName();
                //获取类定义前的@Persistent Annotation
                Persistent per = t.getAnnotation(Persistent.class);
                //创建文件输出流
                String fileName =clazzName+".hbm.xml";
                ps = new PrintStream(new FileOutputStream(fileName));
                 // 执行输出
                 ps.println("<?xml version=\"1.0\"?>");
                 ps.println("<!DOCTYPE hibernate-mapping");
                 ps.println(" PUBLIC \"-// Hibernate/Hibernate Ma  pping DTD 3.0//EN\"");
                 ps.println(" \"http:// hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">");
                 ps.println("<hibernate-mapping>");
                 ps.print(" <class name=\"" + t);
                 // 输出per的table()的值
                 ps.println("\" table=\"" + per.table() + "\">");
                 //获取@Persistent修改类的各个属性字段。t.getEnclosedElements()获取该Elemet里定义的所有程序单元
                 for(Element ele : t.getEnclosedElements()){
                     
                     //只处理成员变量上的Annotation,ele.getKind()返回所代表的的程序单元
                     if( ele.getKind() == ElementKind.FIELD){
                        //被id注解修饰的字段
                         Id idAno= ele.getAnnotation(Id.class);
                         if( idAno != null){
                             String column =idAno.column();
                             String type =idAno.type();
                             String generator = idAno.generator();
                             // 执行输出
                               ps.println(" <id name=\"" + ele.getSimpleName() + "\" column=\"" + column + "\" type=\"" + type + "\">");
                               ps.println(" <generator class=\"" + generator + "\"/>");
                               ps.println(" </id>");
                         }
                         
                         //被Property注解修饰的字段
                         Property p = ele.getAnnotation(Property.class);
                         if( p !=null){
                             // 执行输出
                             ps.println(" <property name=\"" + ele.getSimpleName() + "\" column=\"" + p.column() + "\"type=\"" + p.type() + "\"/>");
                         }
                     }
                 }// end for
                 ps.println(" </class>");
                 ps.println("</hibernate-mapping>");
            }// end for 
            
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            if(ps!=null){
                try{
                    ps.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
        return true;
    }
    /** 
     * 这里必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称 
     * @return  注解器所支持的注解类型集合,如果没有这样的类型,则返回一个空集合 
     */  
    @Override  
    public Set<String> getSupportedAnnotationTypes() {  
        Set<String> annotataions = new LinkedHashSet<String>();  
        annotataions.add(Id.class.getCanonicalName());  
        annotataions.add(Property.class.getCanonicalName());  
        annotataions.add(Persistent.class.getCanonicalName());  
        return annotataions;  
    }  
  
    /** 
     * 指定使用的Java版本,通常这里返回SourceVersion.latestSupported(),默认返回SourceVersion.RELEASE_6 
     * @return  使用的Java版本 
     */
    @Override  
    public SourceVersion getSupportedSourceVersion() {  
        return SourceVersion.latestSupported();  
    }  
}

源代码文件及编译后文件存放位置 source.list文件内容
E:\EclipseWorkspace\Cnu\annotation-processor\src\main\java>javac -encoding UTF-8
 -d classes @sources.list
E:\EclipseWorkspace\Cnu\annotation-processor\src\main\java\classes>jar -cvf annotation-processor.jar com

9.6.2 注解使用程序annotation

<dependency>
          <groupId>com.zlcook.processor</groupId>
          <artifactId>annotation-processor</artifactId>
          <version>0.0.5-SNAPSHOT</version>
          <scope>provided</scope>
      </dependency>
package com.zlcook.annotation.bean;
import com.zlcook.processor.annotation.Id;
import com.zlcook.processor.annotation.Persistent;
import com.zlcook.processor.annotation.Property;

/**
* @author 周亮 
* @version 创建时间:2017年2月19日 下午10:05:05
* 类说明:使用注解完成映射的实体类
*/
@Persistent(table="person_inf")
public class Person {
     @Id(column = "person_id", type = "integer", generator = "identity")
     private int id;
     @Property(column = "person_name", type = "string")
     private String name;
     @Property(column = "person_age", type = "integer")
     private int age;
     public int getId() {
      return id;
     }
     public void setId(int id) {
      this.id = id;
     }
     public String getName() {
      return name;
     }
     public void setName(String name) {
      this.name = name;
     }
     public int getAge() {
      return age;
     }
     public void setAge(int age) {
      this.age = age;
     }
}

9.6.3 运行效果演示

E:\EclipseWorkspace\Cnu\annotation\src\main\java>javac -encoding UTF-8 -d classes -classpath annotation-processor.jar -processor com.zlcook.processor.HibernateAnnotationProcessor com/zlcook/annotation/bean/Person.java
<build>
   <plugins>
    <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <executions>
          <execution>
            <id>default-compile</id>
            <phase>compile</phase>
            <goals>
              <goal>compile</goal>
            </goals>
            <configuration>
             <source>1.8</source>
             <target>1.8</target>
              <annotationProcessors>
                <annotationProcessor>com.zlcook.processor.HibernateAnnotationProcessor</annotationProcessor>
             </annotationProcessors> 
            </configuration>
          </execution>
        </executions>
      </plugin>
   </plugins>
  </build>
mvn clean compile

执行完成后在项目根目录下就出现了Person.hbm.xml文件。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
 PUBLIC "-// Hibernate/Hibernate Ma  pping DTD 3.0//EN"
 "http:// hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.zlcook.annotation.bean.Person" table="person_inf">
 <id name="id" column="person_id" type="integer">
 <generator class="identity"/>
 </id>
 <property name="name" column="person_name"type="string"/>
 <property name="age" column="person_age"type="integer"/>
 </class>
</hibernate-mapping>

本文章涉及代码已放到github上annotation-study

参考文章:
Java注解(3)-注解处理器(编译期|RetentionPolicy.SOURCE)
jar 打包命令详解
如何用javac 和java 编译运行整个Java工程
深入理解Java:注解(Annotation)自定义注解入门
深入理解Java:注解(Annotation)--注解处理器

留言

喜欢就点个赞吧,多谢鼓励,如果有什么不懂的一起探讨一下吧。

上一篇下一篇

猜你喜欢

热点阅读