Application Context not injected in an `#Configuration` annotated class - java

I have a setup like:
#Configuration
public class MyConfig {
#Autowired
ApplicationContext applicationContext;
#Bean
public MyBean myBean() {
return applicationContext.getAutowireCapableBeanFactory().createBean(MyBean.class);
}
}
Soon as I launch my Application, Spring tries to create myBean even before injecting applicationContext. As the latter is null, it will cause a NullPointerException in the method myBean().
Strangely, injecting the Application Context works in some other #Configuration classes, but not in the one mentioned above.
Is there a way force the injection of applicationContext before the method myBean() is called?

I don't think that you have the guarantee that the fields declared with #Autowired be always loaded before the invocation of the methods declared with #Bean .
To ensure that the ApplicationContext object is valued, you could make your configuration class implement ApplicationContextAware.
#Configuration
public class MyConfig implements ApplicationContextAware {
private ApplicationContext context;
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
...
}

Related

about the order of spring bean initialing

I have a custom spring-boot starter project which is used by rest controller, the auto configuration class is used for creating several(according to config value in application.yml) Storage Context instance as spring singleton, so I have to create them dynamically in setBeanFactory method of BeanFactoryAware by :
#Import(StorageContextProperties.class)
public class StorageAutoConfiguration implements BeanFactoryAware {
......
#Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
......
StorageContextProperties config = beanFactory.getBean(StorageContextProperties.class);
config.getProfiles().stream().forEach(p -> {
......
((SingletonBeanRegistry) beanFactory).registerSingleton(profile.name, instance);
the problem is that the method is not been called before the controller #autowired event, so it will complain there is no StorageContext instance, I have also tried BeanFactoryPostProcessor and InitializingBean interface, neither of them works.
But, I notice if I just add some special #Bean method into the auto config class, let's say :
#Import(StorageContextProperties.class)
public class StorageAutoConfiguration implements BeanFactoryAware{
#Bean
public MethodValidationPostProcessor validationPostProcessor2() {
return new MethodValidationPostProcessor();
}
......
#Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
then the setBeanFactory() will be called before the controller, it seems that spring needs MethodValidationPostProcessor instance, so it created shared instance of singleton bean: StorageAutoConfiguration, also create StorageContextProperties instance and call setBeanFactory().
the code works if I add above #Bean method. well things also goes well in this way, but I don't like the style since I actually have no need for MethodValidationPostProcessor.
is there any elegant(without #Bean method) way to achieve it?
my requirements are :
before the controller creating event.
1 create StorageContextProperties ( it's a #ConfigurationProperties class by #Import)
2 some callback I can call registerSingleton() to create my Storage Context instances
Thanks!
[UPDATED1]
I still don't find any way to make it works, but I changed the code as :
#Configuration
#Import(StorageContextProperties.class)
#ConditionalOnProperty(prefix = "avalon.thiton.storage.config", name = "masterKey")
public class StorageAutoConfiguration implements BeanFactoryAware {
private static Logger log = LoggerFactory.getLogger(StorageAutoConfiguration.class);
#Bean
#Order(Ordered.LOWEST_PRECEDENCE)
#ConditionalOnMissingBean(MethodValidationPostProcessor.class)
public MethodValidationPostProcessor placeholder() {
return new MethodValidationPostProcessor();
}
......
It makes me feel..... better.
Hi I've had similar problem when I was trying to initialize custom beans using BeanFactoryAware interface - The #Configuration annotated class was like:
#Configuration
#EnableConfigurationProperties(SomeProps.class)
class MyConfiguration implements BeanFactoryAware {
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
...
...
}
#Bean
MyBean myBean(){
return new MyBean();
}
}
It did not run this configuration class code before MyBean was needed and NoSuchBeanDefinitionException were thrown.
To fix this I've changed BeanFactoryAware interface to extending AbstractBeanFactoryAwareAdvisingPostProcessor like that:
#Configuration
#EnableConfigurationProperties(SomeProps.class)
class MyConfiguration extends AbstractBeanFactoryAwareAdvisingPostProcessor {
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
...
...
}
#Bean
MyBean myBean(){
return new MyBean();
}
}

How to get bean using application context in spring boot

I am developing a SpringBoot project and I want to get the bean by its name using applicationContext. I have tried many solution from web but could not succeed. My Requirement is that I have a controller
ControllerA
and inside the controller I have a method getBean(String className). I want to get instance of registered bean. I have hibernate entities and I want to get an instance of the bean by passing the name of class only in getBean method.
Please help if someone know the solution.
You can Autowire the ApplicationContext, either as a field
#Autowired
private ApplicationContext context;
or a method
#Autowired
public void context(ApplicationContext context) { this.context = context; }
Finally use
context.getBean(SomeClass.class)
You can use ApplicationContextAware.
ApplicationContextAware:
Interface to be implemented by any object that wishes to be notified
of the ApplicationContext that it runs in. Implementing this interface
makes sense for example when an object requires access to a set of
collaborating beans.
There are a few methods for obtaining a reference to the application context. You can implement ApplicationContextAware as in the following example:
package hello;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
Update:
When Spring instantiates beans, it looks for ApplicationContextAware implementations, If they are found, the setApplicationContext() methods will be invoked.
In this way, Spring is setting current applicationcontext.
Code snippet from Spring's source code:
private void invokeAwareInterfaces(Object bean) {
.....
.....
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
}
}
Once you get the reference to Application context, you get fetch the bean whichever you want by using getBean().
actually you want to get the object from the Spring engine, where the engine already maintaining the object of your required class at that starting of the spring application(Initialization of the Spring engine).Now the thing is you just have to get that object to a reference.
in a service class
#Autowired
private ApplicationContext context;
SomeClass sc = (SomeClass)context.getBean(SomeClass.class);
now in the reference of the sc you are having the object.
Hope explained well. If any doubt please let me know.
Even after adding #Autowire if your class is not a RestController or Configuration Class, the applicationContext object was coming as null. Tried Creating new class with below and it is working fine:
#Component
public class SpringContext implements ApplicationContextAware{
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws
BeansException {
this.applicationContext=applicationContext;
}
}
you can then implement a getter method in the same class as per your need to get the bean. Like:
applicationContext.getBean(String serviceName,Interface.Class)
Using SpringApplication.run(Class<?> primarySource, String... arg) worked for me. E.g.:
#SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(YourApplication.class, args);
}
}
As an alternative approach you can use ConfigurableApplicationContext to get bean of any class which is annotated with #Component, #Repository or #Service.
Let's say you want to get a bean of the class BaseComponent :
#Service
public class BaseComponent {
public String getMessage() {
return "hello world";
}
}
Now you can use ConfigurableApplicationContext to get the bean:
#Component
public class DemoComponent {
#Autowired
ConfigurableApplicationContext applicationContext;
public BaseComponent getBeanOfBaseComponent() {
return applicationContext.getBean(BaseComponent.class);
}
}
You can use the ApplicationContextAware class that can provide the application context.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext ctx = null;
public static ApplicationContext getApplicationContext() {
return ctx;
}
#Override
public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
ApplicationContextProvider.ctx = ctx;
}
/**
* Tries to autowire the specified instance of the class if one of the specified
* beans which need to be autowired are null.
*
* #param classToAutowire the instance of the class which holds #Autowire
* annotations
* #param beansToAutowireInClass the beans which have the #Autowire annotation
* in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
ctx.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
}
}
}
If you are inside of Spring bean (in this case #Controller bean) you shouldn't use Spring context instance at all. Just autowire className bean directly.
BTW, avoid using field injection as it's considered as bad practice.
One API method I use when I'm not sure what the bean name is org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType(java.lang.Class<?>). I simple pass it the class type and it retrieves a list of beans for me. You can be as specific or general as you'd like to retrieve all the beans associated with that type and its subtypes, example
#Autowired
ApplicationContext ctx
...
SomeController controller = ctx.getBeanNamesForType(SomeController)
Easy way in configration class call the BEAN annoted method . Yes u heard it right---- :P calling SpringBoot #Bean annoted method return the same bean from config .I was trying to call a logout in #predestroy method in config class from a bean and direcltly called the method to get the same bean .
P.S. : I added debug in the #bean annotated method but it didn't entered the method even when i called it.Sure to blame -----> Spring Magic <----
You can use ServiceLocatorFactoryBean. First you need to create an interface for your class
public interface YourClassFactory {
YourClass getClassByName(String name);
}
Then you have to create a config file for ServiceLocatorBean
#Configuration
#Component
public class ServiceLocatorFactoryBeanConfig {
#Bean
public ServiceLocatorFactoryBean serviceLocatorBean(){
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(YourClassFactory.class);
return bean;
}
}
Now you can find your class by name like that
#Autowired
private YourClassfactory factory;
YourClass getYourClass(String name){
return factory.getClassByName(name);
}
Just use:
org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)
Example:
#Component
public class Example {
#Autowired
private ApplicationContext context;
public MyService getMyServiceBean() {
return context.getBean(MyService.class);
}
// your code uses getMyServiceBean()
}

#Autowired Spring NullPointerException Null Beans after ApplicationContext Creation [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 8 years ago.
When I create my ApplicationContext the myBean Constructors are used successfully.
But after creation the beans are null using #Autowired tag.
I though #Autowired would replace getBean() somehow? Am I getting this wrong?
Why do I need to call GetBean, when I already created my Beans (during ApplicationContext startup) and also Autowired them?
Here is what I have done so far:
Main:
#EnableAutoConfiguration
#Configuration
#ComponentScan("com.somePackage")
public class Main {
ApplicationContext ctx= new AnnotationConfigApplicationContext(AppConfig.class);
SomeBean myBean = ctx.getBean(SomeBean.class);
myBean.doSomething();//succeeds
AnyOtherClass aoc = new AnyOtherClass();//Nullpointer (see AnyOtherClass.class for details)
}
AnyOtherClass:
public class AnyOtherClass {
#Autowired
protected SomeBean someBean;
public AnyOtherClass(){
someBean.doSomething();//Nullpointer
}
AppConfig:
#Configuration
public class AppConfig {
#Bean
#Autowired
public SomeBean BeanSomeBean() {
return new SomeBean();
}
}
MyBean:
public class SomeBean {
public SomeBean (){}
public void doSomething() {
System.out.println("do Something..");
}
}
Note: Interface ApplicationContextAware works fine but without #Autowired. And I would really like to go with #Autowired since it sounds more comfortable and less errorprone to me.
For #Autowired to work in AnyOtherClass, AnyOtherClass needs to be a Spring bean.
Something like this
AppConfig
#Configuration
public class AppConfig {
#Bean
#Autowired
public SomeBean BeanSomeBean() {
return new SomeBean();
}
#Bean
#Autowired
public AnyOtherClass BeanAnyOtherClass() {
return new AnyOtherClass();
}
}
Main
#EnableAutoConfiguration
#Configuration
#ComponentScan("com.somePackage")
public class Main {
ApplicationContext ctx= new AnnotationConfigApplicationContext(AppConfig.class);
SomeBean myBean = ctx.getBean(SomeBean.class);
myBean.doSomething();//succeeds
AnyOtherClass aoc = ctx.getBean(AnyOtherClass.class);
}
If you instantiate a class using new AnyOtherClass(), it will bypass Spring and none of the annotations will work. You must get it out of Spring context in order for it to be a Spring bean.
Try
#Component
public class AnyOtherClass {
#Autowired
protected SomeBean someBean;
public AnyOtherClass(){
someBean.doSomething();//Nullpointer
}
Besides, you need to make sure this class is in com.somePackage package.

Spring Boot Autowired null

I have several classes in a Spring Boot project, some work with #Autowired, some do not. Here my code follows:
Application.java (#Autowired works):
package com.example.myproject;
#ComponentScan(basePackages = {"com.example.myproject"})
#Configuration
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages = "com.example.myproject.repository")
#PropertySource({"classpath:db.properties", "classpath:soap.properties"})
public class Application {
#Autowired
private Environment environment;
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public SOAPConfiguration soapConfiguration() {
SOAPConfiguration SOAPConfiguration = new SOAPConfiguration();
SOAPConfiguration.setUsername(environment.getProperty("SOAP.username"));
SOAPConfiguration.setPassword(environment.getProperty("SOAP.password"));
SOAPConfiguration.setUrl(environment.getProperty("SOAP.root"));
return SOAPConfiguration;
}
HomeController (#Autowired works):
package com.example.myproject.controller;
#Controller
class HomeController {
#Resource
MyRepository myRepository;
MyService (#Autowired does not work):
package com.example.myproject.service;
#Service
public class MyServiceImpl implements MyService {
#Autowired
public SOAPConfiguration soapConfiguration; // is null
private void init() {
log = LogFactory.getLog(MyServiceImpl.class);
log.info("starting init, soapConfiguration: " + soapConfiguration);
url = soapConfiguration.getUrl(); // booom -> NullPointerException
I do not get the SOAPConfiguration but my application breaks with a null pointer exception when I try to access it.
I have already read many Threads here and googled around, but did not find a solution yet. I tried to deliver all necessary information, please let me know if anything misses.
I guess you call init() before the autowiring takes place. Annotate init() with #PostConstruct to make it call automatically after all the spring autowiring.
EDIT: after seeing your comment, I guess you are creating it using new MyServiceImpl(). This takes away the control of the MyServiceImpl from Spring and gives it to you. Autowiring won't work in those case
Did you created a bean for the class SOAPConfiguration in any of your configuration classes? If you want to autowire a class in your project, you need to create a bean for it. For example,
#Configuration
public class SomeConfiguration{
#Bean
public SOAPConfiguration createSOAPConfiguration(){
return new SOAPConfiguration();
}
}
public class SomeOtherClass{
#Autowired
private SOAPConfiguration soapConfiguration;
}

How to inject ApplicationContext itself

I want to inject an ApplicationContext itself to a bean.
Something like
public void setApplicationContext(ApplicationContect context) {
this.context = context;
}
Is that possible in spring?
Previous comments are ok, but I usually prefer:
#Autowired private ApplicationContext applicationContext;
Easy, using the ApplicationContextAware interface.
public class A implements ApplicationContextAware {
private ApplicationContext context;
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
}
Then in your actual applicationContext you only need to reference your bean.
<bean id="a" class="com.company.A" />
Yes, just implement the ApplicationContextAware -interface.
I saw some comments above about #Autowired not working still. The following may help.
This will not work:
#Route(value = "content", layout = MainView.class)
public class MyLayout extends VerticalLayout implements RouterLayout {
#Autowired private ApplicationContext context;
public MyLayout() {
comps.add(context.getBean(MyComponentA.class)); // context always null :(
}
You must do this:
#Autowired
public MyLayout(ApplicationContext context) {
comps.add(context.getBean(MyComponentA.class)); //context is set :)
}
or this:
#PostConstruct
private void init() {
comps.add(context.getBean(MyComponentA.class)); // context is set :)
}
Also note that Upload is another component that must be set within the scope of #PostConstruct. This was a nightmare for me to figure out. Hope this helps!
I almost forgot to mention that the #Scope annotation may be necessary for your Bean, as seen below. This was the case when using Upload within a Bean because the UI is not instantiate/attached prior to the Bean being created and will cause a Null Reference Exception to be thrown. It won't do so when using #Route, but will when using #Component - so the latter is not an option and if #Route is not viable, then I would recommend using #Configuration class to create the bean with the prototype scope.
#Configuration
public class MyConfig {
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyComponentA getMyBean() {
return new MyComponentA();
}
}
Special solution: get Spring beans from any (non Spring) classes
#Component
public class SpringContext {
private static ApplicationContext applicationContext;
#Autowired
private void setApplicationContext(ApplicationContext ctx) {
applicationContext = ctx;
}
public static <T> T getBean(Class<T> componentClass) {
return applicationContext.getBean(componentClass);
}
}

Categories