Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring injects @Autowired fields during manual class instantiation

Tags:

java

spring

In my project I am surprised to see that Spring (4.3) seems to attempt to autowire dependencies for classes even when they are manually instantiated.

MyClass.java (note Lombok annotations):

@NoArgsConstructor
@AllArgsConstructor
public class MyClass {
    @Autowired
    private MyClassDependency dependency;
}

MyClassDependency.java uses a factory method and no Spring annotations:

public class MyClassDependency {
    public static MyClassDependency create() { return new MyClassDependency(); }
}

Spring config resulting in NoSuchBeanDefinitionException:

@Configuration
public class SpringConfig {
    @Bean
    public MyClass myClass() {
        return new MyClass(MyClassDependency.create());
    }
}

Providing the bean makes Spring happy:

@Configuration
public class SpringConfig {
    @Bean
    public MyClass myClass() {
        return new MyClass(); // let autowire inject dependencies
    }
    @Bean
    public MyClassDependency myClassDependency() {
        return MyClassDependency.create();
    }
}

This was a big surprise to me. I'd like to have the simpler first version of the config... Where is this behavior coming from / controlled? (It's possible that I missed it or pulled it from some dependencies).

PS: To clarify, MyClass code is outside of my control but I can change Spring config. I am looking to understand how Spring intercepts the constructor call withing a bean method and whether constructor can be used instead.

like image 982
alexandroid Avatar asked Dec 12 '18 06:12

alexandroid


People also ask

What is difference between @autowired and @inject?

@Inject and @Autowired both annotations are used for autowiring in your application. @Inject annotation is part of Java CDI which was introduced in Java 6, whereas @Autowire annotation is part of spring framework. Both annotations fulfill same purpose therefore, anything of these we can use in our application.

Does Spring @autowired inject beans by name or by type?

if Spring encounters multiple beans with same type it checks field name. if it finds a bean with the name of the target field, it injects that bean into the field.

Is @autowired setter injection?

@Autowired on Setter Methods You can use @Autowired annotation on setter methods to get rid of the <property> element in XML configuration file. When Spring finds an @Autowired annotation used with setter methods, it tries to perform byType autowiring on the method.

Can we use @autowired in a class?

We can also use @Autowired annotation on constructor for constructor based spring autowiring. For @Autowired annotation to work, we also need to enable annotation based configuration in spring bean configuration file. This can be done by context:annotation-config element or by defining a bean of type org.


2 Answers

The problem is the following:

  1. You annotated a field with @Autowired - that tells Spring, that there is a dependency that needs to be injected there
  2. You annotated a method with @Bean - which puts the return value into Spring's context.
  3. When Spring processes the return value of the @Bean-annotated object, it starts to proces its annotations - and there is a field with an annotation, that tells Spring to inject a dependency there, even if the field already has a value assigned.
  4. Spring tries to inject the dependency here, but that dependency does not exist in the Spring context, so injection fails.

So having @Bean, but manually injecting an @Autowired dependency conflicts each other, you may now understand, why - you shall not inject Autowired beans manually! Autowired is an annotation that tells he CI container to do some injection work.

like image 126
persicsb Avatar answered Oct 12 '22 11:10

persicsb


There's a hacky way of doing that, as described in this old Stack Overflow thread.

public static <T> FactoryBean<T> preventAutowire(T bean) {
    return new FactoryBean<T>() {
        public T getObject() throws Exception {
            return bean;
        }

        public Class<?> getObjectType() {
            return bean.getClass();
        }

        public boolean isSingleton() {
            return true;
        }
    };
}

...

@Bean
static FactoryBean<MyBean> myBean() {
    return preventAutowire(new MyBean());
}
like image 24
Fabio Manzano Avatar answered Oct 12 '22 11:10

Fabio Manzano