verpfeilt
verpfeilt

Reputation: 132

Spring: MethodInvokingFactoryBean calls wrong method

I need to do the following Java line in XML:

usersConnectionRepository.setConnectionSignUp(new AccountConnectionSignUp());

So I did this:

<bean id="usersConnectionRepository"
    class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
    scope="singleton">
    <constructor-arg ref="dataSource" />
    <constructor-arg ref="connectionFactoryLocator" />
    <constructor-arg ref="textEncryptor" />
    <aop:scoped-proxy proxy-target-class="false" />
</bean>

<bean id="accountConnectionSignUp" class="edu.kit.tm.cm.ksc.config.AccountConnectionSignUp" />

<bean
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject">
        <ref local="usersConnectionRepository" />
    </property>
    <property name="targetMethod">
        <value>setConnectionSignUp</value>
    </property>
    <property name="arguments">
        <list>
            <ref local="accountConnectionSignUp" />
        </list>
    </property>
</bean>

The error occurs when the method is supposed to be called.

 java.lang.NoSuchMethodException: com.sun.proxy.$Proxy12.setConnectionSignUp(edu.kit.tm.cm.ksc.config.AccountConnectionSignUp)

As you can see above, it's totally searching in the wrong package, and I have no idea why.

I have no idea how to debug this further. I'm inexperienced with Spring and it's XML-Notation. I hope someone can help me. Thank you.

UPDATE

As requested, the complete social.xml. Although, I do not think it is needed to solve this.

<?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:facebook="http://www.springframework.org/schema/social/facebook"
xmlns:twitter="http://www.springframework.org/schema/social/twitter"
xmlns:social="http://www.springframework.org/schema/social" xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/social/facebook http://www.springframework.org/schema/social/spring-social-facebook.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/social/twitter http://www.springframework.org/schema/social/spring-social-twitter.xsd
    http://www.springframework.org/schema/social http://www.springframework.org/schema/social/spring-social.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">


<context:property-placeholder
    location="classpath:/edu/kit/tm/cm/ksc/config/application.properties" />


<bean id="connectionFactoryLocator"
    class="org.springframework.social.connect.support.ConnectionFactoryRegistry"
    scope="singleton">
    <property name="connectionFactories">
        <list>
            <bean
                class="org.springframework.social.twitter.connect.TwitterConnectionFactory">
                <constructor-arg value="${twitter.consumerKey}" />
                <constructor-arg value="${twitter.consumerSecret}" />
            </bean>
            <bean
                class="org.springframework.social.facebook.connect.FacebookConnectionFactory">
                <constructor-arg value="${facebook.clientId}" />
                <constructor-arg value="${facebook.clientSecret}" />
            </bean>
        </list>
    </property>
    <aop:scoped-proxy proxy-target-class="false" />
</bean>



<bean id="usersConnectionRepository"
    class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
    scope="singleton">
    <constructor-arg ref="dataSource" />
    <constructor-arg ref="connectionFactoryLocator" />
    <constructor-arg ref="textEncryptor" />
    <aop:scoped-proxy proxy-target-class="false" />
</bean>

<bean id="accountConnectionSignUp" class="edu.kit.tm.cm.ksc.config.AccountConnectionSignUp" />

<bean
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject">
        <ref local="usersConnectionRepository" />
    </property>
    <property name="targetMethod">
        <value>setConnectionSignUp</value>
    </property>
    <property name="arguments">
        <list>
            <ref local="accountConnectionSignUp" />
        </list>
    </property>
</bean>

<bean id="connectionRepository" factory-method="createConnectionRepository"
    factory-bean="usersConnectionRepository" scope="request">
    <constructor-arg value="#{request.userPrincipal.name}" />
    <aop:scoped-proxy proxy-target-class="false" />
</bean>

<mvc:annotation-driven />
<bean
    class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />


<bean class="org.springframework.social.connect.web.ConnectController">
    <!-- relies on by-type autowiring for the constructor-args -->
    <!-- <constructor-arg ref="connectionFactoryLocator" /> -->
    <!-- <constructor-arg ref="connectionRepository" /> -->
</bean>


<bean id="SimpleSignInAdapter" class="edu.kit.tm.cm.ksc.config.SimpleSignInAdapter" />

<bean class="org.springframework.social.connect.web.ProviderSignInController">
    <!-- relies on by-type autowiring for the constructor-args -->
    <constructor-arg ref="SimpleSignInAdapter" />
</bean>

Update 2

We wrote the Java-examples of the Spring-Social-Documentation to XML. In this case for the ProviderSigninControllers dependencies. Unfortunately there are no XML examples given in this case.

Upvotes: 0

Views: 1887

Answers (1)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 280064

The simple solution is to change proxy-target-class to true in your usersConnectionRepository bean definition and add CGLIB to your class path.

If you don't need the proxying, remove it completely.

Explanation:

First, with this bean declaration

<bean id="usersConnectionRepository"
    class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository"
    scope="singleton">
    <constructor-arg ref="dataSource" />
    <constructor-arg ref="connectionFactoryLocator" />
    <constructor-arg ref="textEncryptor" />
    <aop:scoped-proxy proxy-target-class="false" />
</bean>

Spring is creating a bean of type JdbcUsersConnectionRepository and wrapping it in a JDK proxy (since proxy-target-class is false). The serious shortcoming of JDK proxies, is that they only sub type interfaces.

In other words, Spring will see that the JdbcUsersConnectionRepository class implements the UsersConnectionRepository interface and use that when generating the Proxy. As the javadoc says

  • A proxy class extends java.lang.reflect.Proxy.
  • A proxy class implements exactly the interfaces specified at its creation, in the same order.

So the generated proxy will be of type Proxy and UsersConnectionRepository.

This won't be an issue for MethodInvokingFactoryBean because it stores the reference in a field of type Object. However, when MethodInvokingFactoryBean tries to resolve the Method to invoke, it uses the target object's Class instance, ie. object.getClass(). Since the target object is actually of type Proxy, or com.sun.proxy.$Proxy12 to be exact, it does not have a JdbcUsersConnectionRepository#setConnectionSignUp method and that causes a NoSuchMethodException.

Upvotes: 2

Related Questions