使用 AspectJ 更改 object 属性

Use AspectJ to change object properties

是否可以拦截 object 方法调用并在那一刻修改那些 object 属性?

到目前为止我有什么

@Pointcut("execution(* java.net.HttpURLConnection.setRequestProperty(..))")
public void connectMethodCall() {
}

@Around("connectMethodCall()")
public Object onGetUrlConnection(ProceedingJoinPoint pjp) throws Throwable {
    HttpURLConnection connection = (HttpURLConnection) pjp.proceed();
    connection.setRequestProperty("header key", "header value");
    return pjp.proceed();
}

我想在此示例中将连接 headers 和 return object 设置为执行点。编织是在编译时完成的。我尝试在此之后记录 headers 但没有 headers 我在@Around 建议中设置的。也不会抛出任何错误。

我已经做到了

@Pointcut("call(* java.net.URL.openConnection())")
public void connectMethodCall() {
}

@Around("connectMethodCall()")
public Object onGetUrlConnection(ProceedingJoinPoint pjp) throws Throwable {
    HttpURLConnection connection = (HttpURLConnection) pjp.proceed();
    connection.setRequestProperty("From", "user@example.com");
    return connection;
}

并在此处使用它来设置 headers

public static void main(String[] args) {
    HttpURLConnection urlConnection;
    String result;
    try {
        urlConnection = (HttpURLConnection) (new URL("http://www.google.com").openConnection());
        urlConnection.setDoOutput(true);
        urlConnection.setRequestProperty("Content-Type", "application/json");
        urlConnection.setRequestProperty("Accept", "application/json");
        urlConnection.setRequestMethod("POST");
        urlConnection.setConnectTimeout(10000);
        urlConnection.connect();
        OutputStream outputStream = urlConnection.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
        writer.write("test");
        writer.close();
        outputStream.close();
        int responseCode = urlConnection.getResponseCode();
        if (responseCode == HttpsURLConnection.HTTP_OK) {
            //Read
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));

            String line;
            StringBuilder sb = new StringBuilder();

            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line);
            }

            bufferedReader.close();
            result = sb.toString();

        } else {
            result = "false : " + responseCode;
        }
        System.out.println(result);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

相关代码的问题是我截获了 returns void 和 pjp.proceed() 实际上 returns null 的方法调用。 (仍然不确定是否有办法从 void 方法切入点中挖掘出调用 object?)。理想情况下我会拦截 urlConnection.connect();并从切入点获取 urlConnection object。有办法吗?

关于如何获取 target 对象实例的后续问题的答案很简单,如果我理解正确的话:只需使用 target()参数绑定。快速浏览 AspectJ documentation would have showed you that, e.g. the part about pointcut parameters。我相信这比在这里提问要容易得多,也更省时(也考虑到必须等待关于 SO 的答案)。但无论如何,这是开发人员互相帮助的地方。所以我们开始吧:

尽管您的 MVCE 示例代码没有对 Google API 做任何有意义的事情,我们只添加一行诊断输出以验证方面确实添加了一个请求参数:

// (...)
      urlConnection.connect();

      // Just in order to check if the property has indeed been set in the aspect
      System.out.println(urlConnection.getRequestProperty("From"));

      OutputStream outputStream = urlConnection.getOutputStream();
// (...)

那就用这个方面:

package de.scrum_master.aspect;

import java.net.HttpURLConnection;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MyAspect {
  @Pointcut("call(* java.net.HttpURLConnection.connect()) && target(connection)")
  public void connectMethodCall(HttpURLConnection connection) {}

  @Around("connectMethodCall(connection)")
  public Object onGetUrlConnection(ProceedingJoinPoint pjp, HttpURLConnection connection) throws Throwable {
    connection.setRequestProperty("From", "user@example.com");
    return pjp.proceed();
  }
}

或者更紧凑一点,如果你不需要重复使用点,因为你只在一个建议中使用它:

@Aspect
public class MyAspect {
  @Around("call(* java.net.HttpURLConnection.connect()) && target(connection)")
  public Object onGetUrlConnection(ProceedingJoinPoint pjp, HttpURLConnection connection) throws Throwable {
    connection.setRequestProperty("From", "user@example.com");
    return pjp.proceed();
  }
}

控制台日志为:

user@example.com
false : 405