注解:
Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。
提供了一种原程序中的元素关联任何信息和任何数据的途径和方法
注解介绍
注解如同一种标签,表明对被注解的事物行为的某些角度的评价与解释。
首先来了解一下Java自带的部分常用注解:
@Override:很常见:表示子类对父类方法的重写
@Deprecated:表示已被弃用一个过时的方法等
@Suppvisewarnings:表示阻止警告
@FunctionalInterface:函数式接口注解,
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
还有很多第三方的注解,比如ButterKnife中@BindView便是一个注解。
注解分类
注解也有分类的,按运行机制来划分的话,可分为:
- 源码注解 :注解只会在源码文件中存在,编译成.class文件时会被编译器丢弃
- 编译时注解:在源码和.class文件中都存在如@Override、@Suppvisewarunings都属于此类
- 运行时注解:JVM运行时也会保留注解,可通过反射获取注解信息,如@Deprecated
其中还有元注解,表示对注解的注解,比如以上三种分类,都对应元注解中的@Retention注解,有三类值:
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
}
下面来具体介绍元注解:元注解有5种,如下
@Retention、@Documented、@Target、@Inherited、@Repeatable。
元注解 | 含义 |
---|---|
@Target | 表示该注解的作用域,指定了注解运用的地方,如变量FILED、方法METHON、类、接口(包括注解)、枚举TYPE、构造方法CONSTRUCTOR、局部变量LOCAL_VARIABLE等 |
@Retention | 它的因为意义为保留,说明它的存活时期,表示在什么级别运用该注解,有三种,如上面所说的 |
@Document | 与javadoc文档相关,将该注解中的元素保留到javadoc中 |
@Inherited | 英文意义为继承,表示子类可以继承父类的注解,自承父业的意思,哈哈 |
@Repeatable | 英文意义为可重复的,表示注解能运用多次,相当于可以重复使用该注解 |
下面来使用一下自定义注解:
先定义一个Description,表示对一个人的描述
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {//注解需要使用@interface关键字定义
//如果注解只有一个成员,则需名为value()
//成员类型是受限制的,合法类型包括基本变量类型和String,Class,Annotation,Enum
String desc();//成员必须要无参数无异常
String author();
int age() default 18;//可以使用default指定默认值
}
解析注解
其实我感觉这类不为显式的值,都可以通过反射来获取。
首先创建自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String value();
}
再创建注解所作用的类
@Description("the class Annotation")
public class TestAnnotation {
@Description("the method Annotation")
public String name(){
return null;
}
}
再进行解析
public static void main(String[] args) throws ClassNotFoundException {
//1.使用ClassLoader加载类
Class c = Class.forName("annotation.TestAnnotation");
//2.找到类上的注解
boolean isExist = c.isAnnotationPresent(Description.class);//判断是否存在该注解
if (isExist){
//3.获取注解实例
Description description = (Description) c.getAnnotation(Description.class);
System.out.println(description.value());
}
}
输出为:the class Annotation
方法上的注解也是一样的,
//4.找到方法的注解
Method[] method = c.getMethods();
for (Method m : method){
if (m.isAnnotationPresent(Description.class)){
System.out.println(m.getAnnotation(Description.class).value());
}
}
注解实例
假设一个需求,需要对每个表的字段自动的生成sql查询语句,你要去怎么设计,一个好的方法便是通过注解去获取。
User
@Table("user")
public class User {
@Column("id")
private int id;
@Column("user_name")
private String userName;
@Column("nick_name")
private String nickName;
@Column("age")
private int age;
@Column("city")
private String city;
@Column("email")
private String email;
@Column("mobile")
private String mobile;
....省略get,set方法
Table注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
Column注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
SqlUitl生成Sql语句工具类
public class SqlUtil {
public static String query(Object t) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
StringBuilder sb = new StringBuilder();
//1.获取class
Class c = t.getClass();
//2.获取Table名
if (!c.isAnnotationPresent(Table.class)){
return "";
}
Table table = (Table) c.getAnnotation(Table.class);
sb.append("select * from ").append(table.value()).append("where 1=1");
//3.遍历所有字段
Field[] field = c.getDeclaredFields();
for (Field f : field){
//4.处理每个字段对应的sql
//4.1拿到字段注解值
if (!f.isAnnotationPresent(Column.class)){
continue;
}
Column column = f.getAnnotation(Column.class);
String columbName = column.value();
//4.2拿到字段的名
String fieldName = f.getName();
//获取相应的get方法名
String MethodName = "get" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
Method method = c.getMethod(MethodName);
Object filedValue = method.invoke(t);
//4.3拼装成sql
if (filedValue == null || filedValue instanceof Integer && (Integer) filedValue == 0){
continue;
}
sb.append(" and ").append(columbName);
if (filedValue instanceof String){
sb.append("=").append("'").append(filedValue).append("'");
}else if (filedValue instanceof Integer){
sb.append("=").append(filedValue);
}
}
return sb.toString();
}
}
下面便来测试下效果如何
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
User user = new User();
user.setId(9);
user.setUserName("QQW");
user.setEmail("12156@adas.com");
System.out.println(SqlUtil.query(user));
}
输出:select * from userwhere 1=1 and id=9 and user_name='QQW' and email='12156@adas.com'
。
假如这个时候换另外一个表,现在只需要补上相应的注解即可自动生成,是不是很强大。这也便是注解的作用之一。