Spring / @Transactional with AspectJ is totally ig

2019-01-20 02:52发布

问题:

I use Spring-Data Neo4j 2.2.0-RELEASE. (my following issue would be applicable to any other kind of entity mapping, why not JPA)

In my project, I have a public method annotated with @Transactional Spring's annotation, since I want to update/save an entity inside it:

public class MeetingServices {

    private UserRepository userRepository;

    private MeetingRepository meetingRepository;

    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void setMeetingRepository(MeetingRepository meetingRepository) {
        this.meetingRepository = meetingRepository;
    }

    @Transactional("neo4jTransactionManager")
    public void save(Meeting meeting) {
        User creator = userRepository.getUserByEmail("test@test.com");
        creator.participateIn(meeting); // this line leads to a NotInTransactionException since it signals that no transaction context is associated.
        meeting.setCreator(creator);
    }

My application-context.xml is the following:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/neo4j
       http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean id="graphDatabaseService" class="org.neo4j.kernel.EmbeddedGraphDatabase">
        <constructor-arg value="target/neo4jgraph" />
    </bean>

    <neo4j:config graphDatabaseService="graphDatabaseService" />

    <bean id="meetingServices" class="services.MeetingServices">
        <property name="userRepository"><ref bean="userRepository"/></property>
        <property name="meetingRepository"><ref bean="meetingRepository"/></property>
    </bean>

    <bean id="userServices" class="services.UserServices">
        <property name="userRepository"><ref bean="userRepository"/></property>
    </bean>

    <bean id="neo4jTransactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager">
            <bean class="org.neo4j.kernel.impl.transaction.SpringTransactionManager">
                <constructor-arg ref="graphDatabaseService" />
            </bean>
        </property>
        <property name="userTransaction">
            <bean class="org.neo4j.kernel.impl.transaction.UserTransactionImpl">
                <constructor-arg ref="graphDatabaseService" />
            </bean>
        </property>
    </bean>

    <tx:annotation-driven mode="aspectj"
        transaction-manager="neo4jTransactionManager" />

    <!-- auto-generated repositories for Neo4j storage -->
    <neo4j:repositories base-package="repositories"/> 

    <context:spring-configured/>

    <context:annotation-config/>

</beans>

As we see in this configuration, aspectJ is used for transactions.

So, I tried to test another way of doing by changing my application-context.xml to use the proxy feature instead of aspectJ feature:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/neo4j
       http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean id="graphDatabaseService" class="org.neo4j.kernel.EmbeddedGraphDatabase">
        <constructor-arg value="target/neo4jgraph" />
    </bean>

    <neo4j:config graphDatabaseService="graphDatabaseService" />

    <bean id="meetingServices" class="services.MeetingServices">
        <property name="userRepository"><ref bean="userRepository"/></property>
        <property name="meetingRepository"><ref bean="meetingRepository"/></property>
    </bean>

    <bean id="userServices" class="services.UserServices">
        <property name="userRepository"><ref bean="userRepository"/></property>
    </bean>

    <tx:annotation-driven mode="proxy" />


    <neo4j:repositories base-package="repositories"/>

    <context:spring-configured/>

    <context:annotation-config/>

</beans>

This configuration works pretty well since @Transactional (whose neo4jTransactionManager parameter was of course removed) annotation is now taken in account for my service's method.

My question is, (no matter whether my project would work with simple proxy method):

What did I miss or misconfigure in my first Spring's configuration that makes aspectJ transaction features failed?

I'm currently improving my technical skills with Spring, and read few articles about "Load-time weaving" for aspectJ. Might this be linked to my issue?

回答1:

Try adding <context:load-time-weaver/> to enable load time weaving and add the spring-aspects.jar to the classpath.

See http://static.springsource.org/spring/docs/current/spring-framework-reference/html/aop.html#aop-aj-ltw-spring for more info

Edit

For generic java applications, ie not running in a web or application container, you need to enable the java instrumentatioin via a javaagent option:

java -javaagent:path/to/spring-instrument.jar your.Main

If you want to weaving your own aspects, you need to provide a META-INF/aop.xml file with aspect declarations. (Not requiered for spring only aspects, it's already provided on spring-aspect.jar).

Finally you can use compile time weaving instead, using the maven aspectj plugin, for example:

<plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <configuration>
                <complianceLevel>1.6</complianceLevel>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>