Spring circular dependance with scope PROTOTYPE - java

I created below scenario to understand Spring Circular dependancy.
Its clean when scope is singleton. But conflict occur when SCOPE is PROTOTYPE.
So I want to know Is Spring support avoid below scenario or should we change the design. if so please propose some design.
Consider below example.
#Service
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanB {
private BeanA beanA;
#Autowired
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
public void printBeanB() {
System.out.println(getBeanName());
}
public void printBeanBBeanA() {
System.out.println(getBeanName() + " - " + beanA.getBeanName());
}
public String getBeanName() {
return "Bean B";
}
}
#Service
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanA {
private BeanB beanB;
#Autowired
public void setBeanB(BeanB beanB) {
this.beanB = beanB;
}
public void printBeanA() {
System.out.println(getBeanName());
}
public void printBeanABeanB() {
System.out.println(getBeanName() + " - " + beanB.getBeanName());
}
public String getBeanName() {
return "Bean A";
}
}
This is give an error :
Description:
The dependencies of some of the beans in the application context form
a cycle:
demoApplication (field private com.example.demo.beans.BeanA
com.example.demo.DemoApplication.beanA)
So Is Spring support avoid this or should we change the design. if so please propose some design.

I could be able to resolve this by using #Lazy annotation as below.
#Service
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanA {
#Autowired
#Lazy
private BeanB beanB;
public void printBeanA() {
System.out.println(getBeanName());
}
public void printBeanABeanB() {
System.out.println(getBeanName() + " - " + beanB.getBeanName());
}
public String getBeanName() {
return "Bean A";
}
}
#Service
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanB {
#Autowired
#Lazy
private BeanA beanA;
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
public void printBeanB() {
System.out.println(getBeanName());
}
public void printBeanBBeanA() {
System.out.println(getBeanName() + " - " + beanA.getBeanName());
}
public String getBeanName() {
return "Bean B";
}
}

Related

How to mock behaviour of a bean used in #PostConstruct in #SpringBootTest

I have been searching for a while and I can't find a solution for this. I need to mock a class that is used in the #PostConstruct of another class. My classes are like this:
#Component
class A {
private final B b;
private String s;
public A(B b) {this.b = b;}
#PostConstruct
public void postConstruct() {
s = b.get().getString().toUpperCase();
}
}
#Component
class B {
private final C c;
public B(C c) {this.c = c;}
public C get() {return c;}
}
#Component
class C {
public C() throws Exception {throw new Exception();}
String getString() {return "C";}
}
Now I create the test that fails because the constructor of C fails:
#SpringBootTest
class DemoApplicationTests {
#Autowired A a;
#Test
public void canLoadContext() {
assertThat(a).isNotNull();
}
}
So I try to mock the C class:
#SpringBootTest
class DemoApplicationTests {
#MockBean
static C c;
#Autowired
A a;
#BeforeEach
public void beforeEach() {
when(c.getString())
.thenReturn("C_mocked");
}
#Test
public void canLoadContext() {
assertThat(a).isNotNull();
}
}
But this way it fails with a NPE in the line s = b.get().getString().toUpperCase(); because the C object in b is not the same that I have mocked with #MockBean. When getString() is called, it returns null and we get a NPE when calling .toUpperCase().
I have also tried to overwrite the mock like this:
#Configuration
#AutoConfigureBefore
static class TestContext {
#Primary
public C c() {
return when(mock(C.class).getString())
.thenReturn("C_mocked").getMock();
}
}
But in this case I get this error:
No qualifying bean of type 'com.example.demo.A' available: expected at
least 1 bean which qualifies as autowire candidate.
Could please somebody help out with this?
I don't know how to mark it as duplicate. But please take a look of this https://stackoverflow.com/a/58517289/11538031
The solution remains like this:
#Component
class A {
private final B b;
private String s;
public A(B b) {this.b = b;}
#PostConstruct
public void postConstruct() {
s = b.get().getString().toUpperCase();
}
}
#Component
class B {
private final C c;
public B(C c) {this.c = c;}
public C get() {return c;}
}
#Component
class C {
public C() throws Exception {throw new Exception();}
String getString() {return "C";}
}
#SpringBootTest
class DemoApplicationTests {
#Autowired
A a;
#Test
public void canLoadContext() {
assertThat(a).isNotNull();
}
#TestConfiguration
public static class TestContext {
#Bean
#Primary
public C c() {
return when(mock(C.class).getString())
.thenReturn("C_mocked").getMock();
}
}
}

What is the difference between #Bean and #Component in Spring when using #Conditional?

I want to register a Boss when there is a Car in Spring container.
My code is below
#Configuration
#ComponentScan(value ={"org.example.springframework.condition","org.example.springframework.bean"})
public class ConditionConfig {
}
#Component
public class Car {
}
#Conditional(value = BossCondition.class)
#Component
public class Boss {
}
public class BossCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getRegistry().containsBeanDefinition("car");
}
}
But it doesn't work, I can't discover the car in Condition.
Then, I change my code below, it can work well.
#Configuration
public class ConditionConfig {
#Bean
public Car car() {
return new Car();
}
#Bean
#Conditional(value = BossCondition.class)
public Boss boss() {
return new Boss();
}
}
public class Car {
}
public class Boss {
}
public class BossCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getRegistry().containsBeanDefinition("car");
}
}
So, what is the difference between #Bean and #Component?

How unit test private class

How can we unit test private class ?
For example with an example of private class #Autowired with qualifier
I would like to verify if the good qualifier is call
public class MyClass {
#Autowired
IHelloService helloService;
public void sayHello(List<Person> list) {
for(Person person : list) {
helloService.sayHello(person);
}
}
}
.
#Primary
#Component
public class SayHelloService implements ISayHello {
#Autowired
#Qualifier("french")
ISayHello french;
#Autowired
#Qualifier("english")
ISayHello english;
#Override
public void sayHello(Person person) {
switch (person.getLanguage) {
case "EN":
english.sayHello(Person person);
break;
case "FR":
french.sayHello(Person person);
break;
default:
break;
}
}
}
.
#Qualifier("french")
Component
class SayHelloFrenchService implements ISayHello {
public void sayHello(Person person) {
sysout("Bonjour " + person.getName());
}
}
#Qualifier("english")
Component
class SayHelloFrenchService implements ISayHello {
public void sayHello(Person person) {
sysout("Hello " + person.getName());
}
}
Edit:
I failed my example: the twice qualifier class were private
If i #Mock the interface it works.
I thought i must #Mock the implementation...
But i can't write tests of implementation of private class.

Put bean in to the map to get bean from factory by name

I need to get the bean from the factory by name.
I wonder if there is a more elegant way to deal with this problem?
My working code now looks like this. This is my interface service and "factory"
public interface GreetingService {
void getGreeting(String name);
}
public interface GreetingServiceFactory {
GreetingService getGreetingService(String region);
}
Implementation greetingService:
#Service
public class EnglishGreetingServiceImpl implements GreetingService {
#Override
public void getGreeting(String name) {
System.out.println("Hello " + name);
}
}
#Service
public class GermanGreetingServiceImpl implements GreetingService {
#Override
public void getGreeting(String name) {
System.out.println("Willkommen " + name);
}
}
Implementation factory:
#Service
public class GreetingServiceFactoryImpl implements GreetingServiceFactory {
private Map<String, GreetingService> greetingBeanMap;
#Autowired
#Qualifier("germanGreetingServiceImpl")
private GreetingService germanGreetingService;
#Autowired
#Qualifier("englishGreetingServiceImpl")
private GreetingService englishGreetingService;
#PostConstruct
public void init () {
greetingBeanMap = new HashMap<>();
greetingBeanMap.put("en", englishGreetingService);
greetingBeanMap.put("de", germanGreetingService);
}
#Override
public GreetingService getGreetingService(String region) {
return greetingBeanMap.get(region);
}
}
Main class with example code where I receive bean after some name
#SpringBootApplication
public class SpringFactoryApplication implements CommandLineRunner {
#Autowired
private GreetingServiceFactory greetingServiceFactory;
public static void main(String[] args) {
SpringApplication.run(SpringFactoryApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
String config1 = "en";
GreetingService english = greetingServiceFactory.getGreetingService(config1);
english.getGreeting("John");
String config2 = "de";
GreetingService deutsch = greetingServiceFactory.getGreetingService(config2);
deutsch.getGreeting("Hans");
}
}
In your above code, this piece of code is completely redundant
#Autowired
#Qualifier("germanGreetingServiceImpl")
private GreetingService germanGreetingService;
#Autowired
#Qualifier("englishGreetingServiceImpl")
private GreetingService englishGreetingService;
#PostConstruct
public void init () {
greetingBeanMap = new HashMap<>();
greetingBeanMap.put("en", englishGreetingService);
greetingBeanMap.put("de", germanGreetingService);
}
this piece of code can be replaced by
#Autowired
private Map<String, GreetingService> greetingBeanMap;
When you declare like this, spring will search for all implementations of GreetingService interface and inject into your map, with the key as the bean name. i.e. the greetingBeanMap will have key's as germanGreetingServiceImpl and englishGreetingServiceImpl and value as the bean's itself.
If you want to make the key's as "en" and "de" instead of bean names, then you can name the beans as "en" and "de". Like this
#Service("en")
public class EnglishGreetingServiceImpl implements GreetingService {
......
}
#Service("de")
public class GermanGreetingServiceImpl implements GreetingService {
......
}

Unit testing an EJB with Mockito, TestNG and OpenEJB

I have the following EJB's:
PersonService.java
#Local
public interface PersonService {
long countPersons();
}
PersonServiceImpl.java
#Stateless
public class PersonServiceImpl implements PersonService {
#EJB
private RemotePersonService remotePersonService;
#Override
public long countPersons() {
return remotePersonService.getAllPersons().size();
}
}
RemotePersonService.java
#Local
public interface RemotePersonService {
List<Person> getAllPersons();
}
RemotePersonServiceImpl.Java
#Stateless
public class RemotePersonServiceImpl {
#Override
public List<Person> getAllPersons() {
// Here, I normally call a remote webservice, but this is for the purpose of this question
List<Person> results = new ArrayList<Person>();
results.add(new Person("John"));
return results;
}
}
And here are my tests
AbstractTest.java
public abstract class AbstractTest {
private InitialContext context;
#BeforeClass(alwaysRun = true)
public void setUp() throws Exception {
System.setProperty("java.naming.factory.initial", "org.apache.openejb.client.LocalInitialContextFactory");
Properties properties = new Properties();
properties.load(getClass().getResourceAsStream("/unittest-jndi.properties"));
context = new InitialContext(properties);
context.bind("inject", this);
}
#AfterClass(alwaysRun = true)
public void tearDown() throws Exception {
if (context != null) {
context.close();
}
}
}
PersonServiceTest.java
#LocalClient
public class PersonServiceTest extends AbstractTest {
#EJB
private PersonService personService;
#Test
public void testPersonService() {
long count = personService.countPersons();
Assert.assertEquals(count, 1l);
}
}
Now, want I want to do is replace the RemotePersonService implementation in PersonServiceImpl.java by a mock using Mockito, and still have the same call in my testPersonService method.
I tried that:
PersonServiceTest.java
#LocalClient
public class PersonServiceTest extends AbstractTest {
#Mock
private RemotePersonService remotePersonService;
#EJB
#InjectMocks
private PersonService personService;
#BeforeMethod(alwaysRun = true)
public void setUpMocks() {
MockitoAnnotations.initMocks(this);
List<Person> customResults = new ArrayList<Person>();
customResults.add(new Person("Alice"));
customResults.add(new Person("Bob"));
Mockito.when(remotePersonService.getAllPersons()).thenReturn(customResults);
}
#Test
public void testPersonService() {
long count = personService.countPersons();
Assert.assertEquals(count, 2l);
}
}
But this doesn't work. The #Mock RemotePersonService is not injected in the PersonService, and the true EJB is still used.
How can I make this work ?
Don't use annotations for your tests. Have a constructor that will wire in all your dependencies.
#Stateless
public class PersonServiceImpl implements PersonService {
#EJB
private RemotePersonService remotePersonService;
// Let your test instantiate a mock service and wire it into your test instance using this constructor.
public PersonServiceImpl(RemotePersonService rps) {
this.remotePersonService = rps;
}
#Override
public long countPersons() {
return remotePersonService.getAllPersons().size();
}
}
Create mocks and pass them to it. Your test might look like this:
#LocalClient
public class PersonServiceTest extends AbstractTest {
#Test
public void testPersonService() {
RemotePersonService mockRemotePersonService = Mockito.mock(RemotePersonService.class);
List<Person> customResults = new ArrayList<Person>();
customResults.add(new Person("Alice"));
customResults.add(new Person("Bob"));
Mockito.when(mockRemotePersonService.getAllPersons()).thenReturn(customResults);
PersonService personService = new PersonServiceImpl(mockRemotePersonService);
long count = personService.countPersons();
Assert.assertEquals(count, 2l);
}
}
I use setters on the class, and Lookup for the ejb
private ServicioAsyncMessaging servicioNotificaciones;
I delete the #EJB --> and on the getter
public ServicioAsyncMessaging getServicioNotificaciones() {
if(servicioNotificaciones == null){
servicioNotificaciones = (ServicioAsyncMessaging)Lookup.getEjb(EjbJndiConstantes.EJB_SERVICIO_ASYNC_MSG);
}
return servicioNotificaciones;
}
public void setServicioNotificaciones(ServicioAsyncMessaging servicioNotificaciones) {
this.servicioNotificaciones = servicioNotificaciones;
}
The lookup es:
public static Object getEjb(String lookupName){
Object t = null;
try {
Context ctx = new InitialContext();
t= ctx.lookup(lookupName);
} catch (NamingException e) {
log.error("getEjb | Error {}",e.getMessage(),e);
}
return t;
}
With those changes the mockito --> inject the mocks on the setter.

Categories