概念
注解是Java提供的一种 源程序中的元素关联任何信息和任何元数据的途径和方法。
Java常见注解
- JDK自带注解
- @Override 覆盖父类的方法
- @Deprecated 让方法过时
- @Suppvisewarnings 忽略警告
- 常见第三方注解(Spring为例)
- @Autowired 可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作。通过 @Autowired的使用来消除set ,get方法。
- @Service 用于标注业务层组件。定义某个类为一个bean,则在这个类的类名前一行使用@Service(“XXX”),就相当于讲这个类定义为一个bean,bean名称为XXX。而无需去xml文件内去配置。
- @Repository 用于标注数据访问组件,即DAO组件。
注解分类
- 按运行机制分
- 源码注解:注解只在源码中存在,编译成.class文件就不存在了
- 编译时注解:注解在源码和.class文件中都存在(如:JDK内置系统注解)
- 运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解(如:Spring中@Autowried)
- 按照来源分
- JDK内置注解
- 自定义注解
- 第三方注解
- 元注解——注解的注解
自定义注解
自定义注解的语法要求
- 使用@interface关键字定义注解
- 成员以无参无异常方式声明
可以用default为成员指定一个默认值
如果注解只有一个成员,则成员名必须取名value(),在使用时可以忽略成员名和赋值号(=);
- 成员类型是受限的,合法的类型包括原始数据类型和String, Class, Annotation, Enumeration
注解类可以没有成员,没有成员的注解称为标识注解
- 元注解(注解里的注解)
- @Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD}):定义注解的作用域
- CONSTRUCTOR 构造方法声明
- FIELD 字段声明
- LOCAL_VARIABLE 局部变量声明
- METHOD 方法声明
- PACKAGE 包声明
- PARAMETER 参数声明
- TYPE 类接口
- @Retention(RetentionPolicy.RUNTIME):定义生命周期
- SOURCE 只在源码显示,编译时会丢弃
- CLASS 编译时会记录到class中,运行时忽略
- RUNTIME 运行时存在,可以通过反射读取
- @Inherited:允许子类继承父类上的注解。只能用在类上,如果用在接口上将不会起作用。也只能继承类上的注解,方法上的不能被继承。
- @Documented:生成javadoc的时候包含注解
- @Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD}):定义注解的作用域
使用自定义注解
/* 自定义的注解类 */
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.FIELD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description{
int age() default 10;
String author();
String desc();
}
--------------------------------------------------------------------------------
/*
使用注解的语法:
@<注解名>(<成员名1>=<成员值1>,<成员名1>=<成员值1>,...)
*/
@Description(desc="I am eyeColor",author="Mooc boy",age=18)
public String eyeColor(){
return "red";
}
解析注解
概念:通过反射获取类、函数或成员的运行时注解信息,从而实现动态控制程序运行的逻辑。
步骤:
- 使用类加载器加载类
- 找到类上边的注解
- 拿到注解实例
- 遍历方法上的注解
示例:
//自定义注解 Description.java
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.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description{
int age() default 10;
String author();
String desc();
}
// Person.class 用自定义注解修饰
@Description(desc="human class", author="wgp", age=20)
public class Person {
@Description(desc="walk method", author="gp", age=21)
public void walk(){
System.out.println("human can walk!");
}
}
// 注解解析类 Test.class
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
try {
//1. 使用类加载器加载类
Class c = Class.forName("Person");
//2. 找到类上面的注解
boolean isExist=c.isAnnotationPresent(Description.class);//isAnnotationParse()判断类上是否存在Description这样的注解
if(isExist){
//3.获得注解实例
Description d = (Description)c.getAnnotation(Description.class);
System.out.println(d.desc());
}
//4.找到方法上的注解
Method[] mts = c.getMethods();
for (Method mt : mts) {
if(mt.isAnnotationPresent(Description.class)){
Description d2 = (Description)mt.getAnnotation(Description.class);
System.out.println(d2.author());
}
}
//另外一种获取类上的注解的途径
Annotation[] annos = c.getAnnotations();
for (Annotation anno : annos) {
if(anno instanceof Description){
System.out.println(((Description) anno).author());
}
}
//另一种获取方法上的注解的途径
for (Method mt : mts){
Annotation[] annos2 = mt.getAnnotations();
for (Annotation anno : annos2) {
if(anno instanceof Description){
System.out.println(((Description) anno).desc());
}
}
}
} catch (Exception e) {
//TODO: handle exception
}
}
}
项目案例
说明:
代替Hibernate的解决方案,用在项目的持久层,核心代码就是用自定义注解实现的。
需求:
- 有一张用户表,字段包含id,用户名,密码,地址,手机号;
- 方便的对每个字段或者每个字段的组合进行检索,并打印出SQL。
分析:
- 首先肯定有一个关于用户的JavaBean,包含所有用户信息;
- 有了JavaBean需要将属性与字段匹配,也就是ORM;
- 匹配完相当于拼接SQL语句时,知道表名和字段名称了,还缺少where中的字段值是多少;
- 通过用户类的getter方法来获取字段值为多少,拼接完整SQL语句。
代码实现:
用户类,我们希望用@Table和@Cloumn来绑定;
/** * User:用户JavaBean */ @Table("user") public class User { @Column("id") public Integer id; @Column("user_name") public String userName; @Column("password") public String passWord; @Column("address") public String address; @Column("phone_num") public String phoneNum; public void setId(Integer id) { this.id = id; } public void setUserName(String userName) { this.userName = userName; } public void setPassWord(String passWord) { this.passWord = passWord; } public void setAddress(String address) { this.address = address; } public void setPhoneNum(String phoneNum) { this.phoneNum = phoneNum; } public Integer getId() { return id; } public String getUserName() { return userName; } public String getPassWord() { return passWord; } public String getAddress() { return address; } public String getPhoneNum() { return phoneNum; } }
自定义@Table注解和@Column注解;
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(); } -------------------------------------------------------------------------- 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(); }
根据反射来拼接出全部的SQL语句。
import java.lang.reflect.Field; import java.lang.reflect.Method; public class UserDao { public static void main(String[] args) { User user1 = new User(); user1.setId(0); User user2 = new User(); user2.setUserName("ping"); User user3 = new User(); user3.setAddress("beijing"); user3.setPhoneNum("10101010101"); String sql1 = query(user1); //查询id为0用户 String sql2 = query(user2); //查询用户名含有ping的用户 String sql3 = query(user3); //查询满足多个条件的用户 System.out.println(sql1); System.out.println(sql2); System.out.println(sql3); } public static String query(User user) { StringBuilder sBuilder = new StringBuilder(); Class clazz = user.getClass(); // 获取表名 if(!clazz.isAnnotationPresent(Table.class)){ return null; } Table table = (Table) clazz.getAnnotation(Table.class); String tableName = table.value(); sBuilder.append("select * from ").append(tableName).append(" where 1 = 1"); // 获取所有字段名+值 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if(!field.isAnnotationPresent(Column.class)){ continue; } Column column = field.getAnnotation(Column.class); String columnName = column.value(); //字段名 String fieldName = field.getName(); //属性名 String getMethodName = "get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1); //get方法名 Object fieldValue = null; //属性值(先提升为Object类型) try { Method getMethod = clazz.getMethod(getMethodName); fieldValue = getMethod.invoke(user); //执行get方法获取值 } catch (Exception e) { e.printStackTrace(); } //继续拼接SQL if(fieldValue == null){ continue; } sBuilder.append(" and ").append(columnName); if(fieldValue instanceof String){ sBuilder.append("='").append(fieldValue).append("'"); } else if(fieldValue instanceof Integer){ sBuilder.append("=").append(fieldValue); } } return sBuilder.toString(); } }