I'm studying the MVVM to see if it can help me for my upcoming projects. What I understand so far, is that I need to use a ViewModel for holding my UI datas. I also need to use a Repository class to perform all my Requests to WebServices, and I'm using the Volley Library.
So here's what I did :
The ViewModel
public class MyViewModel extends ViewModel {
private MyRepository repository;
private MutableLiveData<MyPojo> pojo;
public MyViewModel(MyRepository repository) {
this.repository = repository;
this.pojo = new MutableLiveData<>();
}
public LiveData<MyPojo> updatePojo(){
pojo.postValue(repository.getPojo());
return pojo;
}
}
The Repository class
public class MyRepository {
private Application application;
private LiveData<MyPojo> pojo;
public MyRepository(Application application) {
this.application = application;
}
public MyPojo getPojo(){
if(pojo == null){
ApiRequest apiRequest = new ApiRequest(ApiSingleton.getInstance(application).getRequestQueue(), application);
apiRequest.apiGetRequest(ApiRequest.MY_ENDPOINT, null, new ApiRequest.apiCallback() {
#Override
public void onSuccess(Context context, JSONObject jsonObject) {
pojo = ApiResponseParser.parse(jsonObject, MyPojo.class);
}
#Override
public void onError(Context context, String message) {
}
});
}
return pojo;
}
}
It's specified here that a ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context. As you can see, I must use a context in order to perform a Volley request in my Repository class, and my ViewModel has a reference on this class.
Am I missing something in my design? Is Volley not compatible here?
Instead of passing the Application to your MyRepository's constructor and creating ApiRequest, you can pass the ApiRequest to MyRepository's constructor.
public MyRepository(ApiRequest apiRequest) {
this.apiRequest = apiRequest;
}
Now the MyRepository has no reference to Context.
And, regarding ViewModel having direct reference to MyRepository, you can do dependency inversion:
Create an interface, for instance, MyDataStore with the method getPojo(). MyRepository will implement this interface. While creating MyViewModel, you will pass the MyRepository to it, but MyViewModel will only have reference to MyDataStore.
interface MyDataStore {
... getPojo()
}
public class MyRepository implements MyDataStore {
...
}
public MyViewModel(MyDataStore dataStore) {
this.dataStore = dataStore;
this.pojo = new MutableLiveData<>();
}
The LifeCycle library provides the AndroidViewModel component, which is just an Application's context aware ViewModel, pretty close to Bob's answer, and did the job here without memory leaks hazards.
Related
I want all the Service classes in my backend to have CRUD methods.
For that purpose, I thought of creating an interface:
public interface ServiceCRUD {
public Object save(Object object);
...
}
And then on my service, implement it:
#Service
public class SampleService implements ServiceCRUD {
#Autowired
private SampleRepository repository;
#Override
public Sample save(Sample sample) {
return repository.save(sample);
}
...
}
I haven't touched Java in a while, but if I recall correctly, every object extend Object, so why is it that I can't use Object to have the service accept all the entities I might have?
Best regards
You can achieve such scenario using Generics
interface ServiceCRUD {
public < E > void save(E object);
}
class Sample {
private String name = "Joe";
#Override
public String toString() {
return "hello"+name;
}
}
class SampleService implements ServiceCRUD {
#Override
public < Sample > void save(Sample sample) {
System.out.print(sample.toString());
}
}
public class Main {
public static void main(String[] args) {
new SampleService().save(new Sample());
}
}
This is just an example ,you can extend it as per your use case.
See working here
Your interface declares that one is possible to save Object, i. e. any object. But your implementation declares that it cat get only Sample, that's why you get compilation error.
You should go with genetic and let each implementation to declare what kind of object it can deal with. I strongly suggest to have a look at spring data project. It will save you a lot if time
In Spring Framework classes that represent an event to be published by the ApplicationEventPublisher and listened to by the #EventListener are
EventObject <- ApplicationEvent <- PayloadApplicationEvent.
My question is what is the non-nullable source in the initial EventObject constructor and all derived subclasses constructors?
Javadocs give a rather vague explanation that it is
"the object upon which the Event in question initially occurred".
Is it an associated domain entity or publisher service or something else?
Additionally, I am confused why is it required at all if #EventListener states that
"Events can be ApplicationEvent instances as well as arbitrary objects"?
I understand source to be the where the event is created. For example, a #Service that processes a web request received from a #Controller. Therefore, when you call ApplicationEventPublisher.publishEvent() the source parameter to the ApplicationEvent event is this, the service.
public class AwesomeEvent extends ApplicationEvent {
private final String howAwesome;
public AwesomeEvent(Object source, String howAwesome) {
super(source);
this.howAwesome = howAwesome;
}
}
#Service
#RequiredArgsConstructor // because, lazy
public class AwesomeService {
private final ApplicationEventPublisher eventPublisher;
public void awesomeMethod() {
// Do superhero awesome stuff
eventPublisher.publishEvent(new AwesomeEvent(this, "Extremely"));
}
}
Just show some code to you. Look at the getApplicationContext() method.
public abstract class ApplicationContextEvent extends ApplicationEvent {
public ApplicationContextEvent(ApplicationContext source) {
super(source);
}
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an example for what I mean:
I know I can do that:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
#Controller
public class Controller{
#RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
#ResponseBody
public String searchProv(#ResponseBody Option1 data1){
return "option1"
}
#RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
#ResponseBody
public String searchProv(#ResponseBody Option2 data2){
return "option2"
}
}
but I wonder if it is possible to passing different json object to the same endpoint and do that:
#Controller
public class Controller{
#RequestMapping(value = "service/getData", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Any> getData(#ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
such that 'Option1And2' is generic object can be option1 or option2.
I tried to replace 'Option1And2' to 'Any' but it didn't went well because I get a list of keys and values
You should use JsonNode object.
for your example you should do this:
#Controller
public class Controller{
#RequestMapping(value = "service/getData", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Any> getData(#RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a #Service or #Repository, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
#GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
#PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// #DeleteMapping, #PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
#Controller
#RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
#Autowired CategoryServ serv;
#Override
public GenericService<Category> getService() {
return serv;
}
}
#Controller
#RequestMapping("/product")
class ProductContr extends GenericController<Product> {
#Autowired ProductServ serv;
#Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
#Service
class CategoryServ extends GenericService<Category> {
#Autowired CategoryRepo repo;
#Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
#Service
class ProductServ extends GenericService<Product> {
#Autowired ProductRepo repo;
#Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
#NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
#Repository
interface CategoryRepo extends GenericRepository<Category> {
}
#Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with #RequestMapping automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added #RequestMapping annotation and then call the super class method.
Another approach is using annotations.
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
#Data
public class Option1And2{
private String name;
private Long id;
}
If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule.
If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
Let's say I have this code structure:
public class NotificationService {
public void send(Notification notification) {
// call other services and send the notification
}
}
public class OrderNotification implements Notification {
#Autowired
public TranslationService translationService;
private String orderNumber;
public OrderNotification(String orderNumber) {
this.orderNumber = orderNumber;
}
public String getMessage() {
return translationService.trans('notification.order', new Object[]{orderNumber});
}
}
So, my goal is to use the NotificationService in this way:
notificationService.send(new OrderNotification(orderNumber));
But I know that code above won't work, because of the translationService won't be resolved.
My goal is to pass custom parameters to my Notification classes and being able to use services inside that class. What is the best way to do it in the Spring?
I know that below is not the correct answer to your question. It is however a bad design pattern to combine Entities and Services. An Entity should only contain information about the object and not business logic. A Service contains all the business logic.
You need to separate your Service from your Entity.
OrderNotification looks like a regular entity. The entity should not contain business logic. You need a specific service for the business logic.
public class OrderNotification implements Notification {
private String orderNumber;
public OrderNotification(String orderNumber) {
this.orderNumber = orderNumber;
}
public String getMessage() {
return "Order number: " + orderNumber;
}
//Getter & Setters
...
}
#Service
public class NotificationService {
#Autowired
public TranslationService translationService;
public void send(Notification notification) {
//I do not know what trans accepts, so I assume it can accept Notification
translationService.trans(notification.getMessage());
}
}
If you really need to combine the entity and service - Then I recommend this approach:
#Service
public class Master{
#Autowired
NotificationService notificationService
public void testMethod(){
Notification notification = notificationService.createOrder("order1");
notificationService.send(notification);
}
}
#Service
public class NotificationService {
#Autowired
public TranslationService translationService;
public Notification createOrder(String orderNumber){
return new OrderNotification(orderNumber, translationService);
}
public void send(Notification notification) {
// call other services and send the notification
notification.getMessage();
}
}
public class OrderNotification implements Notification {
private TranslationService translationService;
private String orderNumber;
//I have changed this constructor to accept TranslationService.
public OrderNotification(String orderNumber, TranslationService translationService) {
this.orderNumber = orderNumber;
this.translationService = translationService;
}
public String getMessage() {
return translationService.trans('notification.order', new Object[]{orderNumber});
}
}
You have few options available:
Configure AOP and load time weaving to process Spring annotations on objects created with new keyword. This is explained in the docs 5.8.1. Using AspectJ to dependency inject domain objects with Spring.
Declare OrderNotification as a prototype scoped bean and obtain each instance from the context using BeanFactory.getBean(Class<T> requiredType, Object... args) method.
String orderNumber = "123";
OrderNotificaton = factory.getBean(OrderNotificaton.class, orderNumber);
Drop the #Autowired and use plain constructor injection.
public OrderNotification(TranslationService translationService, String orderNumber) {
this.translationService = Objects.requireNonNull(translationService);
this.orderNumber = Objects.requireNonNull(orderNumber);
}
If you only require simple #Autowired I'd go with option 3. It's the simplest approach and makes writing unit tests easier as you don't have to depend on Spring.
I'm learning Google Guice.
I understood how to bind an interface to its implementation.
Now, I have the following helper class :
class PersonHelper {
public static FakeDatabaseConfiguration dbConfig;
public PersonHelper(){
if (dbConfig == null){
dbConfig = new FakeDatabaseConfiguration();
dbConfig.setHost('127.0.0.1');
dbConfig.setPort('3306');
dbConfig.setUsername('root');
dbConfig.setPassword('root');
}
}
public List<Person> getPersons(){
FakeResult fakeResult = dbConfig.executeSQL("select * from Person");
return fakeResult.asList();
}
}
Today, I'm using it like this:
PersonHelper personHelper = new PersonHelper();
List<Person> personsList = personHelper. getPersons();
I'm pretty sure there is a way to make this class better.
Question : How can I make this class as a singleton using Guice so that I don't lazy load the dbConfig variable at each instanciation ?
(I read that there is a #Singleton annotation but, it's considered in Guice just as a scope.)
Regards
First, in your module, you have to declare a provider (FakeDatabaseConfigurationProvider). As stated, this is the best way to inject a configuration object.
Then, declare your helper class as a Singleton and bind it in your module.
This will allow your helper class to be used like this :
public class SomeClass{
#Inject
private PersonHelper personHelper;
...
public void someMethod(){
...
List<Person> personsList = personHelper.getPersons();
..
}
}
And the same instance will be shared through your app.
Here is the suggested code :
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(FakeDatabaseConfiguration.class).toProvider(FakeDatabaseConfigurationProvider.class);
bind(PersonHelper.class).in(Scopes.SINGLETON);
}
/**
* FakeDatabaseConfigurationProvider implementation
*/
static class FakeDatabaseConfigurationProvider implements Provider<FakeDatabaseConfiguration> {
#Override
public FakeDatabaseConfiguration get() {
FakeDatabaseConfiguration dbConfig = new FakeDatabaseConfiguration();
dbConfig.setHost('127.0.0.1');
dbConfig.setPort('3306');
dbConfig.setUsername('root');
dbConfig.setPassword('root');
return dbConfig;
}
}
}
Then, in your PersonHelper :
public class PersonHelper{
private FakeDatabaseConfiguration fakeDatabaseConfiguration;
#Inject
public PersonHelper(final FakeDatabaseConfiguration fakeDatabaseConfiguration){
this.fakeDatabaseConfiguration = fakeDatabaseConfiguration;
}
public List<Person> getPersons(){
FakeResult fakeResult = fakeDatabaseConfiguration.executeSQL("select * from Person");
return fakeDatabaseConfiguration.asList();
}
}
Please look at Binding #Provides method as eager singleton See if that helps. The eagerSingleton part might work for you.
Having it as a scope is exactly what you want: Scopes effectively tell Guice when it's allowed to reuse the same object it's already created, and for #Singleton that answer is "always".
If you were to list the class like this:
#Singleton // Could also be in your module or #Provides method.
class PersonHelper {
private FakeDatabaseConfiguration dbConfig;
public PersonHelper(){
dbConfig = new FakeDatabaseConfiguration();
dbConfig.setHost('127.0.0.1');
dbConfig.setPort('3306');
dbConfig.setUsername('root');
dbConfig.setPassword('root');
}
public List<Person> getPersons(){
FakeResult fakeResult = dbConfig.executeSQL("select * from Person");
return fakeResult.asList();
}
}
Then the the class itself becomes a Singleton. The FakeDatabaseConfiguration will be created whenever the class is instantiated, but for all accesses through Guice, that will only happen once.
Of course, none of this applies to direct constructor calls as new PersonHelper(), but with few exceptions Guice is only good at making guarantees about objects that it provides. Any accesses that Guice can control, including through getInstance or #Inject-annotated fields and constructors, will only see PersonHelper (and therefore FakeDatabaseConfiguration) created exactly once.