user3087506
user3087506

Reputation:

Spring 3 Autowire Bean which uses an Interface

I have an Bean configured in my dispatcher-servlet.xml. In a class i can successfully inject this bean with the autowired annotation e.g.

class test {
  @Autowired
  TestBean testBean;
}

But as soon i add an interface with the "implements" keyword to the testbean, i get an IllegalArgumentException:

java.lang.IllegalArgumentException: Can not set com.test.TestBean field com.test.myclass.testBean to com.sun.proxy.$Proxy26.

When in remove the "implements" keyword, including the name of the interface, all works fine again.

Upvotes: 1

Views: 1758

Answers (2)

rwinner
rwinner

Reputation: 418

in XML:

<bean id="test" class="your.package.Test"/>

make sure Test is in your spring bean XML, you then can do

@Autowired
Test test;

test.testBean.doAnything();

thing to notice here is that you HAVE to intatiate your Test class.

Upvotes: 0

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 280168

You would need to provide more details, like the interface type and your context configuration, but the reason is the following. Spring, by default, uses JDK proxies to add AOP or decorator behavior, for example, for @Transactional or @Async.

JDK proxies only work with interface types, not with class types. Take this example

public class Driver {
    public static void main(String[] args) throws Exception {
        final Example example = new Example();
        Proxied proxy = (Proxied) Proxy.newProxyInstance(Driver.class.getClassLoader(), example.getClass().getInterfaces(), new InvocationHandler() {
            Example target = example;
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("intercepted");
                return method.invoke(example, args);
            }
        });

        proxy.execute();

        System.out.println(proxy.getClass());
        System.out.println(proxy.getClass().getSuperclass());
        System.out.println(Arrays.toString(proxy.getClass().getInterfaces()));
    }

    static class Example implements Proxied {
        @Override
        public void execute() {
            System.out.println("Example executing.");
        }
    }

    static interface Proxied {
        void execute();
    }
}

which prints

intercepted
Example executing.
class com.spring.$Proxy0
class java.lang.reflect.Proxy
[interface com.spring.Driver$Proxied]

For the purpose of this example, Spring will take the Example bean (declared in a context), decide that it needs to proxy it, use the Example class' interfaces, and create whatever InvocationHandler it needs by referring to the bean as the target to invoke the method on.

What you need to take note of is that the object that is returned by Proxy.newProxyInstance(..) is not of type Example. It is of type Proxy and of whatever type the interfaces of Example are. That is why Spring cannot use the proxy object to set a field (through reflection) of type Example, or TestBean in your case.

Two ways to make it work. First, extract an interface from your class, if it doesn't have one already and use a field of the interface type.

Second, you can instead configure your context to use CGLIB proxies which can proxy by class type.

Upvotes: 1

Related Questions