Используйте AspectJ для изменения свойств объекта - PullRequest
0 голосов
/ 26 декабря 2018

Можно ли перехватить вызов метода объекта и изменить эти свойства объекта в этот момент?

Что у меня есть

@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();
}

Я хочу на этом примере установить заголовки соединения ивернуть объект в точку исполнения.Плетение выполняется во время компиляции.После этого я пытаюсь зарегистрировать заголовки, но в @Around advice нет никаких заголовков.Также не выдается никаких ошибок.

Ответы [ 2 ]

0 голосов
/ 19 января 2019

Ответ на ваш следующий вопрос о том, как получить экземпляр объекта target , прост, если я правильно понимаю вопрос: просто используйте привязку параметра target().Быстрый просмотр документации AspectJ показал бы вам, например, часть о параметрах pointcut .Я действительно считаю, что это намного проще и требует меньше времени (в том числе в связи с необходимостью ждать ответов на SO), чем задавать вопросы здесь.Но в любом случае, это место, где разработчики помогают друг другу.Итак, начнем:

Несмотря на тот факт, что ваш пример кода MVCE ничего не делает с API Google, давайте просто добавим одну строку диагностического вывода, чтобы убедиться, что аспект действительно добавил параметр запроса.:

// (...)
      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
0 голосов
/ 26 декабря 2018

Мне удалось сделать это следующим образом

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

и использовать его здесь для установки заголовков

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

Проблема с рассматриваемым кодом заключалась в том, что я перехватил вызов метода, которыйвозвращает void и pjp.proceed () фактически возвращает ноль в этом случае.(все еще не уверен, есть ли способ выкопать вызывающий объект из pointcut void метода?).В идеале я бы перехватил urlConnection.connect ();и получить объект urlConnection из pointcut.Есть ли способ сделать это?

...