Java注解

概念

Java注解用于为Java代码提供元数据,作为元数据,注解不直接影响代码的执行,但也有一些类型的注解实际上可以用于这一目的。

分类

Java注解可以分为2种,第一种是元注解,第二种是自定义注解。

所谓元注解就是用来描述注解的注解,这里的元是由Meta翻译过来的。我们可以把元注解理解为J DK为了让我们自定义注解而提供的一些注解,在Java体系中,元注解只有5个,分别为@Target、@Retention、@Documented、@Inherited以及JDK1.8中新增的@Repeatable,除此之外的所有注解都是基于这5个注解定义出来的,所以也叫自定义注解。自定义注解又可以分为JDK内置注解以及开发者自定义的注解等多种。

五个元注解

1、@Target

用来指定一个注解的使用范围,如是用在方法还是成员变量等。参考该注解的源码可以看到,该注解有一个成员变量 ElementType[] value(); 其中的ElementType表示的是一个枚举类, 所以说该属性可以定义多个ElementType,即可以指定多个范围,如指定类型和方法两个范围,就可以写成如下格式:

Target注解的源码如下图所示:

其中的ElementType如下所示:

2、@Documented

使用该注解会被JavaDoc工具类提取成文档。默认情况下JavaDoc中是不包含注解的,如果定义注解时指定了@Documented,则表明这个注解信息需要包含在JavaDoc中。

3、@Retention

用于描述注解的保留策略,表示在什么级别保存该注解信息。其中有一个RetentionPolicy类型的成员变量用来指定保留策略,RetentionPolicy也是一个枚举。

RetentionPolicy枚举只有3个枚举项:

我们经常看到的RetentionPolicy.RUNTIME ,很多时候是因为要在运行期通过反射读取。

4、@Inherited

用来指定该注解可以被继承。如当我们用@Inherited注解修饰了A类,这时候A的子类B类也会自动具有该注解。

应注意的是,@Inherited只会影响类上的注解,而方法和属性上的注解的继承性是不受@Inherited影响的。而声明在方法、成员变量处的注解,即使该注解没有使用@Inherited标注,默认都是可以继承的,除非子类重写了父类的方法或者父类中的成员变量。

5、@Repeatable

Java8新增的一个元注解,使用该注解来标识允许一个注解在一个元素上使用多次。如果不使用@Repeatable注解修饰自定义注解,那么该自定义注解默认是不能在同一元素上多次使用的。

如我想定义一个NoTXSql注解,这个注解可以在一个方法上用多次,其定义如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 注意这里用的是新定义的注解NoTXSqlGroup
@Repeatable(NoTXSqlGroup.class)
public @interface NoTXSql {

    // 

}



@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface NoTXSqlGroup {
    /**
     * value
     *
     * @return NoTXSql[]
     */
    // 新定义的注解NoTXSqlGroup的value 是数组NoTXSql
    NoTXSql[] value();
}

注解的继承与组合

注解类是不能继承其他类也不能实现其他接口的。但是注解与注解之间是可以建立组合关系的。

注解与反射结合

虽然注解没办法直接影响代码的执行,但是注解结合反射技术,那么可以做的事情的就非常多了。

反射的发生,必然是在运行期,因此@Retention所指定的范围也必然是运行期间,RetentionPolicy必然是RUNTIME。通过反射技术在运行期获取了标注在类和方法上的注解及注解中成员变量的值。

不要过度依赖注解

事务可以分为编程式事务和声明式事务,其中声明式事务就是依赖注解实现的。

声明式事务管理方法允许开发者在配置的帮助下管理事务,而不需要依赖底层API进行硬编码。开发者可以只使用注解或者基于配置的XML来管理事务。

声明式事务的优点

其管理是通过AOP实现的,本质是那个就是在目标方法执行前后进行拦截。在目标方法执行前加入或者创建一个事务,在目标方法执行后,根据实际情况选择提交或者回滚事务。这种方式对代码无侵入性,在方法内只需要编写业务逻辑即可。

声明式事务粒度

声明式事务有一个局限,就是它的最小粒度是作用在方法上的。

如果一个开发者没有注意到一个方法是被事务嵌套的,那么就可能在方法中加入一些如RPC远程调用、消息发送、缓存更新和文件写入等操作。这些操作被包含在事务中就可以有如下2个问题:

(1)、这些操作自身没有办法回滚,就会导致数据不以一致。

(2)、事务中有远程调用就会拉长整个事务,导致本事务的数据库连接一直被占用,如果这样的操作过多,就会导致数据连接池耗尽。

正因为有容易忽视的问题,因此不建议用声明式事务。

声明式事务用不对容易失效

以下场景可能导致事务失效

(1)、@Transactional应用在非public方法上;

(2)、@Transactional注解属性propagation设置错误;

(3)、@Transactional注解属性rollbackFor设置错误;

(4)、在同一个类中调用方法,导致@Transactional失效;

(5)、异常被catch捕获导致@Transactional失效;

(6)、数据库引擎不支持事务。

参考《深入理解Java核心技术》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值