此实例为,使用Spring切面,将接口的部分方法替换为其他实例的同名同参方法
每一个接口类建立对应一个RPC Client的使用,业务场景为对特定标识用户,使用新的RPC Client的部分方法。
AOP 代理
Spring AOP 默认将标准
JDK动态代理
用于 AOP 代理。这使得可以代理任何接口(或一组接口)。
Spring AOP 也可以使用
CGLIB
代理。这对于代理类而不是接口是必需的。如果业务对象未实现接口,则默认情况下使用
CGLIB
。最好的做法是对接口进行编程,而不是对类进行编程。业务类通常将实现一个或多个业务接口。在某些情况下(可能极少发生),您需要建议未在接口上声明的方法,或者需要将代理对象作为具体类型传递给方法,则可以使用强制使用CGLIB。
重要的是要掌握 Spring AOP 是基于“代理”的事实。有关此实现细节实际含义的彻底检查,请参见了解AOP代理。
设计思路
- 使用Spring Aspcct Around切面,切点为原接口的所有方法
- 在对应配置的方法,对特定标识用户替换为新接口
- 通过新接口的实例及方法,动态代理生成结果
代码实现
将
ShareService
的
batchGetShare()
及
listShare()
,对特定标识用户替换为
ShareProxyService
的实现
@Aspect
@Component
public class ShareProxyAspect {
private static Logger logger = LoggerFactory.getLogger(ShareProxyAspect.class);
@Autowired
private ShareProxyService shareProxyService;
@Autowired
private IdentityService identityService;
private static List<String> proxyMethodName = Lists.newArrayList(
"batchGetShare",
"listShare"
);
/**
* 拦截ShareService
*/
@Around(value = "execution(* com.example.api.ShareService.*(..))")
public Object shareProxy(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
Object[] args = proceedingJoinPoint.getArgs();
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = signature.getMethod();
String methodName = method.getName();
if (proxyMethodName.contains(methodName)) {
String uid = UserContext.getLoginUid();
if (identityService.getIdentityFlag(uid)) {
Method[] methods = shareProxyService.getClass().getMethods();
Method proxyMethod = getMethodByName(methods, methodName);
logger.info("ShareProxyAspect invoke,uid={} method={} args={}", uid, methodName, args);
return proxyMethod.invoke(moneyShareProxyService, args);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
}
private Method getMethodByName(Method[] methods, String methodName) {
for (Method method : methods) {
if (method.getName().equals(methodName)) {
return method;
}
}
// ShareProxyService拓展方法保证不会出现空
return null;
}
}