Constructor Injection in configuration classes in Sprin5 - spring

I was trying to create a configuration class
#Configuration
public class AppConfig {
private final SomeBean someBean;
private final AnotherBean anotherBean;
#Autowired
public AppConfig(SomeBean someBean, AnotherBean anotherBean) {
this.someBean = someBean;
this.anotherBean = anotherBean;
}
}
Another approach with
#Configuration
public class AppConfig {
#Autowired
private final SomeBean someBean;
#Autowired
private final AnotherBean anotherBean;
}
Not sure which one I have to go for.
I have seen many codes with the second approach, but not with the first approach.
Any reason for this?

Related

Spring boot create multiple mocks of the same interface doesn't work with constructor

I try to implement MockitoExtension in Spring Boot with a simple service class. When I implement it via field-set, it works. But when I implement it constructor, i doesn't work.
ExampleService:
#Service
public class ExampleService
{
private final AmqpTemplate requestTemplate;
private final AmqpTemplate resultTemplate;
public ExampleService(#Qualifier("requestTemplate") AmqpTemplate requestTemplate,
#Qualifier("resultTemplate") AmqpTemplate resultTemplate)
{
this.requestTemplate = requestTemplate;
this.resultTemplate = resultTemplate;
}
}
ExampleServiceTest:
#ExtendWith(MockitoExtension.class)
public class ExampleServiceTest
{
#InjectMocks
private ExampleService exampleService;
#Mock(name = "requestTemplate")
private AmqpTemplate requestTemplate;
#Mock(name = "resultTemplate")
private AmqpTemplate resultTemplate;
#Test
public void test1()
{
...
}
}
requestTemplate and resultTemplate looks like same object on test cases and it behaves wrong...
When I implement it via Autowired, it works...
#Service
public class ExampleService
{
#Autowired
private AmqpTemplate requestTemplate;
#Autowired
private AmqpTemplate resultTemplate;
...
}
I wonder the constructor solution and reason why it doesn't work.

MapStruct mapper not initialized with autowired when debug

I use spring boot 2.3.2 with mapstruct.
In a service class I have a mapper who have an autowired annotation.
#Service
public BillingService{
private BillingRepository billingRepository;
#Autowired
private BillingMapper billingMapper;
#Autowired
public BillingService (BillingRepository billingRepository){
this.billingRepository=billingRepository;
}
public void getBilling(Long billingId){
....
billingMapper.convertTo(...);
}
}
#RunWith(MockitoJUnitRunner.class)
public class BillingServiceTest{
#Mock
BillingRepository billingRepository;
private BillingService bilingService;
#Spy
private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
#BeforeEach
public void setup(){
MockitoAnnotations.initMocks(this);
billingService = new BillingService();
}
#Test
public void testGetBilling(){
List<Billing> billings = new ArrayList<>();
...
List<BillingPayload> payloads = new ArrayList<>();
when(billingMapper.convertTo(billings)).thenReturn(payloads);
bilingService.getBilling(1l);
}
}
#Mapper(componentModel="spring")
public interface BillingMapper{
...
}
When I debug and I'm stuck in getBilling method in BillingService Class, billingMapper is alway null;
You are using very strange configuration. First of all you have method returning BillingService that doesn't specify any return value so this would not even compile. I suggest following way:
#Service
public BillingService{
private final BillingRepository billingRepository;
private final BillingMapper billingMapper;
// constructor with bean injection
public BillingService(final BillingRepository billingRepository,
final BillingMapper billingMapper) {
this.billingRepository = billingRepository;
this.billingMapper = billingMapper;
}
public void getBilling(Long billingId){
....
billingMapper.convertTo(...);
}
}
Then you can configure your test like following:
#RunWith(SpringJUnit4ClassRunner.class)
public class BillingServiceTest {
#Spy private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
#MockBean private BillingRepository billingRepository;
#Autowired private BillingService billingService;
#TestConfiguration
static class BillingServiceTestContextConfiguration {
#Bean public BillingService billingService() {
return new BillingService(billingRepository, billingMapper);
}
}
#Test
public void testGetBilling(){
List<Billing> billings = new ArrayList<>();
...
List<BillingPayload> payloads = new ArrayList<>();
when(billingRepository.findById(1L)).thenReturn(); // someResult
when(billingMapper.convertTo(billings)).thenReturn(payloads);
bilingService.getBilling(1l);
}
}
#Mapper(componentModel="spring")
public interface BillingMapper{
...
}
Similiar configuration should work. Main problem is that you are mixing #Autowired with setter/constructor injection (don't know since your weird method inside BillingService. Also dont know why you use #Spy annotation when you are tryning to Mock interface. #Spy is used to mock actual instance, while #Mock is mocking Class type. I would stick with #MockBean private BillingMapper billingMapper instead if BillingMapper is specified as Bean in your application.

#MockBean-instance used via #Autowire before method injection

In my JUNIT5-test I want to mock a bean by #MockBean. In my #BeforeEach - method the calls are injected.
But other beans #Autowire-ing the #MockBean are instantiated with the #MockBean before the method injection. This is weird and gives me NPEs. How can I force method injection before use of the #MockBean?
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration("classpath:context/authenticationStaff.xml")
#EnableAutoConfiguration
public class PasswordPolicyServiceTest {
private final List<Reference> bcryptDigestRefs = new ArrayList<>();
private final DigestHistoryRule bcryptDigestRule = new DigestHistoryRule(new BCryptHashBean());
#MockBean
private SystemConfiguration systemConfiguration;
#BeforeEach
public void initMock() {
MockitoAnnotations.initMocks(this);
Arrays.asList(SystemConfigKey.values()).forEach(key -> {
Mockito.when(systemConfiguration.getConfig(key)).thenReturn(getConfig(key, key.getDefaultValue()));
});
Mockito.when(systemConfiguration.getConfig(SystemConfigKey.MIN_PASSWORD_LENGTH)).thenReturn(getConfig(SystemConfigKey.MIN_PASSWORD_LENGTH, "5"));
A failing class is:
#Service
public class SessionCacheManager {
private final Ehcache ehCache;
private final Cache<String, SessionVerificationType> sessionCache;
private final SystemConfiguration systemConfiguration;
#Autowired
public SessionCacheManager(final Ehcache ehCache, final SystemConfiguration systemConfiguration) {
this.ehCache=ehCache;
this.systemConfiguration=systemConfiguration;
SystemConfigType systemConfig = systemConfiguration.getConfig(SystemConfigKey.SESSION_MAX_NUMBER);
Integer numberOfParalledSessions = systemConfig.getIntegerValue();
CacheManager cacheManager=ehCache.registerNewCacheManager(CACHE_MANAGER);
sessionCache = cacheManager.createCache(CACHE_NAME,
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, SessionVerificationType.class, ResourcePoolsBuilder.heap(numberOfParalledSessions)));
}
As I can see (with debug), the "SessionCacheManager" uses the mocked "SystemConfiguration" but systemConfiguration.getConfig(SystemConfigKey.SESSION_MAX_NUMBER); returns a null.
I helped myself, though I do not like my solution. It is more a trick than a solution. But I cannot think of something else right now.
I change the #ContextConfiguration to:
#ContextConfiguration(locations = "/context/authenticationStaff.xml", classes = { SpringApplicationContext.class })
The XML is setup, that it cannot autodetect the class "SystemConfiguration.class".
Instead of that the "SpringApplicationContext.class" provides the "SystemConfiguration.class" as a mocked bean.
#Configuration
public class SpringApplicationContext {
#Mock
private SystemConfiguration mockedSystemConfiguration;
#Bean
public SystemConfiguration systemConfiguration() {
MockitoAnnotations.initMocks(this);
Arrays.asList(SystemConfigKey.values()).forEach(key -> {
Mockito.when(mockedSystemConfiguration.getConfig(key)).thenReturn(getConfig(key, key.getDefaultValue()));
});
Mockito.when(mockedSystemConfiguration.getConfig(SystemConfigKey.MIN_PASSWORD_LENGTH)).thenReturn(getConfig(SystemConfigKey.MIN_PASSWORD_LENGTH, "5"));
Mockito.when(mockedSystemConfiguration.getConfig(SystemConfigKey.PASSWORD_BCRYPTENCODER_COSTFACTOR)).thenReturn(getConfig(SystemConfigKey.PASSWORD_BCRYPTENCODER_COSTFACTOR, "5"));
return mockedSystemConfiguration;
}
private SystemConfigType getConfig(SystemConfigKey key, String value) {
SystemConfigType config = new SystemConfigType();
config.setKey(key);
config.setValue(value);
return config;
}
The test-code now looks like this:
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(locations = "/context/authenticationStaff.xml", classes = { SpringApplicationContext.class })
#EnableAutoConfiguration
public class PasswordPolicyServiceTest {
#Autowired
private PasswordPolicyService passwordPolicyService;
#Autowired
private PasswordHandlerService passwordHandlerService;
#Autowired
private SystemConfiguration systemConfiguration;
private final List<Reference> bcryptDigestRefs = new ArrayList<>();
private final DigestHistoryRule bcryptDigestRule = new DigestHistoryRule(new BCryptHashBean());
#BeforeEach
public void initMock() {
MockitoAnnotations.initMocks(this);
String password=passwordHandlerService.getPassword("my$Password");
bcryptDigestRefs.add(
new HistoricalReference(
"bcrypt-history",
password));
}
This works, but is not a nice solution. Other recommendations are very welcome.

What will happen if i remove #Autowired from field/constructor injection but injecting that bean in another class

Suppose i have a class as,
#Repository
public class StudentServiceDao{
private final StudentClient client;
private final StudentValidator validator;
#Autowired <----
public StudentServiceDao(StudentClient studentClient){
client = studentClient;
validator = new StudentValidator(studentClient.getIdentifier());
}
public List<Student> getStudent(Request request){
StudentRS studentRS= client.getStudentList(request);
validator.validate(studentRS);
return StudentMapper.map(studentRS);
}
}
Now i have another class as,
#Component
public class StudentServiceDaoImpl{
#Autowired
private StudentServiceDao studentServiceDao;
public list<Student> retrieveStudent (Request request){
return studentServiceDao.getStudent(request);
}
}
Now if i remove #Autowired from StudentServiceDao what will happen and why ?
Autowiring can happen multiple ways.
For a few years now (currently 2020) all of these are valid ways to autowire dependencies:
Explicit constructor autowire annotation:
#Repository
public class StudentServiceDao {
private final StudentClient client;
private final StudentValidator validator;
#Autowired
public StudentServiceDao(StudentClient studentClient){
client = studentClient;
validator = new StudentValidator(studentClient.getIdentifier());
}
}
Implicit constructor autowire:
#Repository
public class StudentServiceDao {
private final StudentClient client;
private final StudentValidator validator;
public StudentServiceDao(StudentClient studentClient){
client = studentClient;
validator = new StudentValidator(studentClient.getIdentifier());
}
}
Explicit field autowire:
#Repository
public class StudentServiceDao {
#Autowired
private final StudentClient client;
#Autowired
private final StudentValidator validator;
}
Pick which ever one makes the most sense for you. I personally like implicit constructor. I think it makes instantiating the bean for testing easier with mocks. All types are valid.
5 or 6 years ago, before java config took over, there were other requirements like getters/setters needing to be present, xml files needing to specify all the beans, etc. But those are mostly gone and if you are working on a modern spring app you won't encounter them.
As to why, I have no idea, this is just how it is.

Spring MVC Repository Factory

Approach 1:
Below are the two service classes which are using same 2 repositories.
#org.springframework.stereotype.Service(value = "userService")
public class UserServiceImpl implements UserService {
#Autowired
private CounterRepository counterRepository;
#Autowired
private SessionRepository sessionRepository;
}
#org.springframework.stereotype.Service(value = "projectService")
public class UserServiceImpl implements UserService {
#Autowired
private CounterRepository counterRepository;
#Autowired
private SessionRepository sessionRepository;
}
So in above classes, as you see that CounterRepository & SessionRepository are using two times each in UserServiceImpl & ProjectServiceImpl services.
Is this is correct approach or I can make One Factory Class and use it to get required repo.
Approach 2:
class RepoFactory{
#Autowired
private CounterRepository counterRepository;
#Autowired
private SessionRepository sessionRepository;
public <T> T getRepo(Class<T> entityClass) {
if (entityClass == CounterRepository .class) {
return (T) appMessageRepository;
} else if (entityClass == SessionRepository.class) {
return (T) auditTrailRepository;
}
}
And I use like below
#org.springframework.stereotype.Service(value = "userService")
public class UserServiceImpl implements UserService {
#Autowired
private RepoFactory repoFactory;
public void someMethod(){
repoFactory.getRepo(CounterRepository.class);
.....
}
public void someMethod2(){
repoFactory.getRepo(SessionRepository.class);
.....
}
}
#org.springframework.stereotype.Service(value = "projectService")
public class ProjectServiceImpl implements ProjectService {
#Autowired
private RepoFactory repoFactory;
public void someMethod(){
repoFactory.getRepo(CounterRepository.class);
.....
}
public void someMethod2(){
repoFactory.getRepo(SessionRepository.class);
.....
}
}
Could you please help me out which approach is better according to performance and memory consumption.
If you don't configure it explictly, all spring beans are singleton, it will not affect memory at all! http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-scopes
The first approach is easy to go, recommended!!
The second approach is totally unnecessary, if you want to, you can always autowire applicationContext and use applicationContext.getBean(Mybean.class)
If you have to save some memory on application start, you can have a look at #Lazy, but from my point of view, it is also not necessary.

Resources