业务需求:需要在方法执行的时候给方法的入参加上相关信息;
自然而然的想到用aop来做方法拦截,然后来改变方法的参数;
实现思路:为了能够灵活使用,想到了使用一个注解来作为切点;被这个注解标注过的方法都会被拦截。局限是在spring框架中使用。
阅读了一些相关文章后实现了这个功能;特地将这个方法记录在csdn中,代码中隐去了一些信息,想运行起来需要做一些调整;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Aspect
@Component
public class ModuleResolverAspect {
/**
* 间隔
*/
private static final String INTERVAL = ":";
@Pointcut("@annotation(com.jiatu.core.annotation.ModuleResolver)")
private void annotationPointCut() {
}
@Around("@annotation(com.jiatu.core.annotation.ModuleResolver)")
public ReturnType around(ProceedingJoinPoint proceedingJoinPoint){
// 获取方法入参中的参数列表
Object[] inParams = proceedingJoinPoint.getArgs();
// printArgs(proceedingJoinPoint);// 打印 传入的参数
try {
// 获取参数列表中的 key
String key = (String) getFieldsName(proceedingJoinPoint).get("key");
// 将key拼接上信息
StringBuilder sb = new StringBuilder();
String module = ApplicationConfigUtil.getConfigByKey("module");
if (StringUtils.isEmpty(module)){
throw new AuthorizationException(ResultCode.PERMISSION_MODULE_ERROR);
}
sb.append(module);
sb.append(INTERVAL).append(key);
String moduleKey = sb.toString();
// 改变入参的值
// System.out.println("拼接结果:"+sb.toString());
setFieldValueByName(proceedingJoinPoint,"key",moduleKey);
// printArgs(proceedingJoinPoint);// 打印 修改的参数
// 继续调用本方法执行
return ReturnType.class.cast(proceedingJoinPoint.proceed(inParams));
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
private static Map<String, Object> getFieldsName(ProceedingJoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String[] parameterNames = pnd.getParameterNames(method);
Map<String, Object> paramMap = new HashMap<>(32);
for (int i = 0; i < parameterNames.length; i++) {
paramMap.put(parameterNames[i], args[i]);
}
return paramMap;
}
/**
* 设置执行方法的参数
* @param joinPoint
* @param value
*/
private static void setFieldValueByName(ProceedingJoinPoint joinPoint,String key,String value) {
Object[] args = joinPoint.getArgs();
ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String[] parameterNames = pnd.getParameterNames(method);
Map<String, Object> paramMap = new HashMap<>(32);
for (int i = 0; i < parameterNames.length; i++) {
if (parameterNames[i].equals(key)){
args[i] = value;
}
}
}
private void printArgs(ProceedingJoinPoint proceedingJoinPoint){
Map<String, Object> fieldsMap = getFieldsName(proceedingJoinPoint);
Set<String> keys = fieldsMap.keySet();
for (String e : keys) {
System.out.println("参数名:"+e);
System.out.println("参数值:"+fieldsMap.get(e));
}
}
}
由于业务需要,需要对工具类使用这样的方法拦截,由于种种限制工具类不能注册到spring中交给spring管理,使用aop的方式就不行了。
特地提一下,Spring AOP的实现原理:
Spring AOP是构建在动态代理基础上的,Spring AOP局限于支持 方法级别的拦截。
Spring AOP 支持两种动态代理模式,一种是基于JDK Proxy的动态代理,还有一种是CGLIB方式的动态代理。默认情况下,实现了接口的类会使用基于JDK 生成代理类,没有实现接口的类,会基于CGLIB生成代理类。
CglibProxy:
public class CglibProxy implements MethodInterceptor {
/**
* 间隔
*/
private static final String INTERVAL = ":";
@Override
public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
// 打印log
/*System.out.println("【增强方法】代理对象正在执行的方法:"+method.getName());
System.out.println("方法名:"+method.getName());
for (Object e:params) {
System.out.println("方法入参:"+e);
}*/
// 获取方法名
ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
String[] parameterNames = pnd.getParameterNames(method);
/*for (String name: parameterNames) {
System.out.println("方法参数名:"+name);
}
System.out.println("方法入参名 array长度:"+parameterNames.length+" 方法入参值array 长度:"+params.length);*/
for (int i = 0; i < parameterNames.length; i++) {
// 获取参数名
String parameterName = parameterNames[i];
// 判断是否是需要进行模块管理的入参值
// 可做配置
if (parameterName.equals("key")){
// 去除对应参数名下的 参数值
String param = (String) params[i];
// 拼接上模块信息
String module = ApplicationConfigUtil.getConfigByKey("module");
if (StringUtils.isEmpty(module)){
throw new AuthorizationException(ResultCode.PERMISSION_MODULE_ERROR);
}
StringBuilder sb = new StringBuilder();
sb.append(module).append(INTERVAL).append(param);
// 将拼接上模块信息的key重新赋值给入参
params[i] = sb.toString();
}
}
Object result = methodProxy.invokeSuper(o, params);
return result;
}
private static Map<String, Object> getFieldsNameValue(){
return null;
}
}
CglibProxyFactory:
public class CglibProxyFactory {
/**
* 无参动态代理创建
* @param clazz
* @return
*/
public static Object creatCglibProxyObj(Class<?> clazz){
Enhancer enhancer = new Enhancer();
// 为加强器指定要代理的业务类(下面生成的代理类指定父类)
enhancer.setSuperclass(clazz);
// 设置回调:对于代理类上所有方法的调用,都会调用CallBack,
enhancer.setCallback(new CglibProxy());
return enhancer.create();
}
/**
* 有参动态代理创建
* @param clazz
* @param arguments
* @return
*/
public static Object creatCglibProxyObj(Class<?> clazz, Object[] arguments){
Enhancer enhancer = new Enhancer();
// 为加强器指定要代理的业务类(下面生成的代理类指定父类)
enhancer.setSuperclass(clazz);
// 设置回调:对于代理类上所有方法的调用,都会调用CallBack,
enhancer.setCallback(new CglibProxy());
// create(Class[] argumentTypes, Object[] arguments)
Class[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentTypes[i] = arguments[i].getClass();
}
return enhancer.create( argumentTypes, arguments);
}
}