Spring-AOP load-time weaving on 3rd-party classes

落花浮王杯 提交于 2019-12-08 06:03:46

问题


I wrote an aspect that I'm trying to test with junit. The aspect has an @Around advice on a 3rd party method called setQuery. At compile time it complains: Can't find referenced pointcut setQuery

Here's my aspect:

@Component
@Aspect
public class ElasticsearchQuerySecurityAspect {
    @Around("org.elasticsearch.action.search.SearchRequestBuilder.setQuery() && args(queryBuilder)")
    public void addFilter(final ProceedingJoinPoint pjp, QueryBuilder queryBuilder) throws Throwable {
      Object[] args = pjp.getArgs();

      // Set the filter to use our plugin
      FilterBuilder securityFilter = FilterBuilders.scriptFilter("visibility-filter")
            .lang("native")
            .addParam("visibility-field", "visibility")
            .addParam("parameter", "default");

      // Re-create original query with the filter applied
      QueryBuilder newQuery = QueryBuilders.filteredQuery(queryBuilder,securityFilter);
      log.info("Adding filter to search request");
        // Tell the method to run with the modified parameter
        args[0] = newQuery;
        pjp.proceed(args);
    }
}

Here's my junit test:

@RunWith(SpringJUnit4ClassRunner.class)// NOTE #1
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@EnableLoadTimeWeaving
@ComponentScan
public class ElasticsearchQuerySecurityTest {

  Client client = mock(Client.class);

  @Before
  public void setUp() throws Exception {
  }

  @Test
  public void test() {    
    SearchRequestBuilder s = new SearchRequestBuilder(client);
    QueryBuilder qb = QueryBuilders.queryString("name:foo");
        XContentBuilder builder;
    try {
      builder = XContentFactory.jsonBuilder();
      qb.toXContent(builder, null);
      assertEquals("{\"query_string\":{\"query\":\"name:foo\"}}",builder.string());

      // Call setQuery() which will invoke the security advice and add a filter to the query
      s.setQuery(qb);
      builder = XContentFactory.jsonBuilder().startObject();
      qb.toXContent(builder, null);
      builder.endObject();
      assertEquals("{\"query\": "+
        "{ \"filtered\": "+
        "{ \"query\": "+
        "{ \"query_string\": "+
        "{ \"name:foo\", } }, "+
        "\"filter\": "+
        "{ \"script\": "+
        "{ \"script\": \"visibility-filter\","+
        "\"lang\":\"native\", "+
        "\"params\": "+
        "{ \"visibility-field\":\"visibility\", "+
        "\"parameter\":\"default\" } } } } } }",
        builder.string());
    } catch (IOException e) {
      fail("We threw an I/O exception!");
    }   
  }
}

I also have this aop.xml on the classpath:

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

    <weaver>
        <include within="org.elasticsearch.action.search.*"/>
    </weaver>

    <aspects>
        <aspect name="org.omaas.security.ElasticsearchQuerySecurityAspect"/>
    </aspects>

</aspectj>

I tried an aspect with @Around("execution(public * set*())") and found that it only advised stuff in the current package. How do I get it to be applied to stuff in the 3rd-party package?


回答1:


Spring AOP can only weave into Spring Beans. As your 3rd party target class is not a Spring bean, there is no way to apply an aspect to it. For that purpose you need to use AspectJ which is way more powerful and does not rely on Spring's "AOP lite" implementation based on dynamic proxies.

With AspectJ you have two options:

  • Compile-time weaving (CTW): You can compile aspects into the 3rd party classes and create a new, aspect-enhanced JAR for your dependency.
  • Load-time weaving (LTW): You can weave aspects into the 3rd party classes while they are being loaded at runtime. This takes a few CPU cycles while bootstrapping your application, but spares you from having to re-package the 3rd party JAR.

Edit: Oh, by the way, your pointcut syntax is invalid. You cannot write

@Around("org.elasticsearch.action.search.SearchRequestBuilder.setQuery() && args(queryBuilder)")

Instead you rather need something like

@Around("execution(* org.elasticsearch.action.search.SearchRequestBuilder.setQuery(*)) && args(queryBuilder)")

A method name is not enough, you have to tell the AOP framework that you want to capture its execution() (in AspectJ cou could also capture all its callers via call()). Secondly, you will not capture a method with one QueryBuilder parameter by specifying a method signature setQuery() without any parameters, thus I suggest you use setQuery(*) or, if you want to be even more precise, setQuery(org.elasticsearch.index.query.QueryBuilder). You also need a return type and/or modifier like public in front of the method name or again a joker like *.



来源:https://stackoverflow.com/questions/25894767/spring-aop-load-time-weaving-on-3rd-party-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!