I am using Spring Framework for Dependency Injection. At one point I inject an instance of a certain class into another class and I need to create an additional temporary object of the same class. I probably could change the scope of the injected bean to prototype but I wonder if there's another simple way to do that?
My only idea is to create it using new but wanted to know if there is a proper way to do it with Spring.
Just a general example:
#Inject
private ClassA classA;
public void methodA() {
// here I need another instance of ClassA to be used in the scope of this method
}
Just subclass it and use prototype scope.
#Component
#Scope("prototype")
public class ClassB extends ClassA {
}
Then, to use it:
#Autowired
private ApplicationContext context;
public void methodA(){
// will return a new instance (still a bean) every time its called
ClassB bean = context.getBean(ClassB.class);
...
}
You can also cast down to ClassA and use bean name if you like.
There are two ways you can do this based on the purpose of another instance of ClassA.
First,
you can create a new object using "new" keyword.
public void methodA()
{
ClassA antherClassAInstance = new ClassA();
}
This approch is suitable when you want to use the new instance only for this method.
Second,
Declare global instance using #Autowired annotation.
#Autowired
ClassA antherClassAInstance;
Related
In one of my controller
#Autowired
private Map<String, ABC> abcMap;
now I want mock it in one of the unit test but I always get null pointer exception.
This map contains implementations of ABC abstract class.
Can anyone suggest a possible solution?
I'm not sure what Unit test Framework you are using but there are ways of making it inject the mock details. You'll have to give us more information before before we can answer.
Personally I don't much like Autowired private fields, so at the risk of answering a different question can I suggest you consider using an Autowired constructor instead. From Springs POV it won't make a difference, your object will be create and all the appropriate data wired in. (OK, there is a slight change in the order things are done, but generally you won't notice). You will have to write a constructor to copy the constructor parameters to private fields, but:
Those fields could be made final, which could make your class safer
Your Unit tests wont need any 'magic' to initialise the Autowired fields - just pass parameters
If you refactor you class to remove add/remove/modify an Autowired field then you have to remember to change your test code. With an Autowired constructor you test code has to be changed or it won't compile, and your IDE might even help you do it.
Update
The Autowired constructor alternative looks something like:
#Controller
class MyClass {
private final Class1 bean1;
private final Object value2;
#Autowired
MyClass(Class1 bean1, Class2 bean2) {
this.bean1 = bean1;
this.value2 = bean2.getValue();
}
}
Keys points are:
The class has just one constructor and it requires parameters.
The fields are not annotated #Autowired, because Spring is not assigning values to them; the constructor does that.
The constructor IS annotated as #Autowired to tell Spring to pass the beans as parameters
The first parameter is stored in a final variable - you code can't accidentally over write it, so your code is safer
In my example the second parameter is only used in the constructor, so we don't have to store it as a field in your controller. I often to this if the Bean is an object that passes configuration around.
A No-argument constructor is not required
At test time your code will have to pass parameters to the class.
Your test code will look something like:
class MyClassTest {
private Class1 bean1;
private Class2 bean2;
private MyClass objectUnderTest;
#Before
public void setUp() throws Exception {
bean1 = mock(Class1.class);
bean2 = mock(Class2.class);
// Train mocks here
objectUnderTest = new MyClass(bean1, bean2)
}
#Test
public void myTest() {
// Do something with objectUnderTest
}
}
Key points are:
There are no #MockBean annotations
The Unit test is only using the API that your Controller bean defines; No black magic is required
It's not possible to create a MyClass with out providing the required data. This is enforced by the compiler
I think you can try it.
The sample of code:
public interface Animal {
}
#Service
public class Cat implements Animal{
}
#Service
public class Dog implements Animal{
}
#Service
public class Clinic {
#Autowired
private final Map<String, Animal> animalMap = new HashMap<>(2);
}
Sample of test
#Configuration
public class TestEnvConfig {
#Bean
public Clinic create(){
return new Clinic();
}
#MockBean // you can do it without mock or use #ComponentScan
Dog dog;
#MockBean
Cat cat;
}
#SpringBootTest(classes = TestEnvConfig.class)
#RunWith(SpringRunner.class)
public class ClinicTest {
#Autowired
private Clinic clinic;
}
I defined some interfaces with generic, and I have some classes injected in Spring context as Beans, could I dynamic create a manager bean to manage them, and it could be autowired in fields without any Bean def code of this manager?
I have tried FactoryBean way to implement it, but not worked, it couldn't transmit generic class info and the FactoryBean bean couldn't transmit any changable arguments.
I have tried BeanFactory way to implement it, when I getBeansOfType, these objects created without autowired, not worked...
Now I have a finally method which I think it's not very smart that is using ImportBeanDefinitionRegistrar and ClassPathBeanDefinitionScanner to scan all classes, then insert the manager's beanDefinition.
I'll be very appreciate if you supply any method, Thank you very much !
I want to implement it like this:
public interface Strategy<E extends BaseEnum>
{
public E getType();
}
public interface LoginStrategy extends Strategy<LoginType>
{
public LoginStrategy getType();
}
#Strategy
public class ALoginStrategy implements LoginStrategy
{
public getType()
{
return LoginType.OTP;
}
}
#Strategy
public class BLoginStrategy implements LoginStrategy
{
#Autowired
private UserMapper;
public getType()
{
return LoginType.PASSWORD;
}
}
public LoginServiceImpl implements LoginService
{
#Autowired
private StrategyManage<LoginType, LoginStrategy> strategyManager;
}
I want the strategyManager in LoginServiceImpl which is marked Autowired could be auto generated.
I also have a other question. It may be easier to explain what I want.
I have a model convertor implements a ModelConvertor interface, TL is lowerModel's class, TU is upperModel's class.
now there is a bean include code like this:
#Autowired
private ModelConvertor<UserPO, UserDO> userConvertor;
normally Spring frame would throw a Exception with a "no such bean" message, so I want to make this field could auto inject a value like this:
#Autowired
private ModelConvertor<UserPO, UserDO> userConvertor[ = new DefaultModelConvertor(UserPO.class, UserDO.class)];
How can I do to solve these problems, thanks a lot again!
I have resolved this problem, scan specific packages and dynamic generate beans to put on context.
I have the following scenario:
A bean Autowired in two classes, I populate the bean in one class, then I check the bean in the second class, it is not populated. Whereas, when I add a getter in the class where I populated the bean and call the getter, it returns the bean populated. Why can't I access that bean directly in the other class, shouldn't it be populated since it's acting as a singleton in spring context?
A JUnit4 test class loading the spring context from an xml file:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/spring/test-main-context.xml" })
public class MyTests {
#Autowired
MyObject myObject;
#Autowired
MyUtils myUtils;
#Test
public void testingContext() {
myUtils.setMyObjectFromDB();
System.out.println(myUtils.getMyObject().getId());
System.out.println(myObject.getId());
}
}
MyUtils:
public class MyUtils {
#Autowired MyObject myObject;
public void setMyObjectFromDB() {
MyObject myDBObject = new MyObject();
//
// getting myObjectFromDB;
//
myObject = myDBObject;
}
public MyObject getMyObject() {
return myObject;
}
}
In the test class, myUtils.getMyObject().getId() returns a correct id but the second line myObject.getId() it returns null.
Why is MyObject which is set in MyUtils class and is #Autowired in both classes is not being updated when I access it directly in the test class.
When you're reassigning myObject = myDBObject; in setMyObjectFromDB method, you're creating a new object and saving it's referene in myObject variable which is different from the one created with Autowired.
When you use Autowired, then it will assign the created bean's reference in the variable. But if you reassign that variable, it will point to the new object.
EDIT:
If you really need to update all the variables of myObject with initialized with Autowired, it is better to create a container class that stores myObject variable.
public class MyObjectContainer {
#Autowired
MyObject myObject;
// Getters & Setters
}
In all the classes, where you're autowiring myObject, use an object of MyObjectContainer class instead. And when you want to update myObject value, just update it in myObjectContainer object with it's setter. So your MyUtils would be like:
public class MyUtils {
#Autowired MyObjectContainer myObjectContainer;
public void setMyObjectFromDB() {
MyObject myDBObject = new MyObject();
//
// getting myObjectFromDB;
//
myObjectContainer.setMyObject(myDBObject);
}
public MyObjectContainer getMyObjectContainer() {
return myObjectContainer;
}
}
Looking at the code, the MyObject is not injected in either of the two classes. You got the impression that it was injected in the MyUtils class because you actually created it manually in the setter method.
The brings the question to why MyObject is not injected, and there could be many reasons for that one of which is that you do not have #Component defined in the MyObject class or it is not in your component scan path by adding #ComponentScan(basePackages = "put-your-base-package-name") in your Spring application class.
I have a Service class which is a spring bean and I want to use this Service class inside a class (Class A) which is not a spring bean.
Where exactly should I implement ApplicationContextAware ?
Following is my code
#Service("sharedListsService")
public class SharedListsService
{
}
public class A
{
// I want to call my service class methods here
}
I'm not sure that it is a best solution but you can refactor your A class like following:
public class A {
private SharedListsService sharedListsService;
public void setSharedListsService(SharedListsService sharedListsService) {
this.sharedListsService = sharedListsService;
}
}
and then inject spring bean when you create an A class instance (for example):
SharedListsService sharedListsService = WebApplicationContextUtils.getWebApplicationContext(appContext).getBean(SharedListsService.class);
A a = new A();
a.setSharedListsService(sharedListsService);
ApplicationContextAware applies to spring beans only.
It will inject application context into a bean. That's why you cannot directly use it to get instance of SharedListsService into "A".
You need a bean, possibly a factory for "A" to wire that for you.
I am trying to autowire a member in a class using the constructor.
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(ClassA objectA) {
myMember = objectA;
}
}
If I have multiple sources that create beans of ClassA, is it possible to have a duplicate constructor definition that instantiates based on the bean that was autowired into this class?
I want to do something like this:
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(#Qualifier ("qualifierA") ClassA objectA) {
myMember = objectA;
}
#Autowire
public MyClass(#Qualifier ("qualifierB") ClassA objectB) {
myMember = objectB;
}
}
I tried using #Qualifier this way, but it didn't work.
Is it possible to do what I'm trying to do, with Spring? How can I disambiguate based on the name (qualifierA) or (qualifierB), if the bean definition is like:
#Bean (name = "qualifierA")
public ClassA getQualifierA() {
...
}
#Bean (name = "qualifierB")
public ClassA getQualifierB() {
...
}
You can't have two constructors with the exact same signature in a single class in Java. Nor any other programming language I've ever encountered. You might use method-injection instead, with two methods (named differently, of course), mark them as #Autowired(required = false) and use the proper #Qualifier(...) to specify the instance you want to inject. You might want to handle the case when both instances are present in the spring context, so no unexpected things happen.
The short answer is: no, that is not possible. In Java you cannot have two constructors with exactly the same signature. And also, you can assign only one value to your "myMember".
However, what are you trying to accomplish here? It seems that in some occasions MyClass needs to use "objectA" and in other occasions, you need "objectB".
For these scenarios, you should not use autowiring (you can't), but simply use explicit wiring:
#Bean
MyClass myObject() {
return new MyClass(qualifierA());
}