代理
概述
在指南 - 声明式小节中已经介绍了基于JDK
动态代理的声明式使用,如果更偏好于对非接口类型的代理使用,可以额外引入CGLIB
依赖。例如:
xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
Chance
本身是零依赖设计,但是已经基于provided
类型的scope
引入了CGLIB
并且为非接口类型代理提供了实现。
使用详解
在同时引入CGLIB
依赖和Chance
依赖到前提下,添加一个非接口类ChanceApi
:
java
// 应用在类上的注解
@ChanceFor(maxRetryTimes = 0)
public class ChanceApi {
public String useTypeAnnotation() {
return "useTypeAnnotation";
}
// 应用在方法上的注解
@ChanceFor(maxRetryTimes = 3, include = {RuntimeException.class}, waits = {
@ChanceFor.WaitFor(type = ChanceFor.WaitType.FIXED, multiplier = 30)
})
public String useMethodAnnotation() {
throw new RuntimeException("Error from 'useMethodAnnotation'");
}
}
生成动态代理并且调用对应的方法:
java
public static void main(String[] args) throws Exception {
ChanceApi chanceApi = Chance.newProxy(ChanceApi.class, new ChanceApi());
String typeResult = chanceApi.useTypeAnnotation();
System.out.println("typeResult: " + typeResult);
try {
chanceApi.useMethodAnnotation();
} catch (Exception e) {
// 这里的异常e的类型为ChanceException - 和JDK动态代理调用的场景有所不同
if (e instanceof ChanceException) {
ChanceException ce = (ChanceException) e;
Attempt<?, ?> lastFailedAttempt = ce.getAttempt();
System.out.println("Retry times: " + lastFailedAttempt.attemptTimes());
lastFailedAttempt.rootCauseNow().printStackTrace();
}
}
}
上面的代码最终输出结果:
shell
typeResult: useTypeAnnotation
Retry times: 3
java.lang.RuntimeException: Error from 'useMethodAnnotation'
// ......省略异常堆栈......
使用CGLIB
生成的代理调用对应的方法如果抛出异常,那么捕获的异常就是抛出的异常实例本身,这一点跟JDK
原生的动态代理有点不同。JDK
动态代理在调用对应的方法时候如果方法内部抛出异常,该异常会被包装为UndeclaredThrowableException
抛出。