全面解析java注解
一.注解的定义与分类
注解的概念:
Java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法
JDK自带注解:
@Override 代表子类重写父类的方法
@Deprecated 代表该方法已经过时
@SuppressWarning 代表忽略警告Warnings
注解的分类:
- 源码注解:注解只在源码中存在,编译成.class文件就不存在了
- 编译时注解:注解在源码和.class文件中都存在(@Override,@Deprecated这些都是)
- 运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解(@Autowired)
注解的代价
凡事有得必有失,注解技术同样如此。使用注解也有一定的代价:
- 显然,它是一种侵入式编程,那么,自然就存在着增加程序耦合度的问题。
- 自定义注解的处理需要在运行时,通过反射技术来获取属性。如果注解所修饰的元素是类的非 public 成员,也可以通过反射获取。这就违背了面向对象的封装性。
- 注解所产生的问题,相对而言,更难以 debug 或定位。
二.自定义注解
1.自定义注解的语法要求
a.使用@interface关键字定义注解
b.成员以无参无异常方式声明
c.可以用default为成员指定一个默认值
d.其中成员类型是受限的,合法的类型包括原始类型及String,Class,Annotation,Enumeration
e.如果注解只有一个成员,则成员名必须为value(),在使用时可以忽略成员名和赋值号(=)
f.注解类可以没有成员,没有成员的注解称为标识注解(例如@Overrride)
//以下成员都是不合法的,因为成员要无参数,无异常
String desc(int a);
String desc() throws Exception;//成员类型为受限的,以下不可以
Map desc();
//语法:
//注解属性只能使用 public 或默认访问级别(即不指定访问级别修饰符)修饰。
[访问级别修饰符] [数据类型] 名称() default 默认值;
元注解:
- @Target是作用域
- @Retention生命周期
- @Inherited 这是一个标识型元注解,即允许继承(默认情况下不是这样)
- @Documented 生成javadoc时会包含注解
2.使用自定义注解
解析注解:
概念:通过反射获取类,函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
public static void main(String[] args){//1.使用类加载器加载类try{Class c=Class.forName("com.ann.test.Child");//2.找到类上面的注解boolean isExist=c.isAnnotation(Description.class);if(isExist){//3.拿到注解实例Description d=(Description)c.getAnnotation(Description.class);System.out.println(d.value());
}//4.找到方法上的注解Method[] ms=c.getMethods();for(Method m:ms){boolean isExist=m.isAnnotation(Description.class);if(isExist){Description d=(Description)m.getAnnotation(Description.class);System.out.println(d.value())}}
}}catch(ClassNotFoundException e){e.printStackTrace();
}
}
三.注解实战
需求:
- 有一张用户表,字段包括用户ID,用户名,昵称,年纪,性别,所在城市,邮箱,手机号
- 方便的对每个字段或者字段的组合条件进行检索,并打印出SQL
package reflectionandproxy.test;
//1.首先考虑代码如何与数据库进行映射--->Filter和数据库字段相似
@Table("user")
public class Filter {@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;public String getMobile() {return mobile;}public void setMobile(String mobile) {this.mobile = mobile;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getNickName() {return nickName;}public void setNickName(String nickName) {this.nickName = nickName;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}
package reflectionandproxy.test;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)
public @interface Column {String value();//数据库字段名
}
package reflectionandproxy.test;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)public @interface Table {String value();//表名
}
package reflectionandproxy.test;import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Test {public static void main(String[] args) {Filter f1=new Filter();f1.setId(10);//查询ID为10的用户Filter f2=new Filter();f2.setUserName("Tom");//模糊查询用户名为Tom的用户Filter f3=new Filter();f3.setEmail("tom@gmail.com,cat@gmail.com,7777@qq.com");//查询邮箱为其中任意一个//查询sql语句String sql1=query(f1);String sql2=query(f2);String sql3=query(f3);//输出System.out.println(sql1);System.out.println(sql2);System.out.println(sql3);}private static String query(Filter f1) {//2.接下来我们就考虑如何实现query方法StringBuilder sb=new StringBuilder();//a.获取到classClass clazz=f1.getClass();//b.获取到table的名字boolean exists = clazz.isAnnotationPresent(Table.class);if(! exists){return null;}Table table= (Table) clazz.getAnnotation(Table.class);String tableName=table.value();sb.append("select * from ").append(tableName).append(" where 1=1");//c.获取到所有的字段Field[] fields=clazz.getDeclaredFields();for (Field field : fields){//4.处理每个字段对应的sql语句//4.1 获取到字段名boolean exists1 = field.isAnnotationPresent(Column.class);if(! exists1){continue;}Column column=field.getAnnotation(Column.class);String columnName=column.value();//4.2 获取字段值String fieldName = field.getName();String getMethodName = "get" + fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);Object fieldValue=null;try {Method getMethod = clazz.getMethod(getMethodName);fieldValue =getMethod.invoke(f1);} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {throw new RuntimeException(e);}//4.3拼装sql语句if(fieldValue==null||(fieldValue instanceof Integer && ((Integer) fieldValue).intValue()==0)){continue;}sb.append(" and ").append(columnName).append(" = ").append(fieldValue);}return sb.toString();}
}
控制台输出: