【iMooc】全面解析java注解
在慕课上学习了一个关于java注解的课程,下面是笔记以及一些源码。
Annotation——注解
1.JDK中的注解
JDK中包括下面三种注解:
@Override:标记注解(marker annotation),重写,父类写的方法,如果子类继承了父类,就要重写父类的方法。
@Deprecated:表示过时的语法加上这个注解之后,该方法上会出现一道删除线
@SuppressWarning:忽略警告,压制警告。如果某语句前面出现警告,加上这个注解,警告就会消失。
举例如下:
父类 一个Person接口: @Deprecated表示该方法过时。
package com.ann.test; public interface Person { public String name(); public int age(); @Deprecated public void sing(); }
子类Child 集成父类,必须 重写@Override父类的所有方法。
package com.ann.test; public class Child implements 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 } }
测试代码: 测试代码中调用了过时的sing()函数,产生警告。使用@SuppressWarnings注解 ,可消除警告。
package com.ann.test; public class Test { @SuppressWarnings("deprecation") public void sing() { Person p = new Child(); p.sing(); } }
2.常见第三方注解:
Spring:
- @Autowired:不用配置文件,可以自动生成Dao文件注入进去
- @Serice
- @Repository
Mybatis:
- @InsertProvider
- UpdataProvider
- @Options
3.注解分类:
按照运行机制:
- 编码注解:只存在源码中
- 编译时注解:存在于源码与.class中 如@override、@Deprecated、@SuppressWarning
- 运行时注解:运行时将成员变量自动注入(可能会影响运行逻辑)如Spring中的@AutoWired
按照来源:
- JDK注解
- 第三方注解(Spring等)
- 自定义注解
4.自定义注解
可新建一个Annotation,@interface表示注解。在自定义注解中,
- 成员变量必须是无参无异常的形式,可以设置使用default设置默认值
- 成员变量的类型:基本数据类型、String、class、Annotation、Enumeration
- 成员变量只有一个的时候,一般取名为value();使用的时候,可以直接赋值
- 可以没有成员
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 Decription { String desc(); String author(); int age() default 18; }
@Target:表示注解作用域,关键字包括Constructor(构造函数)、Field(变量)、Local_veriable、method、package、parameter、tyoe(类,接口)
@Retention:生命周期:source、class、runtime
@Inherited:可以被子类继承
@Documented:生成javadoc时包含注解信息
自定义注解的使用:
@<注解名>(<成员名1>=<value1>,.......)
如下:
package com.ann.test; @Decription(desc="I am class description", author = "YM") public class Child implements Person{ @Override @Decription(desc="I am method description", author = "YM", age = 18) 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 } }
5.解析注解
- 首先使用加载器加载类
- 找到类上的注解
- 拿到注解实例
- 找到方法上的注解
另一种解析方法:以方法上的注解为例
- 遍历所有的方法
- 拿出所有的注解
- 遍历所有注解
package com.ann.test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class ParseAnn { public static void main(String[] args) { try { //1.使用类加载器加载类 Class c=Class.forName("com.ann.test.Child"); //2找到类上的注解 boolean isExis = c.isAnnotationPresent(Decription.class); if(isExis){ //3.拿到注解实例 Decription d=(Decription) c.getAnnotation(Decription.class); System.out.println(d.desc()); } //4.找到方法上的注解 Method[] ms=c.getMethods(); for(Method m:ms){ boolean isMExis=m.isAnnotationPresent(Decription.class); if(isMExis){ Decription mds=(Decription)m.getAnnotation(Decription.class); System.out.println(mds.desc()); } } //另外一种解析方法 //1.遍历所有方法 for(Method m:ms){ //2拿出所有注解 Annotation[] an=m.getAnnotations(); //3遍历所有注解 for(Annotation a:an){ //如果a是我们自定义注解的实例 if(a instanceof Decription) { //将a强转成Decription类 Decription d= (Decription)a; System.out.println(d.desc()); } } } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
输出:
I am class description I am method description I am method description
6.注解实战:
需求:一张用户表。自动的对每个字段或字段的组合条件进行检索,拼接sql语句,并打印
Filter.class
package com.ann.dao; @Table("user") public class Filter { @Column("id") private int id; @Column("userName") private String userName; @Column("nikeName") private String nikeName; @Column("age") private int age; @Column("city") private String city; @Column("email") private String email; @Column("mobile") private String 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 getNikeName() { return nikeName; } public void setNikeName(String nikeName) { this.nikeName = nikeName; } 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; } public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } }
Table (自定义注解)作用域为TYPE表示类
package com.ann.dao; 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 { public String value(); }
Column (自定义注解)
package com.ann.dao; 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 { public String value(); }
Test :
package com.ann.dao; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Test { // 1.首先考虑代码怎么与数据库做映射 filter是与数据库关系最紧密的类-转到filter // 2需要考虑query方法如何实现 public static void main(String[] args) { Filter f1 = new Filter(); f1.setId(10);// 查询id为10的用户 Filter f2 = new Filter(); f2.setUserName("Tom");// 查询用户名为tom的用户 f2.setAge(18); Filter f3 = new Filter(); f3.setEmail("123@qq.com,haha@126.com"); String sql1 = query(f1); String sql2 = query(f2); String sql3 = query(f3); System.out.println("sql1:" + sql1); System.out.println("sql2:" + sql2); System.out.println("sql3:" + sql3); //写一个filter可以应用到不同的表中 Filter2 filter2= new Filter2(); filter2.setAmount(10); filter2.setId(1); filter2.setName("技术"); System.out.println(query(filter2)); } private static String query(Object f) { // 首先用一个StringBuilder来存储字符串表示sql语句 StringBuilder sb = new StringBuilder(); // 1获取class Class c = f.getClass(); // 2.获取table名 boolean isCExis = c.isAnnotationPresent(Table.class); if (!isCExis) { 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[] field = c.getDeclaredFields(); for (Field fi : field) { // 4.处理每个字段对应的sql // 4.1拿到字段的名字 boolean isFExis = fi.isAnnotationPresent(Column.class); if (!isFExis) { continue; } Column col = fi.getAnnotation(Column.class); String columnName = col.value(); // 4.2拿到字段的值(反射) String fieldName = fi.getName();// 字段名 // 先拿到get方法的名字 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拼接 if (fieldValue == null || (fieldValue instanceof Integer && (Integer) fieldValue == 0)) { continue; } sb.append(" and ").append(fieldName); if (fieldValue instanceof Integer) { sb.append(" = ").append(fieldValue); } else if (fieldValue instanceof String) { if (((String) fieldValue).contains(",")) {// 邮箱格式包含,表示有多种选择 String[] string = ((String) fieldValue).split(","); sb.append(" in ("); for(String str:string){ sb.append("'").append(str).append("'").append(","); } sb.deleteCharAt(sb.length()-1); sb.append(")"); } else { sb.append(" = '").append(fieldValue).append("'"); } } } return sb.toString(); } }
sql1:select * from user where 1=1 and id = 10 sql2:select * from user where 1=1 and userName = 'Tom' and age = 18 sql3:select * from user where 1=1 and email in ('123@qq.com','haha@126.com')