Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doc: @Transactional on private methods with aspectj mode [SPR-13843] #18416

Closed
spring-projects-issues opened this issue Jan 6, 2016 · 13 comments
Closed

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo opened SPR-13843 and commented

I would like to use @Transactional to private method.I find a way from springdocs 16.5.9,but it's unavailable.

My spring config file like below:

.....
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="tx" mode="aspectj" />
.....

My service class like below:

@Service
public class AdminUsAccountService {

    @Autowired
    private UsAccountMapper usAccountMapper;

    public void out() {
        UsAccount usAccount1 = new UsAccount();
        usAccount1.setId(1);
        usAccount1.setType(1);
        usAccountMapper.updateByPrimaryKeySelective(usAccount1);
        inner();
    }

    @Transactional
    private void inner() {
        UsAccount usAccount2 = new UsAccount();
        usAccount2.setId(3);
        usAccount2.setType(1);
        usAccountMapper.updateByPrimaryKeySelective(usAccount2);
        throw new RuntimeException();
    }
}

I expect the usAccount1's type is 1 and the usAccount2's type is 0(original value in db is 0),but all the two usAccount's type are 1.


Affects: 4.1.7

Backported to: 4.2.7

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Juergen Hoeller commented

How does your account mapper bean work? Have you double-checked that it actually participates in such transactions?

That aside, have you verified that the AspectJ transaction aspect is actually weaved in, either through compile-time weaving or through load-time weaving? Otherwise your @Transactional declarations are not going to work at all in that mode, neither on public nor on private methods.

You could check TransactionSynchronizationManager.isActualTransactionActive() within your inner method to find out whether a transaction has actually been started. Turning on debug logging for org.springframework.transaction would also help.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo commented

I find the AspectJ transaction aspect is not actually weaved in, and the method TransactionSynchronizationManager.isActualTransactionActive() return false within all the out and inner method.
In addition,I find the method TransactionSynchronizationManager.isActualTransactionActive() return true when I change the annotation driven mode to proxy.
That's why?

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo commented

Maybe I lost some configuration?

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo commented

But I don't find additional configuration in the docs.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Juergen Hoeller commented

For load-time weaving, you'll need a context:load-time-weaver element in your configuration, and a corresponding class loader underneath (e.g. through specifying the spring-instrument agent using a -javaagent command line argument for your JVM). This is unfortunately a rather advanced configuration arrangement and not as trivial to set up as proxy-based interception.

Let's turn this into a documentation issue. I'll see what we can do for 4.3 there.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo commented

I'm so sorry, what I just said is not right.
The actual situation is below.
1,When use proxy mode and add @Transactional on the out method and the inner method,then the method TransactionSynchronizationManager.isActualTransactionActive() return true.So,all the value of usAccount1 and usAccount2 in db after execute is zero because transaction has been rollback.
2,When use aspect mode and add @Transactional on the out method and the inner method,then the method TransactionSynchronizationManager.isActualTransactionActive() return false.So,all the value of usAccount1 and usAccount2 in db after execute is one.
3,When use aspect mode and only add @Transactional on the inner method,then the method TransactionSynchronizationManager.isActualTransactionActive() return false.So,all the value of usAccount1 and usAccount2 in db after execute is one.
4,When use proxy mode and only add @Transactional on the inner method,then the method TransactionSynchronizationManager.isActualTransactionActive() return false.So,all the value of usAccount1 and usAccount2 in db after execute is one.
Now,my code is like this.

public void out() {
        UsAccount usAccount1 = new UsAccount();
        usAccount1.setId(1);
        usAccount1.setType((byte) 1);
        System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
        usAccountMapper.updateByPrimaryKeySelective(usAccount1);
        inner();
    }
private void inner() {
        System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
        UsAccount usAccount2 = new UsAccount();
        usAccount2.setId(3);
        usAccount2.setType((byte) 1);
        usAccountMapper.updateByPrimaryKeySelective(usAccount2);
        throw new RuntimeException();
    }

The conditions above show that only use proxy mode and add @Transactional on the two method can active transaction,but this is not what I want.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo commented

I think the weave way is CTW usually.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Juergen Hoeller commented

Compile-time weaving is certainly more reliable. However, it requires special build setup: compiling your application code with ajc against the provided spring-aspects jar.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo commented

Does CTW need to use AspectJ compiler?

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo commented

I got it.Thanks very much.I try to use LTW,but get excepetion below.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.weaving.AspectJWeavingEnabler#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver': Initialization of bean failed; nested exception is java.lang.IllegalStateException: ClassLoader [org.apache.catalina.loader.WebappClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:167)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:606)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:462)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4939)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver': Initialization of bean failed; nested exception is java.lang.IllegalStateException: ClassLoader [org.apache.catalina.loader.WebappClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.weaving.LoadTimeWeaverAwareProcessor.postProcessBeforeInitialization(LoadTimeWeaverAwareProcessor.java:97)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1566)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
... 20 more
Caused by: java.lang.IllegalStateException: ClassLoader [org.apache.catalina.loader.WebappClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar
at org.springframework.context.weaving.DefaultContextLoadTimeWeaver.setBeanClassLoader(DefaultContextLoadTimeWeaver.java:91)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1590)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1561)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
... 29 more

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Xiaolong Zuo commented

The web container what I use is tomcat . When I change weaver-class to TomcatLoadTimeWeaver,then get exception below.

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.weaving.AspectJWeavingEnabler#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver]: Constructor threw exception; nested exception is java.lang.IllegalStateException: Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:167)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:606)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:462)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4939)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loadTimeWeaver': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver]: Constructor threw exception; nested exception is java.lang.IllegalStateException: Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1101)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.weaving.LoadTimeWeaverAwareProcessor.postProcessBeforeInitialization(LoadTimeWeaverAwareProcessor.java:97)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1566)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
... 20 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver]: Constructor threw exception; nested exception is java.lang.IllegalStateException: Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:163)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:89)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1094)
... 31 more
Caused by: java.lang.IllegalStateException: Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available
at org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver.<init>(TomcatLoadTimeWeaver.java:79)
at org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver.<init>(TomcatLoadTimeWeaver.java:48)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
... 33 more
Caused by: java.lang.NoSuchMethodException: org.apache.catalina.loader.WebappClassLoader.addTransformer(java.lang.instrument.ClassFileTransformer)
at java.lang.Class.getMethod(Class.java:1786)
at org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver.<init>(TomcatLoadTimeWeaver.java:69)
... 39 more

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Juergen Hoeller commented

Which version of Tomcat are you using? Recent versions of Tomcat 7 and 8 should provide that addTransformer method out of the box. Alternatively, you could install the spring-instrument-tomcat jar with its custom subclass of Tomcat's default web app class loader.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 7, 2016

Xiaolong Zuo commented

Thanks,Juergen.
I have achieved the goal,use the proxy mode with programmatic transaction.
As to why not use aspectJ(CTW),just because the ajc compiler is conflict with lombok.I don't find a way to resolve the problem.
The version of tomcat what I using is tomcat8.I think that the aspectJ(LTW) is a bit complicated,so I give up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.