前言
spring aop提供了丰富的切面功能,包括了环绕通知、提前通知、结束通知等,能够满足不同的业务场景,有时候需要在方法成功执行完成之后才提供切面通知。注解@AfterReturning 就能够很好的实现这个功能。
具体实现
- 首先需要一个AOP的切面方法。
import java.lang.reflect.Method;
import java.util.Date;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Aspect
@Component
@Lazy(value = false)
public class UserNotifyAop {
@Autowired
IUserNotify iUserNotify;
@Autowired
UserNotifyProducer userNotifyProducer;
@Autowired
UserAuthRepository userAuthRepository;
@AfterReturning(value = "@annotation(userNotifyAnnotation)",returning = "userAuth")
public void after(JoinPoint joinPoint, UserNotifyAnnotation userNotifyAnnotation,Object userAuth) throws Throwable {
Method method = MethodSignature.class.cast(joinPoint.getSignature()).getMethod();
Object[] in = joinPoint.getArgs();
String authType = parseKey(userNotifyAnnotation.authType(), method, in);
String authValue = parseKey(userNotifyAnnotation.authValue(), method, in);
String uniqueid = null;
if (!StringUtils.isEmpty(userNotifyAnnotation.uniqueid())) {
uniqueid = parseKey(userNotifyAnnotation.uniqueid(), method, in);
}
UserNotifyEnum notifyEnum = userNotifyAnnotation.userNotifyEnum();
if(notifyEnum == UserNotifyEnum.REGISTER){
UserNotifyDTO<RegisterInfo> userNotifyDTO = new UserNotifyDTO<RegisterInfo>();
userNotifyDTO.setUserId(((UserAuth)userAuth).getUserId());
userNotifyDTO.setNotifyTime(new Date());
userNotifyDTO.setUserNotifyEnum(notifyEnum);
RegisterInfo registerInfo = new RegisterInfo();
registerInfo.setMobile(authValue);
registerInfo.setRegisteTime(((UserAuth)userAuth).getCreatedTs());
registerInfo.setUniqueid(uniqueid);
userNotifyDTO.setData(registerInfo);
userNotifyProducer.send(userNotifyDTO);
}
if(notifyEnum == UserNotifyEnum.BIND){
UserAuth userAuth1 = userAuthRepository.findByTypeAndValue(authType,authValue);
UserNotifyDTO<BindInfo> userNotifyDTO = new UserNotifyDTO<BindInfo>();
userNotifyDTO.setUserId((userAuth1.getUserId()));
userNotifyDTO.setNotifyTime(new Date());
userNotifyDTO.setUserNotifyEnum(notifyEnum);
BindInfo bindInfo = new BindInfo();
bindInfo.setBindType(authType);
bindInfo.setBindValue(authValue);
userNotifyDTO.setData(bindInfo);
userNotifyProducer.send(userNotifyDTO);
}
}
/**
* 解析SpEL表达式
*
* @param key SpEL表达式
* @param method 反射得到的方法
* @param args 反射得到的方法参数
* @return 解析后SpEL表达式对应的值
*/
private String parseKey(String key, Method method, Object[] args) {
// 获取被拦截方法参数名列表(使用Spring支持类库)
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paraNameArr = u.getParameterNames(method);
// 使用SpEL进行key的解析
ExpressionParser parser = new SpelExpressionParser();
// SpEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
// 把方法参数放入SpEL上下文中
for (int i = 0; i < paraNameArr.length; i++) {
// 使用setVariable方法来注册自定义变量
context.setVariable(paraNameArr[i], args[i]);
}
return parser.parseExpression(key).getValue(context, String.class);
}
@AfterReturning 能够实现获取方法返回值的功能,通过自定义的注解,aop直接切加上自定义通知注解的方法,然后异步发动通知消息。
启动私有方法parseKey 是通过Spel 表达式的方法来解析注解参数,获取具体值。