Programmatically bind instance of class to qualifier in spring - java

I have instances of the same class being created from config values such that I need them to be injected/autowired from that config value. I was doing it in guice like:
for (String s : config.getStrings()) {
bind(Foo.class).annotatedWith(Names.named(s)).toInstance(new Foo(s));
}
Is there any way to do something like this in Spring or is there a way to use something like a FactoryBean to create instances of a class given a qualifier?

public final class FLEXSyncFactory implements ApplicationContextAware {
private static AbstractApplicationContext context;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context = (AbstractApplicationContext) applicationContext;
}
And to register alias of bean
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
Foo fooBean = beanFactory.getBean(Foo.class);
beanFactory.initializeBean(fooBean, "TheAliasForBeanNameYouNeed");
beanFactory.autowireBeanProperties(fooBean, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);

Related

AnnotationConfigApplicationContext has not been refreshed yet using ApplicationContextAware

I am using Spring beans in a non-spring framework and for that I have implemented the ApplicationContextAware to access the spring beans.
#Service
public class ApplicationContextProviderService implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextProviderService.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> beanType) {
System.out.println("bean out: " + applicationContext);
return applicationContext.getBean(beanType);
}
}
I try to access a Spring service: ConnectionStateService from the non-spring class:
this.connectionStateService = ApplicationContextProviderService.getBean(ConnectionStateService.class);
I get the following error:
java.lang.IllegalStateException:
**org.springframework.context.annotation.AnnotationConfigApplicationContext#7f485fda has not been refreshed yet at
** org.springframework.context.support.AbstractApplicationContext.assertBeanFactoryActive(AbstractApplicationContext.java:1072) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE] at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1102) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE] at com.example.app.service.ApplicationContextProviderService.getBean(ApplicationContextProviderService.java:19) ~[classes/:na] at com.example.app.CustomFilter.<init>(CustomFilter.java:26) ~[classes/:na]
How to resolve this issue?
The issue is related when the application tries to call to ApplicationContext before the application runs, so you won't be able to have application context because it wasn't created. The solution is create a singleton for ConnectionStateService class so you won't need to create a service for calling the ApplicationContext.
public class ConnectionStateService {
// static variable instance of type Singleton
private static ConnectionStateService single_instance = null;
// private constructor restricted to this class itself
private ConnectionStateService() {
// some stuffs for initialize here
}
// static method to create instance of Singleton class
public static ConnectionStateService getInstance() {
if (instance == null)
instance = new ConnectionStateService();
return instance;
}
}
I think you are getting the bean when the context is not initialized
So actually be sure you are invoking this piece of code :
this.connectionStateService = ApplicationContextProviderService.getBean(ConnectionStateService.class);
after context is initialized

Not able to load Application Context in Spring Boot app

I am working on a Spring Boot application wherein I am using that application to expose a SOAP webservice. I am using Apache CFX framework for SOAP impl in Spring boot app. I am using Annotation based approach.
I am facing issue in setting the Application Context from the Spring Boot Configuration file in one of the Beans. Below is my code.
#SpringBootApplication
#ComponentScan("com.test")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The configuration file is as below.
#Configuration
public class WebServiceConfiguration {
//All individual bean definitions should go here
#Autowired
ApplicationContext appContext;
#Bean
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
#Bean(name="IValidator")
public IValidator getValidator(){
return new Validator();
}
#Bean(name="SOAPprocessImpl")
public IPSoap getService() {
return new SOAPprocessImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), getService());
endpoint.publish("/WS_1.0");
endpoint.setWsdlLocation("process.wsdl");
return endpoint;
}
Now I have the bean SOAPprocessImpl implementation in which I need to get the Application Context so that I can get handle to the Validator bean. I have declared SOAPprocessImpl as a bean in the configuraton file. The code is as below
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap, ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
context = ac;
}
private static final Logger logger = Logger.getLogger(SOAPprocessImpl.class.getName());
private IValidator validator = (IValidator) context.getBean("IValidator"); // context is NULL here
public IRResponse GetBalance(TSSearchParams SearchParams) {
// Some processing logic
}
}
So the issue is that when I run the boot application by deploying to the embedded Tomcat then the Application Context is not getting set in the SOAPprocessImpl class even after implementing the ApplicationContextAware. I also tried Autowiring but that also is not working.
Strangely I tried to see if I can get the ApplicationContext in the Configuration file where all the bean are defined. Here it is getting setting properly.
Can anyone help me how to solve this issue. I am new to Spring Boot and may have missed some configutaion. Thanks in advance.
Option(1): To fix the issue, you need to use #Configuration to register your SOAPprocessImpl bean to the Spring container as shown below so that ApplicationContext object can be injected :
#Configuration
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap, ApplicationContextAware {
private static ApplicationContext context;
private IValidator validator;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
SOAPprocessImpl.context = ac;
}
#PostConstruct//use PostConstruct
public void init() {
validator = (IValidator) context.getBean("IValidator");
}
//add your current code
}
The important point is that you can't use the context object until the bean is prepared by the container, so you need to use #PostConstruct method as shown above to initialise your variables.
Option2 (recommended):
The best approach is that you can use #Autowired to inject IValidator object into SOAPprocessImpl as shown below so that you don't need your SOAPprocessImpl bean to be aware of ApplicationContextAware. Spring container will inject the instance for the implementation provided for the IValidator class (provided it is under the packages of #Componentscan).
#Component
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap {
private static final Logger logger = Logger.getLogger(SOAPprocessImpl.class.getName());
#Autowired //spring directly injects this object
private IValidator validator;
public IRResponse GetBalance(TSSearchParams SearchParams) {
// Some processing logic
}
}

How to programatically add an #Bean definition to Spring context?

Normally I'm adding my objects to spring context using #Bean definition:
#Autowired
private SpringBus bus;
//register a singleton
#Bean
public WebservicePort getPort() {
//new port()
//initialize
//configure
//return port;
}
But now I need deeper control of the process, especially I want to create the bean name dynamically under which the bean is registered.
I tried:
#Service
public class MyPortRegistrar implements BeanDefinitionRegistryPostProcessor {
#Autowired
private SpringBus bus;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println(bus); //prints null
//create and configure port with the SpringBus
Port port = new WebservicePort(bus); // -> throws NullPointerException
beanFactory.autowireBean(port);
beanFactory.initializeBean(port, "myDynamicPortName");
}
}
But this throws an NPE as the autowired dependecies are not yet initialized here!
So, how can I add those beans programatically?
You should autowire the bean factory, and use a #PostConstruct to register your bean. That way, you guarantee that all dependencies has been injected (the bean factory is injected by the container, no setup is needed).
#Service
public class MyPortRegistrar {
#Autowired
private ConfigurableBeanFactory beanFactory;
#Autowired
private SpringBus bus;
#PostConstruct
public void createPort() {
Port port = new WebservicePort(bus);
beanFactory.registerSingleton("myDynamicPortName", port);
}
}
You should put this before:
beanFactory.autowireBean(port);
But, if you want to initialize a bean, I think that you want to create a single instance (I'm saying this because in the example, you used the #Bean annotation):
beanFactory.initializeBean(port, "myDynamicPortName");
instead of a singleton one:
beanFactory.registerSingleton("myDynamicPortName", port);
An alternative to Khalid's answer (which I think requires some additional dependencies and configuration) is to implement the InitializingBean interface:
#Service
public class MyPortRegistrar implements InitializingBean {
#Autowired
private SpringBus bus;
#Autowired
private ConfigurableBeanFactory beanFactory;
#Override
public void afterPropertiesSet() throws Exception
System.out.println(bus); //prints null
//create and configure port with the SpringBus
Port port = new WebservicePort(bus); // -> throws NullPointerException
beanFactory.autowireBean(port);
beanFactory.initializeBean(port, "myDynamicPortName");
}
}

Obtaining config values from within non-spring managed bean

I'm using annotation-configuration, not XML for my application...
#Configuration
#ComponentScan(basePackages = {
"com.production"
})
#PropertySource(value= {
"classpath:/application.properties",
"classpath:/environment-${COMPANY_ENVIRONMENT}.properties"
})
#EnableJpaRepositories("com.production.repository")
#EnableTransactionManagement
#EnableScheduling
public class Config {
#Value("${db.url}")
String PROPERTY_DATABASE_URL;
#Value("${db.user}")
String PROPERTY_DATABASE_USER;
#Value("${db.password}")
String PROPERTY_DATABASE_PASSWORD;
#Value("${persistenceUnit.default}")
String PROPERTY_DEFAULT_PERSISTENCE_UNIT;
In this file I've noticed I can obtain configuration values from the #PropertySource files. How can I obtain these values outside of a spring managed bean?
Is it possible for me to use my ApplicationContextProvider to obtain these values?
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext (ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
If I understand you correctly, yes you can use your ApplicationContextProvider class. #PropertySource properties end up in the ApplicationContext Environment. You can therefore access them like
public static class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext (ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
Environment env = applicationContext.getEnvironment();
System.out.println(env.getProperty("db.user")); // access them
}
}
So basically anywhere you have a reference to the ApplicationContext, you can get the properties declared in a #PropertySources or a PropertySourcesPlaceholderConfigurer.
However, in this case, the ApplicationContextProvider will have to be declared as a Spring bean in your context.
#Bean
public ApplicationContextProvider contextProvider() {
return new ApplicationContextProvider();
}

Inject spring bean in custom el functions

i want to create a custom el functions to get in a fast way select options from dao. I'm using Spring and i want to inject spring bean dao in my custom el functions class.
In el functions class i'm using static methods and i'm unable to access application context.
I used an implementation of ApplicationContextAware in this way
public class AppContextUtil implements ApplicationContextAware
{
private ApplicationContext applicationContext;
private static final AppContextUtil instance=new AppContextUtil();
private AppContextUtil()
{
}
public static AppContextUtil getInstance()
{
return instance;
}
public <T> T getBean(Class<T> clazz)
{
return applicationContext.getBean(clazz);
}
/**
* {#inheritDoc}
*/
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
}
but applicationContext is null.
The only way to access to applicationContext is as belove
WebApplicationContext appCtx =
WebApplicationContextUtils.getWebApplicationContext(context.getServletContext());
MyDAO myDAO = appCtx.getBean(MyDAO.class);
but in this way i need to pass PageContext in el functions params.
How i can create an el functions class with spring bean support? how i can access in static way to applicationContext?
Thank you.
A dirty solution to "inject" a bean or Application Context into an static field:
#Component
public class AppContextUtil {
private static ApplicationContext applicationContext;
#Autowire
private set ApplicationContext(ApplicationContext applicationContext) {
AppContextUtil.applicationContext = applicationContext;
}
}

Categories

Resources