天天看点

Spring AOP 切换接口实现类

此实例为,使用Spring切面,将接口的部分方法替换为其他实例的同名同参方法

每一个接口类建立对应一个RPC Client的使用,业务场景为对特定标识用户,使用新的RPC Client的部分方法。

AOP 代理

Spring AOP 默认将标准

JDK动态代理

用于 AOP 代理。这使得可以代理任何接口(或一组接口)。

Spring AOP 也可以使用

CGLIB

代理。这对于代理类而不是接口是必需的。如果业务对象未实现接口,则默认情况下使用

CGLIB

。最好的做法是对接口进行编程,而不是对类进行编程。业务类通常将实现一个或多个业务接口。在某些情况下(可能极少发生),您需要建议未在接口上声明的方法,或者需要将代理对象作为具体类型传递给方法,则可以使用强制使用CGLIB。

重要的是要掌握 Spring AOP 是基于“代理”的事实。有关此实现细节实际含义的彻底检查,请参见了解AOP代理。

设计思路

  1. 使用Spring Aspcct Around切面,切点为原接口的所有方法
  2. 在对应配置的方法,对特定标识用户替换为新接口
  3. 通过新接口的实例及方法,动态代理生成结果

代码实现

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