How to pass parameters dynamically to Spring beans - java

I am new to Spring.
This is the code for bean registration:
<bean id="user" class="User_Imple"> </bean>
<bean id="userdeff" class="User"> </bean>
and this is my bean class:
public class User_Imple implements Master_interface {
private int id;
private User user; // here user is another class
public User_Imple() {
super();
}
public User_Imple(int id, User user) {
super();
this.id = id;
this.user = user;
}
// some extra functions here....
}
and this is my main method to perform action:
public static void main(String arg[]) {
ApplicationContext context = new ClassPathXmlApplicationContext("/bean.xml");
Master_interface master = (Master_interface)context.getBean("user");
// here is my some operations..
int id = ...
User user = ...
// here is where i want to get a Spring bean
User_Imple userImpl; //want Spring-managed bean created with above params
}
Now I want to call this constructor with parameters, and these parameters are generated dynamically in my main methods. This is what I mean by I want to pass dynamically – not statically, like declared in my bean.config file.

If i get you right, then the correct answer is to use getBean(String beanName, Object... args) method, which will pass arguments to the bean. I can show you, how it is done for Java based configuration, but you'll have to find out how it is done for an XML based configuration.
#Configuration
public class ApplicationConfiguration {
#Bean
#Scope("prototype") // As we want to create several beans with different args, right?
String hello(String name) {
return "Hello, " + name;
}
}
// and later in your application
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
String helloCat = (String) context.getBean("hello", "Cat");
String helloDog = (String) context.getBean("hello", "Dog");
Is this what are you looking for?
UPDATE
This answer gets too much upvotes and nobody looks at my comment. Even though it's a solution to the problem, it is considered as a Spring anti-pattern and you shouldn't use it! There are several different ways to do things right using factory, lookup-method, etc.
Please use the following SO post as a point of reference:
How to instantiate Spring managed beans at runtime?

Please have a look at Constructor injection.
Also, Have a look at IntializingBean and BeanPostProcessor for other life cycle interception of a springbean.

I think the answers proposed above to use constructor injection/setter injection doesn't work perfectly for the use case you are looking for.
Spring more or less takes static argument values for constructors/setters. I don't see a way to dynamically pass values to get a Bean from Spring Container.
However, if you want to get instances of User_Imple dynamically, I would recommend using a factory class User_Imple_Factory
public class User_Imple_factory {
private static ApplicationContext context =new ClassPathXmlApplicationContext("/bean.xml");
public User_Imple createUserImple(int id) {
User user = context.getBean("User");
return new User_Imple(id, user);
}
}

Constructor injection can help you. In this case you may need to generate a POJO with ID and user as its attributes and pass POJO to constructor. In constructor injection in config file you can refer this constructor with pojo as reference. So you will be handle the dynamic value of data in ID and User.
Hope this helps !!

Perhaps letting the User_Imple be an ordinary Pojo (instead of a Spring bean) will solve your problem?
<!-- Only use User as a Spring Bean -->
<bean id="userdeff" class="User"></bean>
Java:
public static void main(String arg[])
{
ApplicationContext context =new ClassPathXmlApplicationContext("/bean.xml");
User user = context.getBean(User.class);
int id = // dynamic id
Master_interface master = new User_Imple(id, user);
}

Related

How to use Spring ObjectProvider with more than one bean definition

I am using an ObjectProvider to create instances of a prototype scope bean using the getObject() method. Something like this
#Configuration
class Config {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
SomeType typeOne() {
return new SomeType();
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
SomeType typeTwo(String param) {
return new SomeType(param);
}
}
#Service
class Service {
private ObjectProvider<SomeType> objectProvider;
public Service(
ObjectProvider<SomeType> objectProvider) {
this.objectProvider = objectProvider;
}
#Override
public String performAction() {
return getSomeType().doAction();
}
private SomeType getSomeType() {
return objectProvider.getObject();
}
}
But since there are two beans of the type that the ObjectProvider is trying to get (SomeType), I get a NoUniqueBeanDefinitionException. (And I do need the other bean of the same type, because that one I need to provide parameters using objectProvider.getObject(Object... params) )
Playing around and debugging Spring I saw that if you name your ObjectProvider exactly like your bean then it works, something like:
private ObjectProvider<SomeType> typeOne;
My question is, are there other ways to use an ObjectProvider and manage to resolve ambiguity, or is this approach the way to go?
Short answer is you just need to properly qualify the ObjectProvider you want injected, like this:
public Service(#Qualifier("typeOne") ObjectProvider<SomeType> objectProvider) {
this.objectProvider = objectProvider;
}
With Spring configuration, when you specify a bean via a method, and don't specify it's name with #Bean("NAME"), Spring uses the method name as the bean name.
Similarly, when injecting a bean that is not specified by #Qualifier("NAME"), Spring takes the injected variable as the name, if that don't exists or is not unique, you might get some exceptions informing you about this (like the NoUniqueBeanDefinitionException you facing).
So, if you match the bean name and the injected variable name you don't need to be more specific, but if you don't, #Qualifier is there to your rescue :D

Throw exception if bean is defined twice?

In my project I have different services. Each service can define its own Permissions. For each permission, a bean will created. This way, the Authorization service can inject all available permission, without actually knowing them.
The Permission definition of ServiceA will look like this:
#Configuration()
public class ServiceAPermissions extends Permissions {
private static final String BASE = "servicea";
public static final String SERVICEA_READ = join(BASE, READ);
public static final String SERVICEA_WRITE = join(BASE, WRITE);
#Bean()
Permission getReadPermission() {
return new Permission(SERVICEA_READ);
}
#Bean()
Permission getWritePermission() {
return new Permission(SERVICEA_WRITE);
}
}
ServiceB will define the following Permissions:
#Configuration()
public class ServiceBPermissions extends Permissions {
private static final String BASE = "serviceb";
public static final String SERVICEB_READ = join(BASE, READ);
#Bean()
Permission getReadPermission() {
return new Permission(SERVICEB_READ);
}
}
Obviously, this will end in a name clash of the defined beans as I have defined a bean with the name getReadPermission twice. If course I can name the methods like getServiceAReadPermission so they will be distinguished, but this only a convention, which might be ignored.
In this situation, Spring doesn't notify me about the duplicate definition, it simply will just instantiate one and ignore the other definition. Is there a way to tell Spring to throw an Exception, if a bean is defined twice? This way one would be always aware of a duplicate definition.
Alternatively, is there a way to tell spring, that it should use a random bean name instead of the method signature? I know that I can give each bean a name manually #Bean(name = "A name"), but I like to avoid that, as a dev will not be forced to do so and still might forget it.
That design does not seem very logical. A bean is supposed to be available only once, you're using it differently.
I'd suggest to provide a PermissionFactory-Bean which does what you need, along the line of
#Component
public class PermissionFactory {
public Permission createFactory() {
// create A or B permission randomly, as you wanted
}
}

How can I generate/create new spring beans at runtime with a bean definition object?

I am new to Spring, and I would like to write a beanGenerator for a template bean. I would like to use this generator to overcome thread-safe concerns. Can anyone help me to add / modify the code to make this work? It's kind of hard to describe my real issue, so I abstract the issue in the following code:
abstract class BeanDefinition {
abstract public void preprocess();
}
// now we have 1st user specific bean :
class UserSpecifiedBeanDefinition extends BeanDefinition{
#override
public void preprocess() {
// do something
}
}
// we could have more user-specific beans that extend BeanDefinition
....
// Following generator class is used to generate beans
public class BeanGenerator {
private BeanDefinition beanDefinition;
public BeanGenerator(BeanDefinition beanDefinition) {
this.beanDefinition = beanDefinition;
}
public generate() {
BeanDefinition newBean = // create new bean based on beanDefinition? how can I make this work??
newBean.preprocess();
return newBean;
}
}
// In spring.xml, I would like to use them like:
<bean id="generator1" class="com.xxx.xxx.BeanGenerator">
<constructor-arg name="beanDefinition" ref="userSpecifiedBeanDefinition"/>
</bean>
I suppose you have a constructor without parameters. Use reflection to instantiate class
Class c = BeanGenerator.class.getClassLoader().loadClass(beanDefinition.getBeanClassName());
Constructor con = c.getConstructor();
Object instance = con.newInstance();
If you have constructor with parameters the logic should be changed to select correct constructor and pass the parameters to the newInstance() call

Spring bean configuration to use singleton or prototype?

I am using Java7 and Spring3. I have below classes.
Request.java
public interface Request {
public void doProcess();
}
RequestImpl.java
#Transactional
public class RequestImpl implements Request{
private String name;
private String age;
//setters and getters
public void doProcess(){
//use name and age and call third party class which will save data into database
}
}
SpringConfig.xml
<bean id="request" class="pkg.RequestImpl.java" />
Now clients will use RequestImpl as below.
RequestImplreq = (RequestImpl)applicationContext.getBean("request");
req.setName("someName");
req.setAge("20");
req.doProcess();
Now my question is do i need to declare above RequestImpl.java scope as prototype or singleton?
Thanks!
IMHO you are not working well: processes and data to process should be separated (Can DTOs be spring managed beans?) so doProcess() should be defined as doProcess(name,age) or shaded behind a factory or something similar.
Probably the best option is to define
public interface Request {
public void doProcess(String name,String age);
}
#Transactional
public class RequestImpl implements Request{
public void doProcess(String name,String age){
// do what you want
}
}
your SpringConfig.xml stay the same and call code will change to:
Request req= applicationContext.getBean(Request.class);
req.doProcess("someName", "20");
Beside all, perform a ApplicationContext.getBean() and cast result to an implementation is (usually) bad pratice because Spring can proxy returned object and cast to implementation will fail with a ClassCastException
#user3269829 : By default the scope would be singleton now it is totally depend upon your requirement, if you want a bean object for every request then you can go for "prototype" and if you want to share single bean object among the multiple request then you can go for "singleton"
It depends on how your third party class is implemented. If you want to ensure a single instance of your class you can use factory-method of spring beans and ensure single instance.
Check "3.3.2.2 Instantiation with a static factory method" part of Spring Documentation
It should look like this in bean definition:
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
and singleton creator:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private DefaultServiceLocator() {}
public ClientService createClientServiceInstance() {
return clientService;
}
}

How can I select Spring bean instance at runtime

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}

Categories

Resources