使用AspectJ更改对象属性

时间:2018-12-26 07:26:31

标签: java aop aspectj

那时候可以拦截对象方法调用并修改那些对象属性吗?

到目前为止我有什么

@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建议中没有设置标题。也不会引发任何错误。

2 个答案:

答案 0 :(得分:1)

如果我正确理解了问题,那么有关如何获取 target 对象实例的后续问题的答案很简单:只需使用'use strict'; // combination origin-request, origin-response trigger to emulate the S3 // website hosting index document functionality, while using the REST // endpoint for the bucket // https://stackoverflow.com/a/54263794/1695906 const INDEX_DOCUMENT = 'index.html'; // do not prepend a slash to this value const HTTP_REDIRECT_CODE = '302'; // or use 301 or another code if desired const HTTP_REDIRECT_MESSAGE = 'Found'; exports.handler = (event, context, callback) => { const cf = event.Records[0].cf; if(cf.config.eventType === 'origin-request') { // if path ends with '/' then append INDEX_DOCUMENT before sending to S3 if(cf.request.uri.endsWith('/')) { cf.request.uri = cf.request.uri + INDEX_DOCUMENT; } // return control to CloudFront, to send request to S3, whether or not // we modified it; if we did, the modified URI will be requested. return callback(null, cf.request); } else if(cf.config.eventType === 'origin-response') { // is the response 403 or 404? If not, we will return it unchanged. if(cf.response.status.match(/^40[34]$/)) { // it's an error. // we're handling a response, but Lambda@Edge can still see the attributes of the request that generated this response; so, we // check whether this is a page that should be redirected with a trailing slash appended. If it doesn't look like an index // document request, already, and it doesn't end in a slash, and doesn't look like a filename with an extension... we'll try that. // This is essentially what the S3 web site endpoint does if you hit a nonexistent key, so that the browser requests // the index with the correct relative path, except that S3 checks whether it will actually work. We are using heuristics, // rather than checking the bucket, but checking is an alternative. if(!cf.request.uri.endsWith('/' + INDEX_DOCUMENT) && // not a failed request for an index document !cf.request.uri.endsWith('/') && // unlikely, unless this code is modified to pass other things through on the request side !cf.request.uri.match(/[^\/]+\.[^\/]+$/)) // doesn't look like a filename with an extension { // add the original error to the response headers, for reference/troubleshooting cf.response.headers['x-redirect-reason'] = [{ key: 'X-Redirect-Reason', value: cf.response.status + ' ' + cf.response.statusDescription }]; // set the redirect code cf.response.status = HTTP_REDIRECT_CODE; cf.response.statusDescription = HTTP_REDIRECT_MESSAGE; // set the Location header with the modified URI // just append the '/', not the "index.html" -- the next request will trigger // this function again, and it will be added without appearing in the // browser's address bar. cf.response.headers['location'] = [{ key: 'Location', value: cf.request.uri + '/' }]; // not strictly necessary, since browsers don't display it, but remove the response body with the S3 error XML in it cf.response.body = ''; } } // return control to CloudFront, with either the original response, or // the modified response, if we modified it. return callback(null, cf.response); } else // this is not intended as a viewer-side trigger. Throw an exception, visible only in the Lambda CloudWatch logs and a 502 to the browser. { return callback(`Lambda function is incorrectly configured; triggered on '${cf.config.eventType}' but expected 'origin-request' or 'origin-response'`); } }; 参数绑定。快速浏览AspectJ documentation可能会向您显示,例如关于pointcut parameters的部分。我确实相信,与在这里提出问题相比,这样做要容易得多,也省时(也需要等待SO的答案)。但是无论如何,这是开发人员互相帮助的地方。所以我们开始:

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

target()

然后使用此方面:

// (...)
      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();
// (...)

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

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

答案 1 :(得分:0)

我设法做到了这一点

@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()实际上返回null。 (仍然不确定是否有办法从void方法切入点中找出调用对象吗?)。理想情况下,我会拦截urlConnection.connect();。并从切入点获取urlConnection对象。有办法吗?