Call method of Controller1 into another method of Controller2 in Spring - java

I did Google a lot to find my problem but I couldn't and sorry If this question already on the stack overflow because I have not find it.
First let take a look into the code
#Controller
public class Controller1 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
}
public int calculation(int i){
//Some Calcucation
return i;
}
}
and second controller is
#Controller
public class Controller2 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
//In this I want to call the calculation(1) method of controller1.
}
}
My question is that is there any way to call the method of calculation() of controler1 in to controller2. But remember I don't want to make method static in controller1.Is there anyway to call it without make it static?
Thanks
Yasir

You should create service bean for example in configuration file (or use # one of the annotaions) and inject it into controller. For example ()
#Configuration
public class MyConfig {
#Bean
public MyService myService(){
return new MyService();
}
}
#Controller
public class Controller1 {
#Autowire
private MyService myService;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
#Controller
public class Controller2 {
#Autowire
private MyBean myBean;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}

Your controllers should not call each other. If there is a logic which needs to be used by both controllers, it is much better to put that into separate bean, which will be used by both controllers. Then you can simply inject that bean to whicheveer controller neccessary. Try not to put any business logic to controllers, try tu put it to specialized class instead which will be web independent if possible and will accept web agnostic business data as user email, account number etc. No http request or response. This way your class with actual logic is reusable and can be unit tested much more easily. Also, if there is state, it should be contained in your classes outside controllers. Controllers should be stateless and not contail any state at all.
When using MVC pattern and you are deciding where to put your logic, you should separate business logic into model and into controllers you should put only logic regarding user interaction, as explained in this stack overflow post.

Related

Spring MVC Ignore Json property for a given controller method

I have a Java class (MyResponse) that is returned by a multiple RestController methods and has a lot of fields.
#RequestMapping(value = "offering", method=RequestMethod.POST)
public ResponseEntity<MyResponse> postOffering(...) {}
#RequestMapping(value = "someOtherMethod", method=RequestMethod.POST)
public ResponseEntity<MyResponse> someOtherMethod(...) {}
I want to ignore (e.g. not serialize it) one of the properties for just one method.
I don't want to ignore null fields for the class, because it may have a side effect on other fields.
#JsonInclude(Include.NON_NULL)
public class MyResponse { ... }
The JsonView looks good, but as far as I understand I have to annotate all other fields in the class with a #JsonView except the one that I want to ignore which sounds clumsy. If there is a way to do something like "reverse JsonView" it will be great.
Any ideas on how to ignore a property for a controller method?
Props to this guy.
By default (and in Spring Boot) MapperFeature.DEFAULT_VIEW_INCLUSION is enabled in Jackson. That means that all fields are included by default.
But if you annotate any field with a view that is different than the one on the controller method this field will be ignored.
public class View {
public interface Default{}
public interface Ignore{}
}
#JsonView(View.Default.class) //this method will ignore fields that are not annotated with View.Default
#RequestMapping(value = "offering", method=RequestMethod.POST)
public ResponseEntity<MyResponse> postOffering(...) {}
//this method will serialize all fields
#RequestMapping(value = "someOtherMethod", method=RequestMethod.POST)
public ResponseEntity<MyResponse> someOtherMethod(...) {}
public class MyResponse {
#JsonView(View.Ignore.class)
private String filed1;
private String field2;
}

Spring Data Rest / Spring Hateoas Custom Controller - PersistentEntityResourceAssembler

I'm attempting to add some additional business logic to the auto-generated endpoints from the RepositoryRestResource. Please see the code below:
Resource:
#RepositoryRestResource(collectionResourceRel="event", path="event")
public interface EventRepository extends PagingAndSortingRepository<Event, Long> {
}
Controller:
#RepositoryRestController
#RequestMapping(value = "/event")
public class EventController {
#Autowired
private EventRepository eventRepository;
#Autowired
private PagedResourcesAssembler<Event> pagedResourcesAssembler;
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedResources<PersistentEntityResource> getEvents(Pageable pageable,
PersistentEntityResourceAssembler persistentEntityResourceAssembler) {
Page<Event> events = eventRepository.findAll(pageable);
return pagedResourcesAssembler.toResource(events, persistentEntityResourceAssembler);
}
}
I've looked at the following two stackoverflow articles:
Can I make a custom controller mirror the formatting of Spring-Data-Rest / Spring-Hateoas generated classes?
Enable HAL serialization in Spring Boot for custom controller method
I feel like I am close, but the problem that I am facing is that:
return pagedResourcesAssembler.toResource(events, persistentEntityResourceAssembler);
returns an error saying:
"The method toResource(Page<Event>, Link) in the type PagedResourcesAssembler<Event> is not applicable
for the arguments (Page<Event>, PersistentEntityResourceAssembler)".
The toResource method has a method signature that accepts a ResourceAssembler, but I'm not sure how to properly implement this and I can't find any documentation on the matter.
Thanks in advance,
- Brian
Edit
My issue was that I thought I could override the controller methods that are auto-created from #RepositoryRestResource annotation without having to create my own resource and resource assembler. After creating the resource and resource assembler I was able to add my business logic to the endpoint.
Resource:
public class EventResource extends ResourceSupport {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Resource Assembler:
#Component
public class EventResourceAssembler extends ResourceAssemblerSupport<Event, EventResource> {
public EventResourceAssembler() {
super(EventController.class, EventResource.class);
}
#Override
public EventResource toResource(Event entity) {
EventResource eventResource = createResourceWithId(entity.getId(), entity);
eventResource.setName(entity.getName());
return eventResource;
}
}
Updated Controller:
#RepositoryRestController
#RequestMapping(value = "/event")
public class EventController {
#Autowired
private EventRepository eventRepository;
#Autowired
private EventResourceAssembler eventResourceAssembler;
#Autowired
private PagedResourcesAssembler<Event> pageAssembler;
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedResources<EventResource> getEvents(Pageable pageable) {
Page<Event> events = eventRepository.findAll(pageable);
// business logic
return pageAssembler.toResource(events, eventResourceAssembler);
}
}
The thing I don't like about this is that it seems to defeat the purpose of having a RepositoryRestResource. The other approach would be to use event handlers that would get called before and/or after the create, save, delete operations.
#RepositoryEventHandler(Event.class)
public class EventRepositoryEventHandler {
#HandleBeforeCreate
private void handleEventCreate(Event event) {
System.out.println("1");
}
}
There doesn't seem to be any events for the findAll or findOne operations. Anyways, both these approaches seem to solve my problem of extending the auto generated controller methods from RepositoryRestResource.
It requires a PagedResourcesAssembler, Spring will inject one for you if you ask.
public PagedResources<Foo> get(Pageable page, PagedResourcesAssembler<Foo> assembler) {
// ...
}
In this case the resource is Foo. It seems in your case the resource you're trying to return is an Event. If that's so, I would expect your code to look something like:
private ResourceAssembler<Event> eventAssembler = ...;
public PagedResources<Event> get(Pageable page, PagedResourcesAssembler<Event> pageAssembler) {
Event event = ...;
return eventAssembler.toResource(event, pageAssembler);
}
You provide the ResourceAssembler<Event> that tells Spring how to turn Event into a Resource. Spring injects the PagedResourcesAssembler<Event> into your controller method to handle the pagination links. Combine them by calling toResource and passing in the injected pageAssembler.
The final result can be returned simply as a body as above. You could also use things like HttpEntity to gain more control over status codes and headers.
Note: The ResourceAssembler you provide can literally be something as simple as wrapping the resource, such as Event, with a Resource object. Generally you'll want to add any relevant links though.
To hack it you can use just PagedResourcesAssembler<Object> like:
#RequestMapping(method = RequestMethod.GET, value = "")
#ResponseBody
public PagedModel<PersistentEntityResource> getEvents(
Pageable pageable,
PersistentEntityResourceAssembler persistentAssembler,
PagedResourcesAssembler<Object> pageableAssembler
) {
return pageableAssembler.toModel(
(Page<Object>) repository.findAll(pageable),
persistentAssembler
);
}

Handle same URL in different controllers based on parameters - Spring MVC

I have a controller which handles a specific URL
#RequestMapping(value= {"/myurl"})
public ModelAndView handleMyURL()
Instead I want to have 2 separate controllers that let me handle this same /myurl based on the parameters passed to it.
If URL is
/myurl?a=1
goto controller A, otherwise use controller B.
Is there a way to do that?
I found this similar question Spring MVC - Request mapping, two urls with two different parameters
where someone has mentioned "use one method in a misc controller that dispatches to the different controllers (which are injected) depending on the request param." , how do I implement that?
Controller 1
#RequestMapping(value= {"/myurl"}, params = { "a" })
public ModelAndView handleMyURL()
Controller 2
#RequestMapping(value= {"/myurl"}, params = { "b" })
public ModelAndView handleMyURL()
Take a look at chapter 4 of this post for more detail
#Controller
#RequestMapping(value= {"/myurl"})
public TestController{
#Autoware
private TestAController testAController;
#Autoware
private TestBController testBController;
public void myMethod(String a){
if(a.equals("1"){
testAController.foo(a);
}else{
testBController.foo(a);
}
}
}
#Controller
#RequestMapping(value= {"/myurl1"})
public class TestAController{
public void foo(String a){
...
}
}
#Controller
#RequestMapping(value= {"/myurl2"})
public class TestBController{
public void foo(String a){
...
}
}

Populate model with default attributes

I am building a website using the Spring-MVC framework. In the template I want to include some 'generic' information. Generic in the way that this is not specific to the current controller, but used on every page. This can be something like a generated menu, the number of minutes a user is logged in or the current temperature on the north pole.
What I am currently doing
I've made an abstract class with a method to populate the model with the default values. My controllers extend this class and I call this method in every (#RequestMapping-ed) method, which is a downside. It feels kinda hacky and is not very flexible.
#Component
public abstract class AbstractBaseController {
#Autowired private SomeService someService;
protected void populateModel(Model model) {
model.addAttribute("value", someService.getGenericValue());
}
}
#Controller
public class HomeController extends AbstractBaseController {
#RequestMapping("/") public String index(Model model) {
populateModel(model);
return "home";
}
}
My question
How should I provide these generic attributes to my view. Is there a better way than what I am doing now?
(I am using Spring/Spring-MVC 4, JavaConfig, Thymeleaf with thymeleaf-layout-dialect)
Okay, so I actually wasn't that hard. You can annotate a method with #ModelAttribute which makes it available in the template.
#Component
public abstract class AbstractBaseController {
#Autowired private SomeService someService;
#ModelAttribute("value")
protected String getValue() {
return someService.getGenericValue());
}
}
Mind you, this method will always be called. Also when ${value} is not called in the template. So it is not more 'flexible' than the method I described in the question.

Spring MVC session Attribute set intially

I have used the Spring MVC. I set the Session Attribute value like
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String initHome(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
return "homeMenu";
}
It is working fine if i click the home menu url(/home). but if i did not go the
home means it says error as 'session attribute clientObject is required'
so i decided to set sessionattibutes in constructor of controller
#Autowired
public MyController(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
}
it also says error
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myController'
I tried to set using the RequestMapping also like
#RequestMapping(value = "/", method = RequestMethod.GET)
public void initController(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
}
this method is also not called intially
my cointroller look like
#RequestMapping("/sample")
public class MyController {
..
..
is it possible to set the sessionAttribute value in the constructor of controller? or any other way to set the session Attribute initially?
Thanks in advance for your help.
Assuming your createDefaultClient is in the controller add a #ModelAttribute annotation to it.
#ModelAttribute("clientObject")
public ClientObject createDefaultClient() { ... }
This method will be called before any request handling method (as explained in the reference guide)
If you combine that with a #SessionAttribute annotation on your class (which you might already have). You should be able to achieve what you want.
In your request handling methods (methods annotated with #RequestMapping) you can now simply inject the client object as a method argument.
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String initHome(#ModelAttribute("clientObject") ClientObject clientObject) {
// Do something with the clientObject
return "homeMenu";
}
This will only work consistenly within the same controller, so if you need the ClientObject to be used somewhere else (another controller for instance), this isn't going to work (nor is #SessionAttributes designed for that).

Categories