Proceedingjoinpoint 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。
这里详细介绍JointPoint的方法,这部分很重要是coding核心参考部分。开始之前我们思考一下,我们到底需要获取切入点的那些信息。
- 切入点的方法名字及其参数
- 切入点方法标注的注解对象(通过该对象可以获取注解信息)
- 切入点目标对象(可以通过反射获取对象的类名,属性和方法名)
注:有一点非常重要,Spring的AOP只能支持到方法级别的切入。换句话说,切入点只能是某个方法。
Object targetObj =joinPoint.getTarget();
# 可以发挥反射的功能获取关于类的任何信息,例如获取类名如下
String className = joinPoint.getTarget().getClass().getName();
getSignature());是获取到这样的信息 :修饰符+ 包名+组件名(类名) +方法名
这里我只需要方法名
String methodName = joinPoint.getSignature().getName()
方法1:xxxxxx是注解名字
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
xxxxxx annoObj= method.getAnnotation(xxxxxx.class);
}
return null;
方法2:上面我们已经知道了方法名和类的对象,通过反射可以获取类的内部任何信息。
// 切面所在类
Object target = joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
Method method = null;
for (Method m : target.getClass().getMethods()) {
if (m.getName().equals(methodName)) {
method = m;
// xxxxxx annoObj= method.getAnnotation(xxxxxx.class);同上
break;
}
}
这里返回的是切入点方法的参数列表
Object[] args = joinPoint.getArgs();
眼见为实,测试一遍可以理解更深刻
1、自定义注解类:
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLog
{
/**
* 模块
*/
public String title() default "";
/**
* 日志记录service实现
* @return
*/
public String logService() default "operLogServiceImpl";
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否追踪用户操作
* @return
*/
public boolean isTrack() default true;
}
2、切面类(使用JoinPoint)
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Aspect
@Component
public class DemoAspect {
//切入点:aopdemo报下所有对象的save方法
@Pointcut("execution(public * com.kouryoushine.aop.test.*.save*(..))")
public void save(){
}
/**
* 需要在update操作前后分别获取更新前后的值
* @param
* @return
*/
@AfterReturning("save()")
public void afterReturn(JoinPoint joinPoint) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.获取切入点所在目标对象
Object targetObj =joinPoint.getTarget();
System.out.println(targetObj.getClass().getName());
// 2.获取切入点方法的名字
String methodName = joinPoint.getSignature().getName();
System.out.println("切入方法名字:"+methodName);
// 3. 获取方法上的注解
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
ApiLog apiLog= method.getAnnotation(ApiLog.class);
System.out.println("切入方法注解的title:"+apiLog.title());
}
//4. 获取方法的参数
Object[] args = joinPoint.getArgs();
for(Object o :args){
System.out.println("切入方法的参数:"+o);
}
}
}
3、服务类
@Service
public class TestServcie {
@ApiLog(title = "注解的标题",isSaveRequestData = false)
public void save(String parm1,int parm2){
System.out.println("执行目标对象的方法"+parm1+parm2);
}
public void update(){
System.out.println("没有注解的方法,不会被拦截");
}
}
4、测试方法
@Autowired
TestServcie testServcie;
@Test
void test6() throws Exception{
testServcie.save("参数1字符串",33);
}
5、测试结果
com.kouryoushine.aop.test.TestServcie
切入方法名字:save
切入方法注解的title:注解的标题
切入方法的参数:参数1字符串
切入方法的参数:33
ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。
1、自定义注解类:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PromotionApiMonitor {
/**
* 媒体类型
* @return
*/
MediaKeyValue mediaCode();
/**
*
* @return
*/
TaskTypeEnum taskType();
}
2、2切面类(使用 ProceedingJoinPoint)
@Slf4j
@Component
@Aspect
public class PromotionMonitorAspect {
/**
* 拦截入口下所有的方法
*/
@Pointcut("execution(* com.leiting.ads.controller.promote.media.gdt..*.*(..)) " +
"|| execution(* com.leiting.ads.controller.promote.media.toutiao..*.*(..))")
public void pointCut() {
}
/**
* 拦截处理
*
* @param point point 信息
* @return result
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature sign = (MethodSignature) point.getSignature();
Method method = sign.getMethod();
PromotionApiMonitor annotation = method.getAnnotation(PromotionApiMonitor.class);
//获得注解运行成功后返回值(详情可参考下方设定)
Object processResult = point.proceed();
String processResultStr = processResult.toString();
JsonObject respJson=new JsonParser().parse(processResultStr).getAsJsonObject();
int status = respJson.get("status").getAsInt();
if(status == 0){
return processResult;
}
MediaKeyValue mediaKeyValue = annotation.mediaCode();
TaskTypeEnum taskTypeEnum = annotation.taskType();
JsonObject message = respJson.get("message").getAsJsonObject();
JsonElement resultJsonElement = message.get("result");
JSONArray jsonArray = new JSONArray();
if(resultJsonElement != null) {
String resultStr = resultJsonElement.toString();
jsonArray = JSONObject.parseArray(resultStr);
}else {
JSONObject jsonObject = JSONObject.parseObject(message.toString());
jsonArray.add(jsonObject);
}
}
}
@PostMapping("modify_budget")
@PromotionApiMonitor(mediaCode = MediaKeyValue.JRTT_MEDIA,taskType = TaskTypeEnum.MODIFY_ACCOUNT_BUGET)
public Object modifyBudgetBatch(@RequestBody List accountBudgetModifyVoList) {
return touTiaoAccountPromoteService.modifyBudgetBatch(accountBudgetModifyVoList);
}
//return返回类型是:
{
"status": 0,
"message": "数据拉取完成"
}
类似数据包