Announcement Announcement Module
Collapse
No announcement yet.
DefaultAdvisorAutoProxyCreator and Spring Controller : bug or feature ? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • DefaultAdvisorAutoProxyCreator and Spring Controller : bug or feature ?

    Hi,

    I've been running into a problem, and wonder whether it's supposed to work that way, or whether it's a bug...

    I am running the DefaultAdvisorAutoProxyCreator to create Transaction Proxies of all beans with the @Transactional Annotation.

    It works fine as long as the target objects are not Spring MVC Controllers.

    My first problem was that the DefaultAdvisorAutoProxyCreator was defined in some other XML file than the springapp-servlet.xml, so no proxy was created.
    But when I move the DefaultAdvisorAutoProxyCreator to springapp-servlet.xml, the logs show that my controller isa ctually proxied using JDK Proxy, but it has no effect.

    1st question) why don't the beans defined in spring contexts see the springapp-servlet.xml beans, and the springapp-servlet.xml beans actually see the other beans ?

    2nd quesiton: Do Spring MVC Controllers have a special status that prevents them from being transparently proxied correctly ?

    <bean name="/register.spring" id="accountRegistrationController" class="funala.web.AccountRegistrationController">
    <property name="sessionForm"><value>true</value></property>
    <property name="commandName"><value>account</value></property>
    <property name="commandClass"><value>funala.domain.authentic ation.Account</value></property>
    <property name="validator"><ref bean="accountRegistrationValidator"/></property>
    <property name="formView"><value>register</value></property>
    <property name="successView"><value>login.spring</value></property>
    <property name="accountDao" ref="accountDao" />
    </bean>




    <!-- Transaction manager -->
    <bean id="txManager"
    class="org.springframework.orm.jpa.JpaTransactionM anager">
    <property name="entityManagerFactory"
    ref="entityManagerFactory" />
    </bean>

    <!-- AOP Transaction Interceptor -->
    <bean id="txInterceptor"
    class="org.springframework.transaction.interceptor .TransactionInterceptor">
    <property name="transactionManager" ref="txManager" />
    <property name="transactionAttributeSource">
    <bean class="org.springframework.transaction.annotation. AnnotationTransactionAttributeSource" />
    </property>
    </bean>


    <!-- Read all Annotations concerning Transactions -->
    <bean id="txAttributeSourceAdvisor"
    class="org.springframework.transaction.interceptor .TransactionAttributeSourceAdvisor">
    <property name="transactionInterceptor" ref="txInterceptor"/>
    </bean>

    <!-- Automatic transaction demarcation -->
    <bean id="autoProxyCreator"
    class="org.springframework.aop.framework.autoproxy .DefaultAdvisorAutoProxyCreator"/>


    thanks a lot for your help,
    Sami Dalouche
    Last edited by samokk; Apr 12th, 2006, 10:33 AM.

  • #2
    2nd quesiton: Do Spring MVC Controllers have a special status that prevents them from being transparently proxied correctly ?
    No.

    Do you have the @Transactional annotation on your controllers? What is the log output? You said that you do get proxies, just no tx delimitation?

    Comment


    • #3
      Originally posted by Rod Johnson
      No.

      Do you have the @Transactional annotation on your controllers? What is the log output? You said that you do get proxies, just no tx delimitation?
      Yes, I added the @Transactional annotation on my controller, like for other beans :
      org.springframework.aop.framework.autoproxy.Defaul tAdvisorAutoProxyCreator - Creating implicit proxy for bean 'accountRegistrationController' with 0 common interceptors and 1 specific interceptors

      So, it looks like it creates the proxy, but does not seem to use it (the specific logs that appear at the beginning and end of the transaction, interacting with the transaction manager do not appear, like if the controller was directly used, and the proxy was useless..

      Comment


      • #4
        Can anyone point me to the class where the Spring Controllers are called ? (especialy the bean retrieval and the onSubmit() method call).

        I suspect that Spring does not call the proxy but calls the proxied bean instead. When I delegate the onSubmit() call to some method of another bean, which is made @Transactional (and the controller gets its reference through IoC), it works perfectly..

        but it's impossible to get @Transactional to work on the Spring controller's onSubmit() method.

        Thanks,
        Sami Dalouche

        Comment


        • #5
          Just some information for google users ;-)

          Just some more information, for people googling for things such as "How to get Spring declarative transaction demarcation to work, with some persistence technology such as Hibernate, JPA (Java Persistence API), JDBC, or any other".

          Since the official documentation may not be explicit enough for all users, here are some additional information :

          The first thing that you need is a transaction Manager. The implementation is provided by Spring and is dependent on the particular persistence technology.
          In JPA/EJB3 transaction manager case, you can use the following bean definition :

          <!-- Transaction manager -->
          <bean id="txManager"
          class="org.springframework.orm.jpa.JpaTransactionM anager">
          <property name="entityManagerFactory"
          ref="entityManagerFactory" />
          </bean>

          The transaction manager is responsible for begin, rollback and commit of transactions.

          It is now necessary to make Spring aware of which classes should be marked as transactionnal. It is done using the Spring AOP Framework. Some (tough) information about it is given in the reference documentation, and some understandable information for human beings is probably present in the authors's book (I don't have the reference right now, but it must be available from Spring's homepage).

          Roughly, the way spring AOP works is the following :
          - instead of having AOP code directly injected inside the target objects (in our case, the classes needing transactional support. roughly : tx.begin() at the beginning of the methods, and tx.commit() at the end, to simplify), proxies are generated, and other classes use the proxies instead of the original beans thanks to IoC magic. The generation of the AOP proxy is done using a Proxy Creator :

          <!-- Automatic transaction demarcation -->
          <bean id="autoProxyCreator"
          class="org.springframework.aop.framework.autoproxy .DefaultAdvisorAutoProxyCreator"/>


          - This automatic proxy creator will however need two things
          1) what AOP code to inject : we want transactions.

          2) which objects to target (which objects and methods should be made transactional)

          This is done using Advisors and Interceptors.
          <!-- Read all Annotations concerning Transactions -->
          <bean id="txAttributeSourceAdvisor"
          class="org.springframework.transaction.interceptor .TransactionAttributeSourceAdvisor">
          <property name="transactionInterceptor" ref="txInterceptor"/>
          </bean>

          The TransactionSourceAdvisor describes 1) and 2)
          the AOP code to inject is specified using the interceptor described in the txInterceptor bean

          The objects to target are implicit : this Advisor uses @Transactional attributes on the classes :

          @Transactional(readOnly=false)
          public class ...
          {

          @Transactional(readOnly=false)
          public void transactionalMethod(){

          }
          }


          <!-- AOP Transaction Interceptor -->
          <bean id="txInterceptor"
          class="org.springframework.transaction.interceptor .TransactionInterceptor">
          <property name="transactionManager" ref="txManager" />
          <property name="transactionAttributeSource">
          <bean class="org.springframework.transaction.annotation. AnnotationTransactionAttributeSource" />
          </property>
          </bean>



          Hope it can help some people...

          Comment


          • #6
            DefaultAdvisorAutoProxyCreator / &lt;tx:annotation-driven&gt;

            hi,

            i have nearly the same problem. i'm running a web application with some servlets. this is my web.xml:

            Code:
            <?xml version="1.0" encoding="UTF-8"?>
            <web-app>
            	<display-name>Tool</display-name>
            	
            	<context-param>
            		<param-name>contextConfigLocation</param-name>
            		<param-value>/WEB-INF/applicationContext.xml</param-value>
            	</context-param>
            
            	<listener>
            	     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            	</listener>
            
            	<servlet>
            		<servlet-name>shell</servlet-name>
            		<servlet-class>com.google.gwt.dev.shell.GWTShellServlet</servlet-class>
            	</servlet>
            	
            	<servlet>
            		<servlet-name>SpringService</servlet-name>
            		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            		<load-on-startup>1</load-on-startup>
            	</servlet>
            		  	
            	<servlet-mapping>
            		<servlet-name>SpringService</servlet-name>
            		<url-pattern>/service/user</url-pattern>
            	</servlet-mapping>
            
            		
            </web-app>
            as you can see I'm working with gwt but I don't think that's the problem.

            I put the <tx:annotation-driven> element in my applicationContext.xml but it doesn't recognize the @Transactional annotations at all. If I declare the declarative transaction management via the BeanNameAutoProxyCreator and referring to a declarative transactionAttributeSource everything works fine!

            it seems that the proxy is not present for resolving the transaction annotation...

            With applications not using a web configuration the <tx:annotation-driven> element behaves like the DefaultAdvisorAutoProxyCreater and both methods work fine using the ClassPathXmlApplicationContext for initializing.

            Does anyone know waht I'm making wrong?

            Thanks for your help
            Daniel

            Comment


            • #7
              Solution

              OK, so after some research, here is the conclusion :
              * Spring MVC has no bug
              * Spring AOP Transaction Demacarcation works correctly too

              However, the design of Spring MVC is messed up and is incompatible with Spring AOP design. In fact, Spring MVC works by inheriting an AbstractController that abstracts out the whole work flow. This means that the controller calls method on the SAME OBJECT, NOT THE PROXY !!! As a result, it is IMPOSSIBLE to set @Transactional any of its protected method.
              Solutions and work arounds :

              * Use WebWork instead of Spring MVC (no flame war, I just did the mistake once, and would like others to avoid it) .. WW is much better designed, easier to use than Spring MVC, completly integrated with Spring, compatible with Spring AOP Proxy creation (CGLIB proxies only), and its Taglib is way more complete than Spring MVC's one
              * I haven't tested it, but it should be possible to mark the handleRequest method as transactional. Since it may be impossible to override this method from a SimpleForm..Controller (it's marked as final, I think), a possible work around would be to create a new interface with

              @Transactional
              ModelAndView handleRequest(servletRequest, servletResponse) throws Exception

              then have your controllers implement that interface, and The whole http request will be transactional...

              * Another solution, which should work as well, is to fire the spring-aspects.jar, Spring 2.0, an AspectJ compiler, to directly inject the AOP code into the Java bytecode. This should work too, and opens the door to end less possibilities. More information is available from Spring 2 manual, and from http://www.aspectprogrammer.org/blog...cal_gui_2.html


              Good luck
              Sami Dalouche

              Comment


              • #8
                AspectJ is not working...

                Hi Sami,

                thanks a lot for your reply!

                I tried your suggestion with AspectJ but with no result:

                Code:
                	<!--  aspectj driven	--> 
                	  <tx:advice id="txAdvice" transaction-manager="transactionManager">
                	    <tx:attributes>
                	      <tx:method name="save*"/>
                	    </tx:attributes>
                	  </tx:advice>
                	    
                	  <aop:config>
                	    <aop:pointcut id="groupServiceMethods" expression="execution(* server.CategoryService.*(..))"/>
                	    <aop:advisor advice-ref="txAdvice" pointcut-ref="groupServiceMethods"/>
                	  </aop:config>
                This solution works fine within a normal java application but not within a web application. Even when I use a wildcard "*" for the method injection.

                I opened another thread in the web forum:

                http://forum.springframework.org/showthread.php?t=29201

                The funny thing here is that the BeanNameAutoProxyCreator seems to work at least with the wildcard method injection. Nobody gave me an answer yet on that thread but it discusses (almost) the same problem...

                I wonder why the Spring MVC framework is not compatible to the Spring AOP framework...

                Are there any example for using WebWork with Spring? As I mentioned before I'm using the Google Web Toolkit for developing a Rich Internet Client. Therefore I'm only using services (servlets) for fullfilling request from the client. There is no need to use Spring with GWT but for the server side it's a very good framework to use.

                To be honest, I'm worried about using another "complete" framework like WebWork... Do you have an example how to integrate WebWork with Spring?

                Thanks and Regards
                Daniel

                Comment


                • #9
                  Hi,

                  Concerning your initialization problem... I'm not sure about the answer, but make sure you understand how the miscellaneous ApplicationContexts are nested. In fact, Spring creates a hierarchy of ApplicationContexts...

                  The parent context is isually the one that loads the file specified in contextConfigLocation, and it has a children context, which is the spring servlet's one.. I'm not sure of how things work, but I remember having some problems when playinng with AOP.. When you put AOP interceptors in the wrong place, they don't see the beans of the other one, and no proxy is created... Not sure whether it is a bug or a feature...

                  Concerning AspectJ, the Spring /AspectJ integration part that you have to use is NOT the use of Spring AOP to take advantage of AspectJ aspects, but the opposite. What you have to do is take the AspectJ aspects written by spring (in spring-aspects.jar), tell the AspectJ compiler to weave the classes at compile time...

                  Basically, here is what I've had to do to get things to work... I am using Maven2, with AspectJ compiler plugin :

                  1.) Tell the AspectJ compiler to take the aspects in spring-aspects.jar, and apply them to all the classes that have the @Transactional Annotation
                  <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>aspectj-maven-plugin</artifactId>
                  <configuration>
                  <source>1.5</source>
                  <target>1.5</target>
                  <weaveDependencies>
                  <weaveDependency>
                  <groupId>springframework</groupId>
                  <artifactId>spring-aspects</artifactId>
                  </weaveDependency>
                  </weaveDependencies>
                  </configuration>
                  <executions>
                  <execution>
                  <goals>
                  <goal>compile</goal>
                  <goal>test-compile</goal>
                  </goals>
                  </execution>
                  </executions>
                  </plugin>

                  2) It is also necessary to tell spring to prepare some stuff for the aspects to work :
                  <aop:spring-configured/>

                  3) The following line is also necessary, to configure the Annotation Aspect to use the correct transactionManager
                  <!-- Configures the Transactional Aspect to use the correct txManager -->
                  <bean class="org.springframework.transaction.aspectj.Ann otationTransactionAspect"
                  factory-method="aspectOf">
                  <property name="transactionManager" ref="txManager"/>
                  </bean>

                  4) Of course, you remove any tx:advice, tx-annotated, etc.. All this stuff does NOT concern AspectJ, only Spring AOP..

                  5) Once this is working, you can even do crazy stuff, such as injecting dependencies to domain objects (not managed in spring), but this is another story.. Once AspectJ is working, possibilities are endless ;-)

                  PS: have you tried to set the whole handleRequest as Transactional ?

                  PS2: concerning Webwork... Webwork is meant to work with spring BY DEFAULT... It requires an IoC container, and recommends spring, so it's really meant to work with Spring... Webwork documentation is pretty bad, but the examples are really good.. Just java -jar webwork-2.2.4 quickstart:showcase and you will see all the examples. You can then play/look at the code, once you've followed the few tutorials on the website..
                  Generally speaking, WW is really easy to learn, once you've grasped the philosophy (everything is an interceptor).

                  If you need more information about WW & spring, don't hesitate to reply on this post, I can give you more details about my setup, if you need it...

                  The only important thing to know is that you have to use CGLIB proxies, using the following line :
                  <aop:config proxy-target-class="true"/>

                  (or AspectJ bytecode injection also works fine, which is what I am using now)

                  Good luck,
                  Sami Dalouche

                  Comment


                  • #10
                    no luck...

                    Hi Sami,

                    again thank your for your detailed answer! I really appreciate your help.

                    I tried now everything and I'm giving up as I don't have more time to spend on that...

                    To give you some feedback what I've done so far:

                    > PS: have you tried to set the whole handleRequest as Transactional ?

                    Yes I tried it but with no success... I have to explain it a little bit more as I'm using GWT. GWT's rpc is a simple call on a servlet. To use the spring MVC controller I use a gwt-widgets.jar written by George Georgovassilis. In this package there is a class which implements a GWT-Spring controller extending the original GWT controller with the Spring controller interface:

                    Code:
                    public abstract class GWTSpringController extends RemoteServiceServlet implements Controller
                    {
                        private ServletContext context;
                    
                        public ModelAndView handleRequest (HttpServletRequest request, HttpServletResponse response)
                                throws Exception
                        {
                            doPost(request, response);
                            return null;
                        }
                    
                        public void setServletContext (ServletContext context)
                        {
                            this.context = context;
                        }
                    
                        public ServletContext getServletContext ()
                        {
                            return context;
                        }
                    }
                    as you can see the handleRequest method calls the doPost method from the GWT controller (RemoteServiceServlet). The doPost method ist marked as final... therefore it was impossible to use the CGLIB-Proxy on that (using proxy-target-value="true").

                    I wrote an Interface looking like this:

                    Code:
                    public interface GWTSpringControllerTx {
                    
                    	@Transactional
                    	public ModelAndView handleRequest (HttpServletRequest request, 
                    			HttpServletResponse response) throws Exception;
                    }
                    My actual controller class looked like this:

                    Code:
                    public class GroupServiceImpl extends GWTSpringController implements GWTSpringControllerTx, GroupService {..
                    I put <tx:annotation-driven> in my application context.

                    It didn't work... :-( Actually I'm not a Java Expert... It might be wrong using the interface on my GroupServiceImpl class... The gwt-widgets.jar comes with no source so I don't know how I can change anything on that besides using inheritance.

                    > using AspectJ

                    I understand now the difference using AspectJ instead of Spring-AOP. But I think I'm too new to that stuff to understand everything... I tried to use the given spring-aspects.jar using the AnnotationTransactionAspect.

                    I put an aop.xml in my WEB-INF path:

                    Code:
                    <!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">    
                    
                    <aspectj>
                      <aspects>  
                        <include within="org.springframework.transaction.aspectj.AnnotationTransactionAspect"/>
                      </aspects> 
                       <weaver options="-showWeaveInfo -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
                    	 <include within="dsadmintool.server.service..*"/>
                       </weaver>
                    </aspectj>
                    in my application context i tried two ways:

                    Code:
                    	<aop:aspectj-autoproxy/>
                    
                    	<!--
                    	<bean class="org.springframework.transaction.aspectj.AnnotationTransactionAspect"
                    			factory-method="aspectOf">
                    		<property name="transactionManager" ref="transactionManager"/>
                    	</bean>
                    	-->
                    In my Implementation class (not the Interface) I put the @Transactional annotation in front of the save-method.

                    when I put the -javaagent:lib/aspectjweaver.jar in my VM as startup parameter my CPU was running on a 100% for about 10 minutes ;-) At the end the whole startup process for the GWT-Shell stopped with an exception... Actually thousands of exceptions were thrown...

                    I'm really frustrated about all that... I spent a lot of time thinking Spring makes life easier... I still don't understand why Spring MVC is not working with Auto-Proxy Creation. Why isn't it possible to Proxy that bloody (sorry...) Spring controller?

                    Thanks again Sami...

                    Regards
                    Daniel

                    Comment


                    • #11
                      Hi,

                      Concerning GWT.. I don't know much about its design, but it looks to me that it should work as expected...

                      Concerning AspectJ... I haven't tried the load time weaver, as you did, but Using the AspectJ compiler definitely works... Are you using Ant ? Maven ? The maven2 plugin works beautifully, but I'm sure there are also ant & maven1 plugins that work..

                      Concerning Spring MVC and proxying..

                      Look at class A

                      class A {
                      public method() {
                      this.doInternalMethod();
                      }
                      private doInternalMethod() {
                      }
                      }


                      Let's say you have an instance "a" of A, and a proxy that calls "a.method()" around begin() and commit() calls.

                      So.. no matter what you do on the proxy, you understand that the proxy has NO effect on doInternalMethod().. You can only have an action on the public method... once the proxy calls the proxies object "a", then the rest continues as if it weren't proxies.. We can't influence the doInternalMethod() unless we directly inject bytecode.. That's why bytecode injection is wayy more powerful than Proxying, but it's also more complex since you have to use a compiler....

                      Do you get it ?
                      Sami Dalouche

                      Comment

                      Working...
                      X