I have a class that has some dynamic attributes which I received on the constructor.
Other properties on this same class are/can be injected by spring.
I want to know if this is possible and how should I config my application. I using spring 3 and xml configuration.
Here is an example:
class MyClass {
private MyClass2 obj2; // should be injected by spring
private Long myId;
public MyClass(Long dynamicId) {
myId = dynamicId;
}
public void doSomehting() {
obj2.doOtherStuff(this);
}
}
So, what I want, since I must create MyClass dynamically, is that after I call new MyClass(1234), the obj2 gets injected by Spring.
Is it possible?
use an ObjectFactory to retrieve the bean from the di-container. If you wrap this factory in a factory object of your own, you can set any property and still have the bean managed by Spring. Since you want the bean to have some dynamically set property think it through whether you need a singleton-scoped bean (default) or a prototype one.
If you intend to do this from xml config you need to muck around with objectfactorycreatingfactorybean. Spring documentation is excellent, just follow the example.
If you do annotation-based configuration, you just need to autowire the ObjectFactory. Note that YourClass has to be declared as a bean too!
I'm pretty sure that's not possible. It looks like you want a (scary music) factory.
class MyClassFactory {
private final MyClass2 object;
public(MyClass2 object) {
this.object = object;
}
public createMyClass(Lond id) {
return new MyClass(id, object);
}
}
If you create an instance of MyClass dynamically with the new operator, that instance is outside of Spring's bean factory, so Spring cannot inject anything. You really have two options, from what I can see.
1) Make MyClass a prototype bean so Spring gives you a new instance every time you need it. You would need to then define obj2 as a property to be set (or Autowired).
2) Inject obj2 via Spring into the class that is creating the instance of MyClass and make obj2 a constructor argument so you have to inject it.
My 0.02 from what I read from your question. A note of caution, when creating a new instance in Java, it is always outside the bean factory and all work performed outside the bean factory looses the Spring proxy behaviors. Be very careful here, you can get yourself into a rabbit hole that can be hard to track down.
Related
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 8 months ago.
I am new to spring boot.
I am trying to understand Dependency Injection. I created a #Component bean and try to get it's instance object into a simple POJO class.
Let's take a look at example:
// Planet.java
#Component
class Planet() {
private Long planetId;
private String planetName;
// ... getter-setter and constructor
}
since Planet object is annotated with #Component so it will be created by spring context container.
Now I have created a simple POJO class....
// Species.java
class Species() {
private Long spId;
private String spName;
// I want to get the object of Planet from container
#Autowired
private Planet planet;
// getter-setter and constructor
}
Now when I create my Species object with new keyword(new Species()) then...
Planet Object is created by container as expected.
But In my Species object planet property instead of referencing Planet object it reference to a null value (not expected)..
Since I am Autowiring planet property, it should get that object from container but It is not getting that object. Please let me know Where I am doing something wrong..
I also tried to create beans using #Configuaration but still it's not working..
Spring can only autowire beans into others spring-managed beans. Species object here is unmanaged so it can't work.
If you really want to keep the control on species instances (I mean control the construction by yourself) you have to inject dependency by yourself.
So recover the planet object up-front and then inject it into the constructor
Planet p = // Planet injection ;
new Species(planet);
You can also give up control and simply annotate Species with #Component (or a specialized version of it).
Last way via a config type.
#Configuration
public class Config {
#Autowired
private Planet p;
#Bean
public getSpecies() {
return new Species(p);
}
}
Spring, as an IOC container, helps us manage beans, such as contructing, and injecting attribute values. In other words, Spring only helps us manage beans.
Objects generated by new operator are not beans and cannot be managed by Spring. Therefore, the DI capability of Spring cannot be obtained.
So, make Species as a bean and Spring will help us to do dependency injection.
We can annotate Species with #Component(#Service, and so on), or write a method with #Bean to create Species.
I am trying to understand what would be the correct usage of Spring prototype bean.
May be the following code sample will help in you understanding my dilemma:
List<ClassA> caList = new ArrayList<ClassA>();
for (String name : nameList) {
ClassA ca = new ClassA();
//or Shall I use protypebean, using method lookup I can inject the dependency of ClassA.
// ClassA ca = getPrototypeClassA();
ca.setName(name);
caList.add(ca);
}
So my exact point is in this scenario shall I go with method injection or new() operator.
Provide your view with justification.
You can make use of either of the ways, because ultimately client code is responsible for handling the life-cycle of the prototype bean rather than spring container.
According to Spring-docs,
In some respects, you can think of the Spring containers role when
talking about a prototype-scoped bean as somewhat of a replacement for
the Java 'new' operator. All lifecycle aspects past that point have to
be handled by the client.
Spring does not manage the complete lifecycle of a prototype bean: the
container instantiates, configures, decorates and otherwise assembles
a prototype object, hands it to the client and then has no further
knowledge of that prototype instance. It is the responsibility of the
client code to clean up prototype scoped objects and release any
expensive resources that the prototype bean(s) are holding onto.
If ClassA needs to have #Autowired references, then go for a prototype bean.
Otherwise a simple POJO (that the Spring container is unaware of) will do.
It seems that your instance need some runtime values in order to initialise properly. In such case ,it depends on if you need to use spring feature such as AOP for the ClassA instance. If yes , go with the method injection .If not , you can consider using factory pattern . Much more OO and cleaner to me :
Something like the following . You should get the idea.
#Component
public class FactoryForClassA {
#Autowired
private FooBean someDependencyForClassA;
public ClassA create(String name){
ClassA a = new ClassA(someDependencyForClassA);
a.setName(name);
return a;
}
}
And the client code:
#Autowired
private FactoryForClassA factoryForClassA;
List<ClassA> caList = new ArrayList<ClassA>();
for (String name : nameList) {
ClassA a = factoryForClassA.create(name);
caList.add(ca);
}
I currently have numerous classes with a field like
private MyStuff myStuff = new MyStuff();
It would be preferable if there was a MyStuff singleton which all the classes used. Our project has a MyConfiguration class with some Beans in it, but they don't seem to get used, at least not directly, so I can't use them for examples. I have been tasked to create a MyStuff bean in the MyConfiguration class, and then inject it into my other classes.
What I have so far:
#Configuration
public class MyConfiguration
{
#Bean
public MyStuff myStuff()
{
return new MyStuff();
}
}
public SomeClass
{
public void dealWithStuff()
{
someStuff.myMethod();
}
#Autowired
private MyStuff someStuff;
}
This compiles but does not run. someStuff is null when it tries to call myMethod(). Apparently it does not see the bean or make the connection. What am I missing?
I'm going to make an assumption, so correct me if I'm wrong: you are creating instances of SomeClass yourself. Something like
SomeClass someInstance = new SomeClass();
outside of any Spring component.
In this case, how do you expect Spring to inject anything since it's not even process it.
You need to let Spring create objects (beans) that need to have other beans injected.
The problem is naming. Refer to this for a complete description but briefly it says
If you intend to express annotation-driven injection by name, do not primarily use #Autowired, even if is technically capable of referring to a bean name through #Qualifier values. Instead, use the JSR-250 #Resource annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process.
It means that you should be doing something like this:
#Resource(name="myStuff")
public void setSomeStuff(MyStuff someStuff){
this.someStuff = someStuff;
}
The reason is that you have defined your bean as myStuff but referring it as someStuff.
Also to have a singleton instance, all you need is to define its scope as singleton in your Spring Configuration as following:
#Configuration
public class MyConfiguration
{
#Bean #Scope(BeanDefinition.SCOPE_SINGLETON)
public MyStuff myStuff()
{
return new MyStuff();
}
}
As I see in your code, your object reference name is "someStuff" but you are referring to myStuff you should use someStuff.myMethod();
Is it possible to use #Configurable on a class that's weaved using AspectJ and get Spring to load in values on fields/methods which are annotated with #Value?
I know its possible with #Autowired and #Resource etc... Are there any others.
e.g.
#Configurable
public Class MyObj{
#Value("$(my.prop)")
private String aField;
public String getAField(){
return aField;
}
}
And then have something like
public aMethodSomewhereElse(){
MyObj obj = new MyObj()
assertNotNull(obj.getAField());
}
Are there any alternatives to being able to create MyObj with the new operator and still get spring to handle the annotations?
--EDIT:--
It IS possible to do this using new when using #Autowired, have a look at some Hibernate and JPA stuff with Spring and AOP... I've used this in the past to do some profiling of Java code. But I really want to use SPEL and #Value before I mock up a full example I was hoping to find the answer here. FYI - if you don't belive me the Spring Manual even says it is possible to do this, what I want to know is if its possible to use #Value annotations in the same scope...
The Spring container instantiates and configures beans defined in your
application context. It is also possible to ask a bean factory to
configure a pre-existing object given the name of a bean definition
containing the configuration to be applied. The spring-aspects.jar
contains an annotation-driven aspect that exploits this capability to
allow dependency injection of any object.
And...
Using the annotation on its own does nothing of course. It is the
AnnotationBeanConfigurerAspect in spring-aspects.jar that acts on the
presence of the annotation. In essence the aspect says "after
returning from the initialization of a new object of a type annotated
with #Configurable, configure the newly created object using Spring in
accordance with the properties of the annotation". In this context,
initialization refers to newly instantiated objects (e.g., objects
instantiated with the 'new' operator) as well as to Serializable
objects that are undergoing deserialization (e.g., via readResolve()).
http://static.springsource.org/spring/docs/3.0.0.RC2/reference/html/ch07s08.html
Cheers.
You are absolutely right - #Autowired fields will be wired in an #Configurable annotated class even outside of a Spring container, assuming that you have a AspectJ infrastructure in place.
You have noted a good catch though, #Value fields are processed by a Spring bean post processor(AutowiredAnnotationBeanPostProcessor), which resolves the #Value annotated fields. It does not act on objects instantiated outside of the container though - so in short, the #Autowired fields should get wired in, but #Value properties will not.
Doing
MyObj obj = new MyObj()
means that obj is not managed by spring, so it will not do autowiring.
Only way to do that is to obtain instance from an application context. For example:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyObj obj = context.getBean("myBean");
I don't think it is possible to use new operator and ask spring to autowire properties. I think 1 way to solve this is to get a static reference to applicationContext and create a prototype scoped bean.
#Component
public class ApplicationContextLocator {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public ApplicationContextLocator() {
super();
}
#Autowired
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextLocator.applicationContext = applicationContext;
}
}
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
#Component
public class MyObj {
.....
}
public aMethodSomewhereElse(){
MyObj obj = ApplicationContextLocator.getApplicationContext().getBean(MyObj.class)
assertNotNull(obj.getAField());
}
I always have a question from the 1st day when I used spring. If a class has a constructor that needs two parameters, but these 2 parameters are not fixed, they are generated according to input request, every time they are different, but I need spring container to manage the class's instance, how to achieve this in spring?
For example
Class A{
A(int x,int y){//omit}
}
but x, and y are not fixed,we need to calculate x and y by our program, then we can create instance for A, in ordinary java code,like below
int x=calculate(request);
int y=calculate(request);
A a=new A(x,y);
But how to make spring manages the class A's instance creation?
Additional information: Why I need Class A is managed by spring, because A depends on some other classes which are managed by spring.
The most straightforward way to do it is to use ApplicationContext.getBean(String name, Object... args) - it can create a prototype-scoped bean passing the given arguments to its constructor. Obviosly it's not a good idea to use ApplicationContext directly in any bean that uses A.
A more elegant approach is to hide the creation of A behind a factory. That factory can use the previous approach internally, or it can obtain an instance of a bean in a regular way (Provider<A>, etc) and then call a non-public initialization method to pass that parameters (instead of passing parameters through using constructor).
Yet another apporach is to use #Configurable and load-time weaving that allows Spring to initialize objects created with new. Though it requires some extra configuration of runtime environment.
they are generated according to input request, every time they are different, but I need spring container to manage the class's instance, how to achieve this in spring?
You don't. Classes that you need to instantiate in response to user input are not meant to be managed by Spring.
Just because you are using Spring to manage some beans, does not mean that all beans/classes should be managed by Spring.
You want your Spring Bean to be defined as a prototype instead of a singleton. That way, on every new request your Spring context will create a new instance of the bean.
In Java config, it will look something like this:
#Scope("prototype") #Bean public MyBean myBean() { ... }
In xml:
<bean id="myBean" class="whatever.MyBean" scope="prototype"> ...
There are also scopes that can be tied to HTTP sessions. See:
http://static.springsource.org/spring/docs/3.0.0.M3/reference/html/ch04s04.html
And, as others pointed out, you will have to define a factory method for your bean:
See: Spring and passing parameters to factory-method in runtime
I'd assume that int is replaceable by some Object instance.One way to achieve this is to use Spring's FactoryBean feature to customize the initialization of your bean:
class AFactory implements o.s.b.f.FactoryBean {
private SomeObject firstParam;
private OtherObject secondParam;
public Object getObject() {
return new A(firstParam, secondParam);
}
public Class getObjectType() {
return A.class;
}
public boolean isSingleton() {
return false;
// i.e. every time #getObject() is called a new instance is created === prototype scope
}
public void setFirstParam(SomeObject firstParam){}
public void setSecondParam(OtherObject secondParam){}
}
Note that if SomeObject and OtherObject can be actually other FactorBeans that are factories for the dependencies of A, then every time there is a call to AFactory#getObject(), you'd actually receive a new instance of A that uses fresh instances of its required dependencies.
You could try to use this method in BeanFactory class (extended by ApplicationContext)
Object getBean(String name, Object... args)throws BeansException;
in your case:
context.getBean("A", x, y);
where "A" is the bean name for class A.
Maybe you have to change the constructor to:
Class A{
A(Request request){ this.x=calculate(request); ....}
}