浅析@SneakyThrows

简介: 在日常的开发中,相信你一定使用过Lombok,它是一款开源的可用于Java平台的代码生成库。我们在定义JavaBean的时候,会使用IDE自动生成构造方法、getter、setter、equals、hashCode、toString等方法,一旦类的属性有修改就要重新生成,通过使用Lambok的简单注解来精简代码就能达到消除冗长代码的目的。

前言

image.png
在日常的开发中,相信你一定使用过Lombok,它是一款开源的可用于Java平台的代码生成库。我们在定义JavaBean的时候,会使用IDE自动生成构造方法、getter、setter、equals、hashCode、toString等方法,一旦类的属性有修改就要重新生成,通过使用Lambok的简单注解来精简代码就能达到消除冗长代码的目的。

Lombok原理

Lombok基于JSR269 Pluggable Annotation Processing API(插入式注解处理器),在jdk6以后提供了一种方式,可以让我们修改编译过程,在编译期融入我们自己编译逻辑,也就是插入式注解处理器,它提供了一组编译器的插入式注解处理器的标准API在编译期间对注解进行处理。解决了APT(注解处理工具)没有集成到javac中,只能在运行时通过反射来获取注解值,运行时代码效率降低等问题,而javac的编译过程如下:

  • 解析与填充符号表过程:读取命令行上指定的所有源文件,将其解析为语法树,然后将所有外部可见的定义输入到编译器的符号表中。
  • 插入式注解处理器的注解处理过程:调用所有适当的注解处理器。如果任何注解处理器生成任何新的源文件或类文件,则将重新启动编译,直到没有新文件创建为止。
  • 分析与字节码生成过程:分析器创建的语法树将被分析并转换为类文件。在分析过程中,可能会找到对其他类的引用。编译器将检查这些类的源和类路径。如果在源路径上找到它们,则这些文件也将被编译,尽管它们将不受注解处理。

image.png
javac将源代码分析生成抽象语法树AST,Lombok注解处理器对抽象语法树进行处理,Lombok解析handler输出修改后的AST,javac将修改后的AST解析和生成,生成字节码文件。

捕获异常

在日常开发中我们经常处理非运行时异常的时候,它们从类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能正常编译通过。例如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。我们通过显示的异常捕获,或者忽略,或者对外抛出。

public static void main(String[] args) {
    try {
        Class clz = Class.forName("com.demo.duansg.SneakyThrows");
        System.out.println("success");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } 
}
public static void main(String[] args) {
    try {
        Thread.sleep(3000);
        System.out.println("success");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

@SneakyThrows

sneaky:鬼鬼祟祟的;暗中的,卑鄙的。看字面意思就是暗地里鬼鬼祟祟的抛出的意思。其功能其实也是如此,它的作用就是减少程序的异常捕获。往往在开发中,大部分情况下的异常,都是一路抛到头,所以有些时候的大家处理Exception的常见手段就是外面包一层RuntimeException,如下所示

try{
    //......
} catch(Exception e) {
    log.error(message, e)
    throw new RuntimeException(e);
}
而Lombok的@SneakyThrows就是为了消除这样的模板代码。使用注解后不需要在显示的对Exception进行处理
@SneakyThrows
protected T newInstance() {
     Class<?> clazz = Class.forName("com.demo.duansg.SneakyThrows");
     return (T) clazz.newInstance();
}
通过编译后,真正生成的代码,类似于Lombok的@Data注解,在编译时就已经把处理的代码嵌入到了class内。
protected T newInstance() {
    try {
        Class<?> clazz = Class.forName("com.demo.duansg.SneakyThrows");
        return clazz.newInstance();
    } catch (Throwable var2) {
        throw var2;
    }
}

@SneakyThrows定义

它可用于方法和构造函数上,其value是Class<? extends Throwable>[],是Throwable异常的子类集合,如下所示:

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.SOURCE)
public @interface SneakyThrows {
    /** @return The exception type(s) you want to sneakily throw onward. */
    Class<? extends Throwable>[] value() default {Throwable.class};
}

@SneakyThrows自定义处理异常

假如在一个方法中有两个异常,但是我们需要手动处理其中一个的时候,也可以根据你的自定义需要,使用Lombok的@SneakyThrows自定义处理的异常,如下所示:

@SneakyThrows({ClassNotFoundException.class,})
protected void newInstance() {
    Class<?> clazz = Class.forName("com.demo.duansg.SneakyThrows");
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

@SneakyThrows原理

/**
    * Throws any throwable 'sneakily' - you don't need to catch it, nor declare that you throw it onwards.
    * The exception is still thrown - javac will just stop whining about it.
    * <p>
    * Example usage:
    * <pre>public void run() {
    *     throw sneakyThrow(new IOException("You don't need to catch me!"));
    * }</pre>
    * <p>
    * NB: The exception is not wrapped, ignored, swallowed, or redefined. The JVM actually does not know or care
    * about the concept of a 'checked exception'. All this method does is hide the act of throwing a checked exception
    * from the java compiler.
    * <p>
    * Note that this method has a return type of {@code RuntimeException}; it is advised you always call this
    * method as argument to the {@code throw} statement to avoid compiler errors regarding no return
    * statement and similar problems. This method won't of course return an actual {@code RuntimeException} -
    * it never returns, it always throws the provided exception.
    * 
    * @param t The throwable to throw without requiring you to catch its type.
    * @return A dummy RuntimeException; this method never returns normally, it <em>always</em> throws an exception!
*/
public static RuntimeException sneakyThrow(Throwable t) {
  if (t == null) throw new NullPointerException("t");
    return Lombok.<RuntimeException>sneakyThrow0(t);
}

@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
  throw (T)t;
}

从Lombok的核心方法不难看出,它是利用泛型将传入的Throwable强转为RuntimeException。虽然事实上我们不是RuntimeException。但是JVM并不关心这个,这样写只是为了骗过javac编译器,在代码的运行阶段,会对泛型进行擦除。

小结

从此上看来,使用@SneakyThrows时,如果你的代码遇到了异常,那么在编码阶段你是无法感知的。而如果你的代码里可能并没有异常,但是你却声明了该注解,反而会给了解这个注解的开发者带来不必要的误会。所有此注解还是谨慎使用为好,其实它从发布到如今一直以来都备受开发者的争议,有人觉得其使用方式很方便,但是也有人觉得是这是一种对语言逻辑的破坏,反而笔者觉得,约定是大于规范的,开发团队达成一致,Lombok是可以在一定程度上提升团队代码可读性和团队开发效率的。

目录
相关文章
|
7月前
|
Java 应用服务中间件 nginx
【异常解决】java程序连接MinIO报错The request signature we calculated does not match the signature you provided.
【异常解决】java程序连接MinIO报错The request signature we calculated does not match the signature you provided.
705 0
|
Java 关系型数据库 MySQL
阿里巴巴Java开发手册简介(终极版、华山版、泰山版)(附下载地址)
阿里巴巴Java开发手册简介(终极版、华山版、泰山版)(附下载地址)
4664 0
|
Java 网络架构 容器
面向整洁对象的分层架构COLA 4.0
COLA 是 Clean Object-Oriented and Layered Architecture的缩写,代表“面向整洁对象的分层架构”。 目前COLA已经发展到COLA 4.0。 COLA分为两个部分,COLA架构和COLA组件。
面向整洁对象的分层架构COLA 4.0
|
微服务 测试技术 Java
阿里技术专家详解 DDD 系列- Domain Primitive
关于DDD的一系列文章,希望能继续在总结前人的基础上发扬光大DDD的思想,但是通过一套我认为合理的代码结构、框架和约束,来降低DDD的实践门槛,提升代码质量、可测试性、安全性、健壮性。
58504 16
阿里技术专家详解 DDD 系列- Domain Primitive
remote: HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2
remote: HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2
3694 0
|
9月前
|
算法 关系型数据库 MySQL
MySQL事务隔离实现原理,多版本并发控制MVCC
MySQL事务隔离实现原理,多版本并发控制MVCC
143 0
|
9月前
|
SQL 关系型数据库 MySQL
【MySQL进阶-04】深入理解mysql事务本质(超级详解)
【MySQL进阶-04】深入理解mysql事务本质(超级详解)
94 1
|
6月前
|
SQL XML Java
MyBatis-plus超神用法--一文带你玩转MP
MyBatis-plus超神用法--一文带你玩转MP
186 0
|
8月前
|
Java 数据库连接 数据库
springboot 如何对数据库密码进行加密
在Spring Boot中,你可以通过以下步骤对数据库密码进行加密: 1. 引入必要的依赖: 在 `pom.xml` 文件中添加以下依赖,以使用Spring提供的加密功能: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 创建加密配置类: 在你的项目中创建一个配置类,用于配置密码加密的设置。例如,创建一个名为 `EncryptionConfig` 的
2240 0