有用的Java注解
好处:
能够读懂别人的代码,特别是框架相关的代码;
让编程更加简洁,代码更加清晰。
使用自定义注解解决问题!!
Java1.5版本引入。
Java中的常见注解
- @Override:告诉使用者及编译器,该方法覆盖了父类或接口中的同名方法
- @Deprecated:表示该方法已经过时了。
- @Suppvisewarnings:忽略deprecated给出的警告。
- 常见第三方注解:
- Spring: @Autowired, @Service, @Repository
- Mybatis: @InsertProvider, @UpdateProvider, @Options
注解分类
- 按照运行机制分:
- 源码注解:注解只在源码中存在,编译成class文件就不存在了
- 编译时注解:注解在源码和.class文件中都存在
- 运行时注解 :在运行阶段还起作用,甚至会影响运行逻辑的注解 如@Autowired注解
- 按照来源分:
- 来自JDK的注解
- 来自第三方的注解
- 自己定义的注解
- 元注解:注解的注解
自定义注解
- 语法要求
- 成员类型是受限的,合法的类型包括原始类型及String,Class,Annotation,Enumeration;
- 如果注解只有一个成员,则成员们必须取名为value(),在使用时可以忽略成员名和赋值号(=);
- 注解类可以没有成员,没有成员的注解类成为标识注解;
- 元注解(注解的注解)
- @Target(……)
- @Retention:生命周期(SOURCE:只在源码显示编译时丢弃;CLASS:编译时会记录到class中,运行时忽略;RUNTIME:运行时存在,可以通过反射读取)
- @Inherited:允许子类继承,只能在类或者抽象类间继承,接口是不会继承的。而且继承时只是类的继承,类中的方法并不会继承。
- @Documented:生成javadoc时会包含信息。
- 到处一个Javadoc:在工程上点击Export->javadoc->设置路径,通过index.html即可查看你导出的Javadoc
- 使用自定义注解
解析注解
- 概念:通过反射获取类、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。
-
//实例:拿到类名字的解释 public class ParseAnn { public static void main(String[] args) { //1.使用类加载器加载类 try { Class c = Class.forName("com.ann.test.Child"); //2.找到类上面的注解 boolean isExist = c.isAnnotationPresent(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 isMExist = m.isAnnotationPresent(Description.class); if(isMExist) { Description d = (Description) m.getAnnotation(Description.class); System.out.println(d.value()); } } //另外一种解析方法 for(Method m : ms) { Annotation[] as = m.getAnnotations(); for(Annotation a : as) { if(a instanceof Description) { Description d = (Description)a; System.out.println(d.value()); } } } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
-
-
//Child.java信息 @Description("I am class annotation") public class Child implements Person { @Override @Description("I am method annotation") public String name() { // TODO Auto-generated method stub return null; } @Override public int age() { // TODO Auto-generated method stub return 0; } @Override public void sing() { // TODO Auto-generated method stub } }
实践
- 项目取自一个公司的持久层框架,用来代替Hibernate的解决方案,核心代码就是通过注解来实现的。
- 需求:
- 1.有一张用户表,字段包括用户ID、用户名、昵称、年龄、性别、所在城市、邮箱、手机号。
- 2.方便的对每个子弹或字段的组合条件进行检索,并打印出SQL。
- 使用方式要足够简单。
-
private static String query(Object f) { StringBuilder sb = new StringBuilder(); //1.获取到class Class c = f.getClass(); //2.获取到table的名字 boolean exist = c.isAnnotationPresent(Table.class); if(!exist) return null; Table t = (Table) c.getAnnotation(Table.class); String tableName = t.value(); sb.append("select * from ").append(tableName).append(" where 1=1 "); //3.遍历所有的字段 Field[] fArray = c.getDeclaredFields(); for(Field field : fArray) { //4. 处理每个字段对应的SQL //4.1 拿到字段名 boolean fe = field.isAnnotationPresent(Column.class); if(!fe) 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 = c.getMethod(getMethodName); fieldValue = getMethod.invoke(f); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //4.3拼装SQL if(fieldValue == null || (fieldValue instanceof Integer && (Integer)fieldValue == 0)) continue; if(fieldValue instanceof String) { if(((String) fieldValue).contains(",")) { String[] values = ((String) fieldValue).split(","); sb.append("and ").append(fieldName).append(" in("); for(String v : values) { sb.append("'").append(v).append("',"); } sb.deleteCharAt(sb.length()-1); sb.append(")"); } else sb.append("and ").append(fieldName).append("='") .append(fieldValue).append("'"); } else sb.append("and ").append(fieldName).append("=").append(fieldValue); } return sb.toString(); }
总结
- 注解的作用范围@Target和生命周期@Retention
- 作用范围包括:包、类、字段、方法、方法的参数和局部变量
- 生命周期:源文件SOURCE、编译CLASS、运行RUNTIME
- 等读懂注解
- 在实际项目中用注解解决问题,并能自定义注解