扯扯注解的蛋 为什么学习注解?学习注解有什么好处?学完能做什么? 1、能够读懂别人的代码,特别是框架相关的代码 2、让编程更加简洁,代码更加清晰 3、让别人高看你一眼 注解是java1.5引入的 概念: java人提供了一种原程序中的元素关联任何消息和任何元数据的途径和方法 java中的常见注解 JDK自带注解: @Override 放在方法前,表示该方法重写了父类的这个方法 @Deprecated 放在方法上,表名该方法已经过时,后面还使用这个方法会弹出黄色警告线 @SuppressWarnings("deprecation") 放在方法前,忽视警告 常见的第三方注解: Spring: @Autowired 自动装载该属性 @Service @Repository Mybatis: @InsertProvider @UpdateProvider @Options 注解的分类 按运行机制分: 源码注解:注解只在源码中存在,编译成class文件后就不存在了 编译时注解:注解种子源码和class文件都存在。我们使用的三个JDK注解都是编译时注解 运行时注解:运行阶段还会起作用,甚至会影响运行逻辑的注解 例如spring的@autowried 元注解:注解的注解 自定义注解 自定义注解的语法要求 注解的注解(元注解) 使用自定义注解 解析注解 概念:通过反射获取类、函数或成员上运行的注解信息,从而实现动态控制程序运行的逻辑 注解应用实战
一个注解的例子和一些概念:
//一个注解的例子 @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Description{ String desc(); String author(); int age() default 18; } //1.使用@interface关键字定义注解 //2.成员以无参无异常的方式声明 可以给成员指定默认值,比如age //3、成员类型是受限的,合法的类型包括原始类型以及String,Class,Annotation,Enumeration //4.如果注解只有一个成员,则成员必须取名为value(),在使用时可以忽略成员名和赋值号(=) //5.注解类可以没有成员,没有成员的注解成为标识注解 /** 元注解:用在注解上的注解 @Target 注解使用的一个作用域,多个用逗号隔开,它的可选值: CONSTRUCTOR构造方法声明 FIELD 字段声明 LOCAL_VARIABLE 局部变量声明 METHOD 方法声明 PACKAGE 包声明 PARAMETER 参数声明 TYPE 类,接口 例子: @Target({ElementType.METHOD,ElementType.TYPE}) @Retention 注解的声明周期,可选属性 SOURCE 只在源码显示,编译时会丢弃 CLASS 编译时会记录到class中,运行时忽略 RUNTIME 运行时存在,可以通过反射读取 例子: @Retention(RetebtionPolicy.RUNTIME) @Inherited 标识注解,允许子注解继承它 @Document 也是标识注解,生成doc的时候回包含注解信息 */ /** 使用自定义注解 使用注解的语法: @<注解名>(<成员名1>=<成员值1>,<成员名2>=<成员值2>,...) 例子: @Desciption(desc="I am eyeColor",author="Mooc body",age=18) public String eyeColor(){ return "red"; } */
接触了解简单注解:
package com.ann.test; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Description { String desc(); String author(); int age() default 18; } /* * 当注解使用在不符合类型的场合的时候,会发生编译期错误 * 如果Retention注解的值是SOURCE或者CLASS时,使用反射解析注解会得不到,因为反射是得到的是运行期的Class文件 * @Inherited是个标识注解表名注解可以继承 * 我发现直接解析Child上面的注解的话是无法的到任何注解的,说明它并没有继承Person类的注解,这是为什么呢? * If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, * and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type. * 注解是应该声明在class上的 * 我们将Person更改为class,则发现可以在子类得到其类上面的注解,但是,其方法上的注解是无法继承得到的,所以我们没有获取到 */ package com.ann.test; @Description(author="guozhen",desc="I am a interface annotion!",age=22) public class Person { public String name(){ return null; } @Description(author="I am a method annotation!",desc="haha",age=22) public int age(){ return 0; } //我发现并不是所有的人都会唱歌,所以这个sing()不应该写在这里,但是有人实现了这个接口不能改动 @Deprecated //表示这个方法已经过时 public void sing(){ } } package com.ann.test; public class Child extends Person { @Override 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 } } package com.ann.test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class Test { public static void main(String[] args) { //解析注解。 //1.首先找到装载了注解的class文件 try { Class c=Class.forName("com.ann.test.Child"); //2判断类上是否存在指定注解 boolean isExit=c.isAnnotationPresent(Description.class); if(isExit){ //3.得到注解并输出其内容 Description d=(Description)c.getAnnotation(Description.class); System.out.println(d.author()+" "+d.age()+" "+d.desc()); } //解析方法上的注解 //1.得到方法 Method[] ms=c.getMethods(); for(Method m:ms){ isExit=m.isAnnotationPresent(Description.class); if(isExit){ Description d=(Description)m.getAnnotation(Description.class); System.out.println(d.author()+" "+d.age()+" "+d.desc()); } Annotation[] as=m.getAnnotations(); for(Annotation a:as){ if(a instanceof Description){ Description ds=(Description)a; System.out.println(ds.author()+" "+ds.age()+" "+ds.desc()); } } } //2.或者也可以这样解析方法上的注解 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @SuppressWarnings("deprecation") public void sing(){ Person p=new Child(); p.sing();//这个方法已经过时,虽然可以使用,但是会给警告。我们可以添加注解使得忽视警告 } // @Description(desc="I am eyeColor",author="Moocbocy",age=18) public String eteColor(){ return "red"; } }
小小实战,加深理解:
package com.ann.test; 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 Table { String value(); } package com.ann.test; 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 Column { String value(); } package com.ann.test; /** * * @author Administrator * 我们要操作的是Student表 * 里面有这些字段: * id name age sex email */ @Table("Student") public class Student { @Column("id") private int id; @Column("username") private String name; @Column("age") private int age; @Column("sex") private String sex; @Column("email") private String email; 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; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } package com.ann.test; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; /** * * @author Administrator * 通过一个简单的例子实现注解应用 * 希望达到的效果: * 通过传递对象的类型,获取值使用注解的值在对应表中查询数据数据满足传递过来的对象的所有条件值 * */ public class TestAnn { public static void main(String[] args) { TestAnn ta=new TestAnn(); Student s=new Student(); s.setName("郭大侠"); s.setEmail("453454@sa.com"); Student s2=new Student(); s2.setAge(21); s2.setName("杨康"); Student s3=new Student(); s3.setEmail("1580909730@qq.com,18307006930@163.com,32919@sohu.com"); System.out.println(ta.query(s)); System.out.println(ta.query(s2)); System.out.println(ta.query(s3)); //如果需要JDBC操作,就需要将得到的使用连接池进行执行就好了 //这个代码只实现了一部分功能,仅仅是作为参考 } public String query(Object s){//优化一下这里,传入的不一定是Student类型,为Object类型的对象 /** * 分析:得到正确的sql语句,首先需要确定几个条件 * 这个语句时做什么的?查询 * 查询的内容是什么?* * 查询的表名是什么?通过注解Table配置的Value值 * 查询的字段是什么?通过注解配置的Column的value * 对应字段的值是什么?就是对应对象对应字段的值,使用getXxx()方法获取 */ StringBuilder sb=new StringBuilder(); Class c=s.getClass();//获取字节码文件 //得到表名 String tableName=null; Object columnName=null; Object columnValue=null; boolean isExit=c.isAnnotationPresent(Table.class); if(!isExit){ return null; } Table t=(Table) c.getAnnotation(Table.class); tableName=t.value(); sb.append("select * from "+tableName+" where 1=1");//为什么使用1==1,为了避免后面没有条件的话报错 //接下来将@Clumn中的值和对象对应属性的值注解进去 //Field[] fileds=c.getFields();//这里注意不能直接用这个方法得到的只有public修饰的字段 Field[] fileds=c.getDeclaredFields(); for(Field f:fileds){ if(!f.isAnnotationPresent(Column.class)){ continue; }else{ //值为null或者int类型值为0的属性不应该出来 Column cc=f.getAnnotation(Column.class); columnName=cc.value(); //获取对应的属性 String methodName="get"+f.getName().substring(0, 1).toUpperCase()+f.getName().substring(1); try { columnValue= c.getMethod(methodName).invoke(s);//这里报错Integer不能转换为String if((columnValue instanceof Integer && (Integer)columnValue==0)||columnValue==null){ continue; } //字符串需要加单引号 if(columnValue instanceof String){ //多个字符串字段的使用逗号拆分然后依次拼接,并且查询条件编程in,模糊查询 String[] strs=((String) columnValue).split(","); if(strs.length>1){ sb.append(" "+columnName+" in ("); for(String ss:strs){ sb.append("'"+ss+"',"); } sb.deleteCharAt(sb.length()-1);//减去最后一个逗号 sb.append(")"); }else{ sb.append(" and "+columnName+" = '"+columnValue+"'"); } }else{ sb.append(" and "+columnName+" = "+columnValue); } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } } return sb.toString(); } }