0

I have my spring project (Not Spring Boot) I turned on aspect with <aop:aspectj-autoproxy /> After I did it I have odd behaviour with my @PersistenceContext and @Transactional annotations.

When I had <aop:aspectj-autoproxy proxy-target-class="true" /> I had error in my generic class only in case I had internal call of methods. For example: when I called method delete externally ad method find was called internally from delete method. Check example below.
My generic class example:

@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class GenericServiceImpl<T extends IEntity<PK>, PK extends Serializable> implements IGenericService<T, PK> {
    private final Class<T> type;
    
    public GenericServiceImpl(final Class<T> type) {
        this.type = type;
    }

    private EntityManager em;
    
    @PersistenceContext
    public final void setEntityManager(final EntityManager em) {
        this.em = em;
    }

    public final EntityManager getEntityManager() {
        return em;
    }
 
    public final void delete(final PK pk) { // I call this method externally
        T o = find(pk); // internal call which didn't work
        if (o != null) {
            o.setDeleted(true);
            save(o);
        }
    }

    @Transactional(readOnly = true)  // when I call this method externally everything works fine
    public final T find(final PK pk) {
        if (pk == null) {
            return null;
        }

        T o = em.find(type, pk); // In case I call find method from delete method em is null
        if (o != null && o.isDeleted()) {
            return null;
        } else {
            return o;
        }
    }
}

When I had aop:aspectj-autoproxy/ I had 2 problems:

  • It was no possible to get parameters name in aspect
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
methodSignature.getParameterNames(); // here I had null

But in my case I worked around it by using autogenerated name of parameters (for my case it was enough).

  • When I turned on proxy-target-class="true" `<aop:aspectj-autoproxy proxy-target-class="true"/> I had error in my generic class only in case I had internal call of methods. For example: when I called method delete externally and method find was called internally from delete method. In this case my EntityManager was null. Check example above.

When I removed proxy-target-class="true" <aop:aspectj-autoproxy /> I fixed crash with em is null but I received odd methods behavior. For example: After I called delete method my entity was deleted from database. But when I called the find the method I received previous state of entity. It looks like my entity was cached in the persistence context. How can I fix it?

JAVA version: 8 My POM deps (only meaningful):

    <spring.version>4.3.30.RELEASE</spring.version>
    ....
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
        
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.19</version>
        </dependency>


Volodymyr Bilovus
  • 654
  • 2
  • 7
  • 20
  • You wrote a long-winded question, I tried to help you, and now you are not reacting. I do not find that particularly polite. Would you please provide some feedback? – kriegaex May 07 '23 at 14:00

1 Answers1

0

This is a preliminary answer, too long to fit into a comment. I will update it, as the OP provides more information.

It is a bit difficult for me to understand what you are trying to explain. I think I can help you, if you publish a minimal, complete, and verifiable example on GitHub, adding context to your sample code. Please also make sure that the reproducer initialises and uses an in-memory DBMS like H2. Thank you.

When just copying & pasting your code fragment (with lots of missing classes referenced from it) into my IDE, I noticed the hint "Methods annotated with '@Transactional' must be overridable" on your final find method:

IntelliJ IDEA warning

Making it non-final should help, otherwise Spring AOP cannot create any proxy method for it.

Furthermore, if you expect self-invocation to trigger aspects - both your own ones and declarative ones like @Transactional - you cannot use Spring AOP, but have to switch to native AspectJ, because the latter is independent of proxies. BTW, it would also work with final methods. See my answer here to understand how Spring proxies work.

kriegaex
  • 63,017
  • 15
  • 111
  • 202