Programmatically set a specific bean object - Spring DI - java

In my program I need to programmatically configure an ApplicationContext. Specifically, I have a reference to an instance of MyClass and I want to define it as a new bean called "xxyy".
public void f(MyClass mc, ApplicationContext ac) {
// define mc as the "xxyy" bean on ac ???
...
...
// Now retrieve that bean
MyClass bean = (MyClass) ac.getBean("xxyy");
// It should be the exact same object as mc
Assert.assertSame(mc, bean);
}
The BeanDefinition API let's me specify the class of the new bean, so it does not work for me since I want to specify the instance.
I managed to find a solution but it took two additional factory beans which seems like too much code for such an eartly purpose.
Is there a standard API that addresses my needs?

You need to jump through a few hoops to do this. The first step is to obtain a reference to the context's underlying BeanFactory implementation. This is only possible if your context implements ConfigurableApplicationContext, which most of the standard ones do. You can then register your instance as a singleton in that bean factory:
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext)appContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
beanRegistry.registerSingleton("xxyy", bean);
You can "insert" any object into the context like this.

you can use this context:
GenericApplicationContext mockContext = new GenericApplicationContext();
which has a
mockContext.getBeanFactory().registerSingleton("name", reference);
and plug it in the real context
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "real-context.xml" }, mockContext);
and the classes are:
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

Related

Spring declare multiple beans

I have an HashMap of myobjects which I want to loop through and declare each one of them as bean. At the end I want to choose one particular key as #primary.
Is there anyway to do this in Spring Java Config?
I can't really do this in listeners as these beans are required when context loads up. I was looking at factory beans but could not figure it out.
Any help is appreciated.
Thanks
Abhi
You can create a set of beans by getting a reference to ApplicationContext in one of your Java configuration classes. For example,
#Configuration
public class ServicesConfig {
#PostConstruct
public void onPostConstruct() {
Map<String, MyClass> mapOfClasses = HashMap<>(); // your HashMap of objects
AutowireCapableBeanFactory autowireCapableBeanFactory = context.getAutowireCapableBeanFactory();
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) autowireCapableBeanFactory;
for (Map.Entry<String, MyClass> myClassEntry : mapOfClasses.entrySet()) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(myClassEntry.getValue().getClass());
beanDefinition.setAutowireCandidate(true);
registry.registerBeanDefinition(myClassEntry.getKey(), beanDefinition);
autowireCapableBeanFactory.autowireBeanProperties(myClassEntry,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
}
}
}
Here MyClass is the type of your myobject and each MyClass can also have #Autowired annotations. At this point, I assume that each of these objects will be given their bean name the key of the HashMap. This objects then can be used as dependencies for other beans if you want.
Same thing can be achieved by implementing BeanDefinitionRegistryPostProcessor and overriding postProcessBeanDefinitionRegistry to register your HashMap of objects. In this method you will be able to create BeanDefinitions with BeanDefinitionBuilder.
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MyClass.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("key_of_this_object", beanDefinition);
I'm not sure if you can mark one of these beans as #Primary. But based on this post you can define your own bean resolver by extending DefaultListableBeanFactory which I haven't tested myself. As an alternative you may use #Qualifier as you already know which object is going to be the primary bean.
Hope this will help.
P.S If objects are already available to be added in to the ApplicationContext, following approach would add these custom objects to ApplicationContext
ConfigurableListableBeanFactory configurableListableBeanFactory = ((ConfigurableApplicationContext)context).getBeanFactory();
configurableListableBeanFactory.registerSingleton("key_of_this_object", myClassObject);

Using class.forname but want to autowire members of the target class

I have this requirement,
My framework is in a way that it reads the class name from the configuration file as a string and I would like to use methods inside that class.
Obvious solution is to use reflection,
I have used reflection and able to call methods I wanted, but the problem is the variables inside the target class are not autowired. I understand I am not letting spring to autowire the fields by using reflection (Spring with class.forname()).
Is there a way for me to autowire the class variables instead of creating new instance? Or Am I in a deadlock situation?
Option 1: If you have access to the current Spring ApplicationContext, you could do this as follows:
String className = <load class name from configuration>
Class<?> clazz = Class.forName(className);
ApplicationContext applicationContext = <obtain Spring ApplicationContext>
applicationContext.getBean(clazz);
This of course means that the class whose instance you wish to load is a Spring managed bean. Here is a concrete example:
package org.example.beans;
#Component
class Foo { ... }
#Component
class SpringApplicationContext implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
#Override
public void setApplicationContext(final ApplicationContext context) throws BeansException
CONTEXT = context;
}
public static <T> T getBean(String className) {
return CONTEXT.getBean(Class.forName(className));
}
}
Option 2: You could manually create an instance of the required class and then ask Spring to populate its dependencies.
This again requires access to the ApplicationContext. For example:
T object = Class.forName(className).newInstance();
applicationContext..getAutowireCapableBeanFactory().autowireBean(object);
It's possible. Have a look at how Spring's JUnit test integration does it. That's in the spring-test module.
The runner is SpringJUnit4ClassRunner, but the actual injection code is in DependencyInjectionTestExecutionListener.injectDependencies. It uses a Spring context that implements AutowriteCapableBeanFactory.
The code to do this looks like below. Note that this assumes that you have used annotations to indicate which fields need to be autowired.
Object bean = ...;
AutowireCapableBeanFactory beanFactory = ...;
beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
beanFactory.initializeBean(bean, "beanName");

Spring - passing a custom instance to a constructor

I'm trying to implement a command-query design pattern into
a MVC spring based application.
I have, for example, some decorated commands using decorator pattern
like bellow:
ICommandHandler handler =
new DeadlockRetryCommandHandlerDecorator<MoveCustomerCommand>(
new TransactionCommandHandlerDecorator<MoveCustomerCommand>(
new MoveCustomerCommandHandler(
new EntityFrameworkUnitOfWork(connectionString),
// Inject other dependencies for the handler here
)
)
);
How can I inject such a handler into a controller constructor? Where should
I instantiate this handler? A place where this can be instantiated can be
the controller constructor, but this isn't the best solution. Any other ideeas?
Thanks
If you're using PropertyPlaceholderConfigurer (old) or PropertySourcesPlaceholderConfigurer (new), and your connection string is in a .properties file or environment variable you can do the following for the connection string. You can also autowire objects into a configuration class and annotate a method with #Bean to do what the Spring context xml does. With this approach you can create your beans as you wish and they're available to autowire just like you defined them in xml.
#Configuration
public class MyAppConfig {
#Autowired private MyType somethingToAutowire;
#Bean
public ICommandHandler iCommandHandler(#Value("${datasource.connectionString}")
final String connectionString) {
return new DeadlockRetryCommandHandlerDecorator<MoveCustomerCommand>();
// You obviously have access to anything autowired in your configuration
// class. Then you can #Autowire a ICommandHandler type into one of your
// beans and this method will be called to create the ICommandHandler (depending on bean scope)
}
}

Accessing cached ApplicationContext in spring

I need to fetch a singleton bean from same ApplicationContext twice in 2 different classes.
Example snippet :
CLass A {
public void foo(){
ApplicationContext context = new ClassPathXmlApplicationContext("common.spring.xml");
MyParametrizedSingletonClass myParametrizedSingletonClass = (MyParametrizedSingletonClass) context.getBean("myParametrizedSingletonClass");
// do more stuff..
}
CLass B {
public void bar(){
ApplicationContext context = new ClassPathXmlApplicationContext("common.spring.xml");
MyParametrizedSingletonClass myParametrizedSingletonClass = (MyParametrizedSingletonClass) context.getBean("myParametrizedSingletonClass");
// do more stuff..
}
Since MyParametrizedSingletonClass is a singletom it if its constructor is called more than once for same constructor arguments it throws error.
How do I cache and reuse ApplicationContext with spring?
You are creating two different context, so even if bean is singleton it will create single instance per context,
if you want to cache application context you can create a class and provide singleton instance of application context
Autowire the bean.
By default the spring injects the autowired beans into required classes and these beans are not created an new everytime. They are singleton by default.
in common.spring.xml file for the bean name myParametrizedSingletonClass add the scope singleton to it as a parameter while defining the bean in the xml file

Injecting beans into a class outside the Spring managed context

I'm an end-user of one of my company's products. It is not very suitable for integration into Spring, however I am able to get a handle on the context and retrieve the required bean by name. However, I would still like to know if it was possible to inject a bean into this class, even though the class is not managed by Spring itself.
Clarification: The same application which is managing the lifecycle of some class MyClass, is also managing the lifecycle of the Spring context. Spring does not have any knowledge of the instance of MyClass, and I would like to some how provide the instance to the context, but cannot create the instance in the context itself.
You can do this:
ApplicationContext ctx = ...
YourClass someBeanNotCreatedBySpring = ...
ctx.getAutowireCapableBeanFactory().autowireBeanProperties(
someBeanNotCreatedBySpring,
AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT, true);
You can use #Autowired and so on within YourClass to specify fields to be injected etc.
One way to bring a bean into Spring despite its manufacture being external is to use a helper class marked as a #Configuration bean that has a method (marked with #Bean) that actually makes the instance and hands it back through Spring (which does its property injection and proxy generation at that point).
I'm not quite sure what scope you need; with prototype, you'll get a fresh bean in each place.
#Configuration
public class FooBarMaker {
#Bean(autowire = Autowire.BY_TYPE)
#Scope("prototype")
public FooBar makeAFooBar() {
// You probably need to do some more work in here, I imagine
return new FooBar();
}
}
You can inject properties required for manufacture into the #Configuration bean. (I use this to create instances of an interface where the name of the class to instantiate is defined at runtime.)
suppose that u have the following dependency chain:
A --> B --> C --> x --> y -- > Z
A, B, C are spring managed beans (constructed and manged by spring framework)
x, y are really simple POJOs that constructed by your application, without spring assistance
now if you want that y will get a reference to Z using spring that you need to have a 'handle' to the spring ApplicationContext
one way to do it is to implement ApplicationContextAware interface . In this case I would suggest that either A, B or C will implement this interface and will store the applicationContext reference in a static member.
so lets take Class C for example:
class C implmenets ApplicationContextAware{
public static ApplicationContex ac;
void setApplicationContext(ApplicationContext applicationContext) {
ac = applicationContext;
}
.............
}
now, in class y you should have:
(Z)(C.ac.getBean("classZ")).doSomething()
HTH -- Yonatan
Another way to do this is to us use AspectJ. This is the recommended way of injection Spring beans into non-managed objects that are created with the new operator. See this for details:
http://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html
Searching endless combos of autowire inject spring bean into pojo applicationcontextaware beanaware etc circled me back here but this didnt provide a complete enough solution for me.
This is a much better implementation/tutorial of this IMO:
I hope it helps everyone like it finally helped me.
Accessing Spring Beans from outside Spring Context
Be careful that in oldest version of Spring, there is thread-safe problem with bean factory http://jira.springframework.org/browse/SPR-4672
If you want to create an object outside the Spring context, and make that object available for injection into other beans that are in the Spring context, you can follow the steps in this article.
Basically, you create a parent application context and push your external object into this parent context as a singleton. Then you create you main application context (for example, from xml files), with the parent application context as its parent.
Object externalObject = ...
GenericApplicationContext parent = new StaticApplicationContext();
parent.getBeanFactory().registerSingleton( "externalObject", externalObject );
parent.refresh();
ApplicationContext appContext = new ClassPathXmlApplicationContext( ... , parent);
From a Spring configuration class, set a static field on the non-Spring class that needs the beans.
I have an example in my answer to a Liquibase question: https://stackoverflow.com/a/71191546/5499391

Categories

Resources