从JDK5最先,Java增加对注解的支撑,注解能够在编译,类加载和运转时被读取,并实行响应一些界说好的处置惩罚。经由过程注解能够在不转变原有代码和逻辑的情况下举行一些其他的增补操纵。

体系注解

元注解

在java中体系为我们预置了一部分注解,我们能够经由过程这些注解来界说其他注解的作用和有效局限等特征。

@Target

@Target用于申明Annotation所润饰的对象局限,所能润饰的局限都被界说在罗列类ElementType中。

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 {
}

从注解界说能够看到该注解用在注解界说上。

@Documented 用于形貌别的范例的 annotation 应该被作为被标注的顺序成员的大众 API,因
此能够被如javadoc之类的东西文档化。然则实际运用并不多,有其他更好的替换。

@Inherited

@Inherited是一个符号注解,@Inherited透露表现被其标注的范例是被继续的。若是一
个运用了@Inherited 润饰的 annotation 范例被用于一个 class,则这个 annotation 将被用于该class 的子类。

简朴来讲就是在子类中若是想要猎取父类被那些注解润饰,那末子类能拿到的仅仅是被@Inherited标注过得注解。而其他没有运用 @Inherited的注解是没法再子类猎取的。

规范注解

上面引见的几种元注解是在我们举行自界说注解的时刻会用到的,而下面我们引见几种日常平凡营业开辟会常常运用的注解。

@Deprecated

@Deprecated用来形貌在以后体系中已被烧毁不引荐运用的类或要领等。

@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自带的注解以外,我们也能够经由过程自界说注解来对我们的代码功用举行搜检和加强。

Last modification:March 25, 2020
如果觉得我的文章对你有用,请随意赞赏