关于注解咱们须要知道的

从JDK5开始,Java增长对注解的支持,注解能够在编译,类加载和运行时被读取,并执行相应一些定义好的处理。经过注解能够在不改变原有代码和逻辑的状况下进行一些其余的补充操做。java

系统注解

元注解

在java中系统为咱们预置了一部分注解,咱们能够经过这些注解来定义其余注解的做用和有效范围等特性。mysql

@Target

@Target用于说明Annotation所修饰的对象范围,所能修饰的范围都被定义在枚举类ElementType中。sql

public enum ElementType {
    TYPE,//表示能够用于类,接口,注解或者枚举定义中
    FIELD,//字段
    METHOD,//方法(不包括构造方法)
    PARAMETER,//方法的参数
    CONSTRUCTOR,//构造方法上
    LOCAL_VARIABLE,//局部变量
    ANNOTATION_TYPE,//只能用在注解上
    PACKAGE,//做用包上 package-info.java 
    TYPE_PARAMETER,//表示注解能写在类型变量(泛型参数)的声明语句中如 List<Integer> list = new @Save ArrayList<>();
    TYPE_USE //表示注解能写在使用类型的任何语句中(声明语句、泛型和强制转换语句中的类型)。
}
复制代码
TYPE_PARAMETER
@Target(ElementType.TYPE_PARAMETER)
public @interface Save {
}

public class Test<@Save T> {
    List<Integer> list = new @Save ArrayList<>();//仅用于展现能够用到的地方
}
复制代码

@Retention

Retention 定义了该Annotation被保留的时间长短:表示须要在什么级别保存注解信息,用于描述注解的生命周期(即被描述的注解在什么范围内有效),取值被定义在枚举类RetentionPolicy中:数据库

public enum RetentionPolicy {
    SOURCE,//表示在源代码时有效,编译后的文件没有该注解,通常该类注解仅用于标识如@SuppressWarnings

    CLASS, //默认行为 自定义注解若是没有显示的声明则默认为该行为 在编译时不会被抛弃,可是会被虚拟机抛弃

    RUNTIME //保留到运行时,能够经过反射来获取 通常该类注解会影响系统的运行
}
复制代码

@Documented

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
复制代码

从注解定义能够看到该注解用在注解定义上。bash

@Documented 用于描述其它类型的 annotation 应该被做为被标注的程序成员的公共 API,因 此能够被如javadoc之类的工具文档化。可是实际使用并很少,有其余更好的替代。框架

@Inherited

@Inherited是一个标记注解,@Inherited表示被其标注的类型是被继承的。若是一 个使用了@Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该class 的子类。ide

简单来讲就是在子类中若是想要获取父类被那些注解修饰,那么子类能拿到的仅仅是被@Inherited标注过得注解。而其余没有使用 @Inherited的注解是没法再子类获取的。工具

标准注解

上面介绍的几种元注解是在咱们进行自定义注解的时候会用到的,而下面咱们介绍几种平时业务开发会常常使用的注解。ui

@Deprecated

@Deprecated用来描述在当前系统中已经被废弃不推荐使用的类或方法等。this

@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
复制代码

若是咱们使用了被@Deprecated标注的类或方法等,在进行编译的时候会显示相应的提示信息。

@Override

@Override是咱们使用很频繁的一个注解,因为重写的操做仅存在于方法中,因此@Override也只能对方法进行标注。

@Override功能主要是用来校验当前被标注的方法是否为重写方法,平时咱们在继承抽象类或实现接口时都应使用该注解来标注被重写的方法。

@SuppressWarnings

@SuppressWarnings用于可选择的抑制编译器在编译时产生警告信息。

//做用范围
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
复制代码

@SuppressWarnings可选择的值有不少:

  • deprecation:不产生使用过时方法(...)的警告,@SuppressWarnings("deprecation")
  • unchecked:执行了未检查的转换的警告
  • finally:finally语句没法正常完成时的警告
  • ...
  • all:任意类型的警告

自定义注解与解析

定义注解

自定义一个注解及其简单,使用@interface关键字便可完成。同时咱们须要肯定咱们定义的注解使用范围和其具体用途,根据此来肯定使用元注解的哪些参数来修饰咱们定义的注解。

这里咱们定义一个@Log注解用于在对数据库进行增删改时插入一条日志进行记录。因为该注解须要在运行时对数据库进行修改,因此很明显做用有效期为RUNTIME,做用范围可使方法也可使类。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Log {
    String value();//value的值为本次操做类型:insert,delete等
}
复制代码

注解解析

定义接口并实现:

public interface UserService {
    @Log(OperationType.INSERT)
    void save(User user);
}
复制代码
public class LogProxyExt implements InvocationHandler {

    private Object obj;

    static Connection connection = null;

    public static Connection getConnection() throws ClassNotFoundException, SQLException {
        if(connection == null){
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/fuxi?serverTimezone=UTC", "root", "root");
        }
        return connection;
    }

    public LogProxyExt(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //查找方法上是否存在该注解
        Log annotation = method.getAnnotation(Log.class);
        if(annotation != null){
            User user = (User) args[0];
            if(user!=null && StringUtils.isNotBlank(user.getName())){
                connection = getConnection();
                PreparedStatement statement = connection.prepareStatement("insert into log(`log_detail`,`operation`) values(?,?)");
                statement.setString(1, user.getName());
                statement.setString(2, annotation.value());
                statement.executeUpdate();
            }
        }

        //查找类上是否存在该注解
        proxy.getClass().getAnnotation(Log.class);
        //对类上的注解进行解析
        //todo
        Object invoke = method.invoke(obj, args);

        return invoke;
    }

    public static void main(String[] args) {
        UserService operat = new UserServiceImpl();
        LogProxyExt ext = new LogProxyExt(operat);
        //这里不能是具体的类,必须是接口 不然会抛出类转换异常
        UserService proxy = (UserService) Proxy.newProxyInstance(operat.getClass().getClassLoader(), operat.getClass().getInterfaces(), ext);
        User user = new User();
        user.setName("lisi");
        proxy.save(user);
    }
}
复制代码

上述解析经过JDK动态代理进行实现,对UserService接口进行代理,保证在UserService中任意位置使用@Log注解都能完成插入日志操做。实际使用能够配合Spring Aop或者拦截器之类的对全局请求进行处理。

小结

注解是一个颇有用的特性,在现有的框架中大多数都已经支持或正在支持注解。使用注解能够大大提高咱们平时的开发速度,保证代码的简洁性。除了框架和JDK自带的注解以外,咱们也能够经过自定义注解来对咱们的代码功能进行检查和加强。

相关文章
相关标签/搜索