In spring framework 4.0 I've got a SpecificService inheriting from a GenericService that is Injected with a service. The SpecificService cannot access the Injected service.
Is there a way to make the Injected service accessible from the child class (SpecificService)?
// GenericService.java
#Service
#Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class GenericService {
#Inject
OtherService otherService;
// ...
private void fn() {
System.out.println(otherService) // (id = 12)
}
}
// SpecificService.java
class SpecificService extends GenericService {
// ...
private void fn() {
System.out.println(otherService) // null
}
}
// GenericService.java
public abstract class GenericService {
protected final OtherService otherService;
public GenericService (OtherService otherService) {
this.otherService = otherService;
}
// ...
private void fn() {
System.out.println(otherService) // (id = 12)
}
}
// SpecificService.java
#Service
#Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
class SpecificService extends GenericService {
#Inject
public SpecificService (OtherService otherService) {
super(otherService);
}
private void fn() {
System.out.println(otherService) // null
}
}
I'm not 100% this works as I only used Spring 3 with #Autowired, but there I had the same problem and this was the solution. Assuming you can use #Inject on a constructor argument as well.
Related
I'm creating an application based on Hazelcast and spring-boot-starter-web. The application structure is:
Book controller-> BookService interface-> BookServiceImplementation-> put into hazelcast queue
Author controller-> AuthorService interface-> AuthorServiceImplementation-> put into hazelcast queue.
For that purpose i need one class that contains hazelcastInstance to share it in all services so i created blackboard interface but because i use #Autowired it is creating new instance for every service and i need to set hazelcast instance again.
My code so far:
Controller:
#GetMapping("book")
public ResponseEntity<Void> getBookDetails(
#RequestParam(value = "bookId", required = false) Long bookId) {
bookService.add(bookId);
return new ResponseEntity<Void>(HttpStatus.OK);
}
Service impl:
#Service("bookService")
public class BookServiceImpl extends BaseService implements BookService {
#Override
public void add(Long bookId) {
blackboard.add(bookId, Listeners.BOOK_QUEUE_NAME);
}
BaseService:
public class BaseService {
#Autowired
protected Blackboard blackboard;
public void loadInstance(ClientConfig clientConfig) {
HazelcastInstance hazelcastInstanceClient = HazelcastClient.newHazelcastClient(clientConfig);
blackboard.setHazelcastInstance(hazelcastInstanceClient);
}
}
Blackboard interface impl:
#Component("blackboard")
public class BlackboardImpl implements Blackboard {
private HazelcastInstance hazelcastInstance;
#Override
public HazelcastInstance getHazelcastInstance() {
return hazelcastInstance;
}
#Override
public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
this.hazelcastInstance = hazelcastInstance;
}
#Override
public boolean add(Object obj, String collectionId) {
IQueue<Object> queue = hazelcastInstance.getQueue(collectionId);
return queue.add(obj);
}
}
Using Powermock 2.0.7 (powermock-api-mockito2, powermock-core, powermock-module-junit4) and mockito-core (3.3.3). I thought I had created a comparable test scenario in a separate project (which worked), but something else must be missing.
Library class to be mocked:
public class CommonConstants {
private ConfigurationDataImpl configurationData;
private static Properties sysProperties;
private static Map<String, String> sysPermissions;
public CommonConstants(ConfigurationDataImpl configurationData) {
this.configurationData = configurationData;
}
public void init() {
sysProperties = this.configurationData.getSysParams();
sysPermissions = this.configurationData.getSysPermissions();
}
public static String getSysProperties(String key) {
return sysProperties.getProperty(key);
}
public static String getSysPermissions(String key) {
return (String)sysPermissions.get(key);
}
}
In my test, I have:
#RunWith(PowerMockRunner.class)
#PrepareForTest({CommonConstants.class}) <==== class that evidently is not modified!
class MyServiceTest {
public MyServiceTest() {}
#Mock
private MyDao myDao;
#InjectMocks
private MyService myService;
#Test
void retrieveUsers() {
RequestPayload rp = returnPayload();
PowerMockito.mockStatic(CommonConstants.class); <==== EXCEPTION
when(CommonConstants.getSysProperties(HOURS_TO_REGISTER)).thenReturn("24");
...
In the service code, I have:
#Service
#Slf4j
public class MyService {
MyDao myDao;
public MyService(MyDao myDao) {
this.myDao = myDao;
}
public UserListResponse retrieveUsers(RequestPayload requestPayload, String customer) {
List<User> users = myDao.getPtdUsers(queryParams, customer, totalRecords);
int hoursToExpire = Integer.parseInt(CommonConstants.getSysProperties(HOURS__TO_REGISTER));
...
Am I leaving out something? All help appreciated.
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.
I'm planning to create my objects in my Spring MVC using the below setup but How can I inject values to my MyService ie; instantiate the object with default value...
public class MyController {
private MyService myService;
#Autowired
public void setMyService(MyService aService) { // autowired by Spring
this.myService = aService;
}
#RequestMapping("/blah")
public String someAction()
{
// do something here
myService.foo();
return "someView";
}
}
MyService
class Myservice(){
String servicename;
public Myservice(servicename){
this.servicename = servicename;
}
}
Without Spring
MyService first = new MyService("firstservice");
MyService second = new MyService("secondservice");
Just declare your constructor with #Autowired to mark it as the constructor to use and its parameter with #Value to indicate the value to use.
#Autowired
public Myservice(#Value("example") String servicename){
Or use a placeholder
#Autowired
public Myservice(#Value("${placeholder.key}") String servicename){
Firstly, your exam are wrong on using Spring DI. To inject Myservice type to another You should declare MyService as a interface instead:
interface Myservice(){
public void foo();
}
After that, declare an implementation of this interface (again, use Spring DI to inject String type):
class BarService() implements Myservice{
String servicename;
#Autowired
public Myservice(#Value("servicename") String servicename){
this.servicename = servicename;
}
public void foo(){
}
}
let imagine I have per entity a repository class (spring data jpa) for database access and a service class. The dependencies are managed by spring framework. Every service method does in most cases the same, so there is mainly code duplication:
public class NewsService {
#Inject
private NewsRepository newsRepository;
public void add(News news) {
// do some validation
newsRepository.save(news);
}
}
public class UserService {
#Inject
private UserRepository userRepository;
public void add(User user) {
// do some validation
userRepository.save(user);
}
}
Now i thought about creating an abstract class like this:
public abstract class AbstractService<T> {
private UnknownRepository unknownRepository;
public void add(T entity) {
// do some validation
unknownRepository.save(entity);
}
}
public class NewsService extends AbstractService<News> {
}
public class UserService extends AbstractService<User> {
}
My problem: How can i overwrite the repository used inside the abstract class based on my entities?
You can replace the UnknownRepository field with an abstract method and a type parameter:
// R is the type of the repository
public abstract class AbstractService<T,R extends BaseRepository> {
protected abstract R getRepository();
public void add(T entity) {
getRepository().save(entity);
}
}
And inject the specific repository to the implementations of this class:
public class NewsService extends AbstractService<News, NewsRepository> {
#Inject private NewsRepository newsRepository;
#Override
public NewsRepository getRepository() {
return newsRepository;
}
// the inherited add() method works now
}