0

I'm trying to log which aspect instance is responsible for which proxied object. However, when I'm collecting proxy object context through this() PCD and using perthis() instantiation model I'm getting an error related to the variable name of proxy object that I use in the pointcut expression:

warning no match for this type name: bean [Xlint:invalidAbsoluteTypeName]

Maven dependencies that I use:

<properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.19</version>
        </dependency>
</dependencies>

This is an aspect that I use to implement that I needed:

@Aspect("perthis(com.sj.aspects.AssociationsAspect.exampleBeans())")
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AssociationsAspect {
    public AssociationsAspect(){
        System.out.println("Creating aspect instance!");
    }

    @Pointcut("execution(* com.sj.utilities.Example.*(..))")
    public void exampleBeans(){};

    @Pointcut("execution(* com.sj.utilities.Example.*(..)) && this(bean)")
    public void exampleOperations(Object bean){};

    @Before(value = "exampleOperations(bean)", argNames = "jp,bean")
    public void beforeExampleMethodsExecution(JoinPoint jp, Object bean){
        System.out.println(
                "JoinPoint: " + jp.getStaticPart() +
                        "\n\taspect: " + this +
                        "\n\tobject: " + bean
        );
    }
}

I tried to change bean as variable name to concrete type, but from documentation it will give different from binding result:

Any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:

  this(com.xyz.service.AccountService)

As well as it will be changing exiting error to another:

error at ::0 formal unbound in pointcut 

Funny enough, if you put away ("perthis(com.sj.aspects.AssociationsAspect.exampleBeans())") then everything will work fine and Spring container will create a separate aspect instance for every proxy object in it. However that is not desirable and most likely a bug, because through @Scope() annotation I only say that there can be multiple instances of same aspect and that Spring container will need to create new instance when I told it to do, but not that it need to create them when it wants to.

The final solution to which I came was to use JoinPoint reflection API instead of collecting context through this() PCD. And it works fine, however I have preconceptions related to how @Scope() works without perthis() instantiation model and with it.

In the end I want to know, 'Is there a solution for collecting proxy object context with this() PCD and using perthis() instantiation model at the same time?'. As well as what are mistakes in the aspect that I described earlier, that give such an error.

bsheps
  • 1,438
  • 1
  • 15
  • 26
  • Follow-up question after having written my second answer: What is the rationale behind using a non-singleton aspect here in the first place? Your code example does not explain it. I do see any context objects or anything else which would require or be made easier by multiple aspect instances. Was trying a different instantiation model merely an experiment, or is there an actual use case? – kriegaex Feb 23 '23 at 07:25
  • It was an experiment to test multiple instance model of aspects. – Sergejs Jevstigņejevs Feb 23 '23 at 08:12
  • You should have chosen to experiment with a use case that can profit from such a model. Otherwise, you know how to do it, but not why you would actually use it. – kriegaex Feb 23 '23 at 08:46

2 Answers2

0

This answer is incorrect in this particular context, but maybe valuable for other readers, because it describes a frequently made mistake by readers of the Spring manual who do not realise that the code snippets in the manual refer to pointcut methods with fully qualified name rather than to target methods.


I am assuming that you got the syntax example for perthis() from this part of the Spring manual. Unfortunately, the example is somewhat misleading, because it uses fully qualified pointcut method names, expecting the user to have read another manual section describing how to use shared pointcut definitions from a separate class, which is a rather exotic use case.

Inside perthis(), when not referencing a poincut method name but defining an inline pointcut targeting a specific application method, you need to specify a full pointcut, not just a method name without even a return type. Examples for valid clauses would be:

  • perthis(myPointcut()), if myPointcut is specified as a separate, named pointcut
  • perthis(this(org.acme.MyClass))
  • perthis(execution(* doCommand(..))
  • perthis(execution(* doCommand(..)) && this(org.acme.MyClass))

See also the AspectJ documentation here and there. Unfortunately, documentation is sparse.

In your particular case, you might want:

perthis(execution(* com.sj.utilities.Example.*(..)))

But of course, referring to an external pointcut like

perthis(exampleBeans())

for pointcuts in the same class or super class or

perthis(com.sj.aspects.AssociationsAspect.exampleBeans())

for poinctuts in a shared pointcut class is also OK.


Update: I created Spring pull request # 29998 in order to improve the documentation, because I also misunderstood the brief examples out of context there.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • It still doesn't work: the error refers to ```@Pointcut("execution(* com.sj.utilities.Example.*(..)) && this(bean)")``` **bean** variable name part: ```warning no match for this type name: bean [Xlint:invalidAbsoluteTypeName]```. Through ```this(com.sj.MyInterface)``` you are getting proxies which have specified interfaces, however I doesn't need it. I just need proxy object that doesn't have interfaces, nothing else, but it can't understand what **bean** means if I use perthis() instantiation model. In singleton it perfectly understands what **bean** variable name means. – Sergejs Jevstigņejevs Feb 20 '23 at 12:07
  • The `bean` variable only makes sense if you bind it to a method parameter, i.e. the advice method has a parameter like `MyClass bean`, `MyInterface bean` or `Object bean`. If you do not need the `this` instance in the advice method, you would rather write `this(org.acme.MyType)` to limit the type or omit the `this()` specifier completely, like I said in my answer. Why don't you start simple like I suggested in my answer, before trying the next more complicated step of binding parameters? Don't just copy & paste code you don't understand. – kriegaex Feb 20 '23 at 13:25
  • Well, if you have read definition of aspect that I provided in the question, then you would have seen that there is named pointcut ```@Pointcut("execution(*com.sj.utilities.Example.*(..)) && this(bean)") public void exampleOperations(Object bean){};``` which collects proxy object context and as a first parameter has ```Object bean```. Then this named pointcut is used in the before advice ```@Before(value = "exampleOperations(bean)", argNames = "jp,bean") public void beforeExampleMethodsExecution(JoinPoint jp, Object bean){...}``` which has second parameter as ```Object bean```. – Sergejs Jevstigņejevs Feb 20 '23 at 14:21
  • I am sorry to have missed that. I have rarely seen users refer to fully qualified pointcut names, which are only necessary when using pointcuts defined in classes other than the current aspect class itself. So usually, they just make mistakes when using that kind of syntax, trying to refer to target methods in a wrong way. I can look into this further after work. Would you mind providing a minimal reproducer project on GitHub? That would save me time and also provide me with a full picture. I do not need your company-confidential code, just an abstracted version of it reproducing the issue. – kriegaex Feb 21 '23 at 07:39
  • Ok. [This is the link to GitHub](https://github.com/SergejsJevstignejevs/spring-aop-associations). – Sergejs Jevstigņejevs Feb 21 '23 at 11:20
0

Adding another answer, leaving the previous one there for reference, even though it is false in this case, because the OP did not make the common mistake to refer to method calls in a wrong way, but actually his perthis clause as such is correct, referring to a pointcut with a fully qualified name.


Thanks for your GitHub sample project.

The problem seems to be a limitation of Spring AOP for your particular use case. Maybe you can open an issue and ask if this restriction can somehow be lifted configuratively. I guess it might be a chicken-vs-egg problem during bootstrapping, i.e. what is instantiated first and available for context binding.

The problem is also not limited to perthis instantiation, but also happens for pertarget in combination with advice pointcuts using this(myProxy) or target(myProxyTarget).

If you, as you found out already, simply use jp.getThis() or jp.getTarget() instead of binding the objects to an advice method parameter directly, you are fine. If you use singleton instantiation, you can use this(myProxy) or target(myProxyTarget) without problems, too. According to your example, you found out about that already, I am just summarising for other readers.

BTW: Good digging, nice research for this edge case.

P.S.: If you would use native AspectJ in your Spring application instead of Spring AOP, probably it would just work the way you want.


With regard to this(com.xyz.service.AccountService) leading to a subsequent error at ::0 formal unbound in pointcut, there you are actually making a mistake. When not referring to a method parameter name but to a class, you cannot bind the parameter and need to remove Object bean from your pointcut and from the advice method using it.

kriegaex
  • 63,017
  • 15
  • 111
  • 202