spring autowiring not working from a non-spring managed class
Asked Answered
P

11

44

I have a class (Class ABC) that's instantiated by calling the constructor. Class ABC in turn has a helper class (Class XYZ) injected using auto-wired.

Ours is a Spring MVC based application and I don't see any exception while server start-up.

But I still see Class XYZ coming as null. Is it because of the fact that Class ABC is not instantiated by Spring Container?

In such scenarios, how do i make use of auto-wiring?

Thanks.

Popsicle answered 21/8, 2013 at 1:31 Comment(2)
I am sure you cannot Autowire beans which are not managed by Spring.. You need to get hold of the reference of an instance of XYZ by some other mean. If it is a Helper class make the methods of XYZ static and use them using the class name. That is what I did.Charters
Please refer to github.com/ahmedbhaila/non-managed-beans, I have tried and it works.Tuck
A
53

You can use this way to use spring bean in non-spring bean class

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextUtils implements ApplicationContextAware {
     
      private static ApplicationContext ctx;
     
      @Override
      public void setApplicationContext(ApplicationContext appContext) {
        ctx = appContext;
      }
     
      public static ApplicationContext getApplicationContext() {
        return ctx;
      }
}

now you can get the applicationcontext object by getApplicationContext() this method.

from applicationcontext you can get spring bean objects like this:

ApplicationContext appCtx = ApplicationContextUtils.getApplicationContext();
String strFromContext = appCtx.getBean(beanName, String.class);
Arminius answered 28/8, 2013 at 11:6 Comment(7)
How to set appContext?Stein
Spring container will inject that object .Arminius
In my case, I need add @Component on ApplicationContextUtils, or only get null during ApplicationContextUtils .getApplicationContext();Granulose
Awesome solution!Sargasso
This solution begs the question of the OP. ApplicationContextAware only works for classes which are Spring Beans themselves (thus annotated with @Component). But the OP asked explicitely for classes that are not Spring Beans i. e. not managed by Spring!Discoverer
@PeterWippermann Not sure if your comment is still up to date, but the idea here would be that class ABC which is not managed by Spring would use the class proposed by this answer to get the dependency. So no, the original class ABC does not need to be ApplicationContextAware.Methacrylate
another option to do it is creating a field in the startup class (usually Application): public static ApplicationContext context and then in the main: context = SpringApplication.run(Application.class, args). Then you can get a bean from everywhere using Application.context.getBean(XYZ.class) or similarWiper
M
12

Autowiring won't work because class ABC is not managed by Spring. You could make Spring manage ABC by using one of the @Component annotations (@Component, @Service, @Controller, etc) above the class definition, and then using context:component-scan in your application context XML, or go old school and just define the bean directly in your application context.

If for some reason you can't make Spring manage class ABC, you can load the application context in ABC using something like:

ApplicationContext context = new ClassPathXmlApplicationContext("path/to/applicationContext.xml");

and then use:

XYZ someXyz = (XYZ) context.getBean("MyXYZ");

to manually set the bean value.

Matterhorn answered 21/8, 2013 at 2:1 Comment(0)
B
5

Correct: You can't just call new on a class and get it all wired up; Spring has to be managing the bean for it to do all of its magic.

If you can post more details on your use case, we may be able to suggest useful options.

Brabazon answered 21/8, 2013 at 1:45 Comment(1)
U
4

You can use Spring's @Configurable annotation in the class you want to autowire other beans. Additionally, you will need to annotate any configuration bean with @EnableSpringConfigured so that Spring is aware of your configurable beans.

@EnableSpringConfigured documentation

public @interface EnableSpringConfigured Signals the current application context to apply dependency injection to non-managed classes that are instantiated outside of the Spring bean factory (typically classes annotated with the @Configurable annotation). Similar to functionality found in Spring's XML element. Often used in conjunction with @EnableLoadTimeWeaving.

@Configurable(autowire = Autowire.BY_TYPE)
public class ABC {
    @Autowire private XYZ xyz;
    ...
}

@Configuration
@EnableSpringConfigured
public class Application {
    ...
}

public class MyClass {
    public void doSomething() {
        ABC abc = new ABC(); // XYZ is successfully autowired
        ...
    }
}
Unstudied answered 13/9, 2016 at 18:31 Comment(1)
Hi ibai, What's MyClass for? It's also a non-spring managed object. Could we new ABC() in the Application class? Thanks.Tuck
L
4

For noobs like me who do basic Spring Boot and aren't familiar with the lingo:

  • Your Services, Repos etc are all Beans
  • You can get your Beans from the ApplicationContext

Ashish's answer works for me, but this article provides a bit more explanation imo.

If you don't know the name of the bean you want, try looking in this array:

String[] names = context.getBeanDefinitionNames();

If you're confused by the talk of 'component scan' and config files, it may help to know that the @SpringBootApplication annotation (which you might find near your main() method) implicitly calls @Configuration and @ComponentScan.

This means all files in that package (declared at the top of the main class) are picked up by Spring, and any beans you want to add can be written alongside main()

Lass answered 22/8, 2018 at 11:19 Comment(0)
B
3

In short, yes, ABC is not getting injected with XYZ because Spring is not managing ABC. Spring can't configure objects that it doesn't know about.

You could manage ABC by annotating it with @Service or @Component. Note that in order for Spring to pick up on these annotations, Spring must have auto-scanning turned on:

<context:component-scan base-package="com.mypackage.awesomeproject" />
Baziotes answered 21/8, 2013 at 1:45 Comment(0)
P
1

First question - yes you have null because class not initiated with spring Second question - I think you can use aspectj support http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-using-aspectj

Pirtle answered 21/8, 2013 at 7:52 Comment(0)
P
0

You can annotate ABC class with @Configurable annotation. Then Spring IOC will inject XYZ instance to ABC class. It's typically used with the AspectJ AnnotationBeanConfigurerAspect.

Pepin answered 21/8, 2013 at 8:56 Comment(0)
M
0

Spring has util class

 BeanUtils.instantiateClass(clazz)
 BeanUtils.instantiate(clazz)

 YouClass ins = BeanUtils.instantiate(YouClass.class)

https://docs.spring.io/autorepo/docs/spring/4.0.2.RELEASE/javadoc-api/org/springframework/beans/BeanUtils.html

Martian answered 29/5, 2020 at 9:33 Comment(1)
Be aware that those methods instantiate a class using its no-arg constructor. You can get a BeanInstantiationException if no primary/default constructor was found.Wedge
W
0

Another option could be to get the context in the startup class (usually Application) and save it there:

public class Application {
    public static ApplicationContext context;
    public static void main(String[] args) {
        context = SpringApplication.run(Application.class, args);
    }
}

Then you can get a bean from everywhere using Application.context.getBean("MyXYZ") or similar

Wiper answered 13/11, 2023 at 22:18 Comment(0)
E
-2

In response to the answer provided by @Ashish Chaurasia, would like to mention that the solution is partial. The class ApplicationContextUtils should also be a spring bean in order for spring to invoke the below code.

if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(ctx); 
}

@Component at the top of the class would make the solution complete. Also, there is one more alternative of doing it with the @Autowired annotation.

@Component
public class ApplicationContextProvider {
    private static ApplicationContext context;

    public static ApplicationContext getApplicationContext() {
        return context;
    }

    @Autowired
    public void setContext(ApplicationContext context) {
        ApplicationContextProvider.context = context;
    }
}

The getBean method can now easily be accessed by -

ApplicationContextProvider.getApplicationContext().getBean("myBean");

Encaenia answered 21/8, 2019 at 17:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.