CoreJava -- 注解和反射

1. 注解

注解(Annontion)是Java5开始引入的新特征。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。

注解的分类(按照运行机制分类)

  • 源码注解:注解只在源码中存在,编译成.class文件就不存在了,如lombok中的@Data。
  • 编译时注解:注解在源码和.class文件中都存在,如@Override。
  • 运行时注解:在运行阶段会起作用,甚至影响运行逻辑的注解,如Spring中的@Autowired。

注解的分类(按照来源分类)

  • 原生JDK
  • 来自第三方
  • 自定义

2. 元注解

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解有以下几种:

  • @Target
  • @Retention
  • @Documented
  • @Inherited

2.1 @Target

用于描述注解的范围,即注解在哪用。它说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)等。取值类型(ElementType)有以下几种:

  1. CONSTRUCTOR: 构造方法声明
  2. FIELD: 类成员变量声明
  3. LOCAL_VARIABLE: 局部变量声明
  4. METHOD: 方法声明
  5. PACKAGE: 包声明
  6. PARAMETER: 方法参数声明
  7. TYPE: 类、接口(包括注解类型) 或enum声明
  8. TYPE_PARAMETER:1.8版本开始,描述类、接口或enum参数的声明
  9. TYPE_USE:1.8版本开始,描述一种类、接口或enum的使用声明
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Log {
    ......
}
// 表示Log注解可以用在类、接口、enum和方法上

2.2 @Retention

用于描述注解的生命周期,表示需要在什么级别保存该注解,即保留的时间长短。取值类型(RetentionPolicy)有以下几种:

  • SOURCE:在源文件中有效(即源文件保留)
  • CLASS:在class文件中有效(即class保留)
  • RUNTIME:在运行时有效(即运行时保留)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
  ......
}
// 示例使用RetentionPolicy.RUNTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理。

2.3 @Documented 

标识注解,用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
  ......
}

2.4 @Inherited

标识注解,用于表示某个被标注的注解是可以被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 不能用于接口实现。只能继承父级类上的注解,不能继承父级方法上的注解。

2.5 综合案例

package com.okgo.annotation;

import java.lang.annotation.*;

//测试原注解
@MyAnnotation
public class Test01 {

    public void test() {

    }

}

//定义一个注解
//Target 表示我们的注解可以用在哪些地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})

//Retention 表示我们的注解在什么地方有效   runtime>class>source
@Retention(value = RetentionPolicy.RUNTIME)

//Documented 表示是否将我们的注解生成在JAVAdoc中
@Documented

//Inherited  子类可以继承父类的注解
@Inherited
@interface MyAnnotation{

}

3. 自定义注解

3.1 语法要求

import java.lang.annotation.*;


/**
 * 自定义注解的语法要求
 *      1. 使用 @interface 关键字定义注解
 *      2. 以“接口的方法”为成员,以无参无异常方式声明
 *          可以使用 default 给成员指定一个默认值
 *          成员的类型是受限制的,合法的类型包括基本类型及 String, Class, Annotation, Enumeration
 *          如果注解只有一个成员,则成员取名必须为 value(), 且在使用时可以忽略成员名和赋值符号(=)
 *          注解类可以没有成员,没有成员的注解称为标识注解
 *
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
    String desc();
    String author();
    int age() default 18;
}


@Description(desc = "i am interface anno")
public class Person {

    @Description(desc = "i am interface method anno")
    String name(){
        return "name";
    }
    int age(){
        return 0;
    }
    @Deprecated
    void sing(){

    }
}

@Description(desc = "i am class anno")
public class Child extends Person{

    @Description(desc = "i am method anno")
    @Override
    public String name() {
        return null;
    }

    @Override
    public int age() {
        return 0;
    }

    @Override
    public void sing() {

    }
}

 3.2 解析注解

/**
 *  解析注解:通过反射获取类、方法或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。
 *
 *       1:使用类加载器加载类
 *       Class c = Class.forName("com.ann.test.Child");
 *
 *       2:找到类上面的注解
 *       isAnnotationPresent(类类型):Class对象的方法,判断当前类类型是否存在某个类类型的注解,返回类型为boolean。
 *       boolean isExist = c.isAnnotationPresent(Description.class);
 *
 *       3:获取类上自定义注解实例,需要强制类型转换。
 *       Description d =(Description)c.getAnnotation(Description.class);
 */
public class ParseAnno {
    public static void main(String[] args) {
        try {
            Class<?> c = Class.forName("com.okgo.spring.annotation.Child");
            boolean present = c.isAnnotationPresent(Description.class);
            if (present) {
                Description descAnno = c.getAnnotation(Description.class);
                System.out.println(descAnno.desc());
            }
            Method[] methods = c.getMethods();
            for (Method method : methods) {
                boolean b = method.isAnnotationPresent(Description.class);
                if (b) {
                    Description desAnno = method.getAnnotation(Description.class);
                    System.out.println(desAnno.desc());
                }
            }
            //
            for (Method method : methods) {
                Annotation[] annotations = method.getAnnotations();
                for (Annotation annotation : annotations) {
                    if (annotation instanceof Description) {
                        Description desc = (Description) annotation;
                        System.out.println(desc.desc());
                    }

                }

            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

3.3 最佳实践

 

// User 实体类,对应数据库表user
@Table("user")
public class User {
    @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 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;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
}
// 两个自定义注解
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();
}

 

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) {
        User u1 = new User();
        u1.setId(10);

        User u2 = new User();
        u2.setUserName("lucy");

        User u3 = new User();
        u3.setEmail("1@gmail.com,2@gmail.com,3@gmail.com");

        String sql1 = query(u1);
        String sql2 = query(u2);
        String sql3 = query(u3);
        System.out.println(sql1);
        System.out.println(sql2);
        System.out.println(sql3);
    }

    private static String query(User u) {
        StringBuilder sb = new StringBuilder();
        //1. 获取到class
        Class<? extends User> cls = u.getClass();
        //2. 获取到table name
        boolean present = cls.isAnnotationPresent(Table.class);
        if (!present) {
            return null;
        }
        Table table = cls.getAnnotation(Table.class);
        String tableName = table.value();
        sb.append("select * from ").append(tableName).append(" where 1 = 1");
        //3. 遍历所有字段
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            //4. 处理每个字段对应的sql
            //4.1 拿到字段名
            boolean fExists = field.isAnnotationPresent(Column.class);
            if (!fExists) 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 fieldValve = null;
            try {
                Method getMethod = cls.getMethod(getMethodName);
                fieldValve = getMethod.invoke(u);
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                e.printStackTrace();
            }
            //4.3 拼装SQL
            if (fieldValve == null || (fieldValve instanceof Integer && (Integer)fieldValve == 0)) continue;
            sb.append("and").append(columnName);
            if (fieldValve instanceof String) {
                if (((String) fieldValve).contains(",")) {
                    String[] strings = ((String) fieldValve).split(",");
                    sb.append(" in (");
                    for (String v : strings) {
                        sb.append("'").append(v).append("'").append(",");
                    }
                    sb.deleteCharAt(sb.length()-1).append(")");
                }else {
                    sb.append(" = ").append("'").append(fieldValve).append("'");
                }
            }
            sb.append(" = ").append(fieldValve);
        }

        return sb.toString();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值