Reduce Repetitive Code in Spring MVC Controllers
source link: https://lorenzo-dee.blogspot.com/2020/09/reduce-repetitive-code-in-spring-mvc.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Reduce Repetitive Code in Spring MVC Controllers
After spending some time doing sustained engineering (a.k.a. maintaining legacy code), I ventured to reduce repetitive code in Spring MVC @Controller
s. I started with an abstract base controller class. But I soon found out that it was a dead-end because @RequestMapping
is not inherited from (or combined with) parent classes and/or interfaces (see Spring MVC @RequestMapping
Inheritance).
With some free time to further think about this, I took different approach.
@Controller
public
class
RepositoryCrudController {
private
final
Repositories repositories;
private
final
RepositoryInvokerFactory repositoryInvokerFactory;
// ...
@RequestMapping
(
"/{repository}"
)
String index(
@PathVariable
(
"repository"
) String repositoryKey,
Pageable pageable, Model model) {
// ... (only if repository has findAll(Pageable) method)
return
repositoryKey +
"/index"
;
}
@GetMapping
(
"/{repository}/{id}"
)
String show(
@PathVariable
(
"repository"
) String repositoryKey,
@PathVariable
(
"id"
) Object id, Model model) {
// ... (only if repository has findById method)
return
repositoryKey +
"/show"
;
}
@GetMapping
(path =
"/{repository}"
, param =
"create"
)
String create(...) {
// ... (only if repository has save method)
return
repositoryKey +
"/create"
;
}
@PostMapping
(
"/{repository}"
)
String save(...) {
// ... (only if repository has save method)
return
"redirect:/"
+ repositoryKey +
"/{id}"
;
}
@GetMapping
(path =
"/{repository}/{id}"
, param =
"edit"
)
// ... edit (only if repository has findById and save methods)
@PutMapping
(
"/{repository}/{id}"
)
// ... update (only if repository has save method)
// @DeleteMapping("/{repository}/{id}")
// ... delete (only if repository has deleteById method)
}
This approach is largely inspired by RepositoryEntityController
of Spring Data REST.
Instead of defining an abstract base controller class, I created a concrete controller class with the default (or repetitive) behavior. The default behavior relies on invoking methods on Spring Data repositories.
For custom controllers, instead of creating subclasses (of the abstract base class), the controller can simply define handler methods that behave differently. For example:
@Entity
public
class
Article {...}
// Spring Data JPA
public
interface
ArticleRepository
extends
CrudRepository<Article, ...> {...}
@Controller
@RequestMapping
(
"/articles"
)
public
class
ArticlesController {
// no need for index() handler method
// just set-up "articles/index" view
// no need for show() handler method
// just set-up "articles/show" view
@GetMapping
(param =
"create"
)
String create(Model model) {
// Do something that is _not_ *default* behavior
// e.g. provide options for dropdowns, or use form-backing object/JavaBean
// ...
return
"articles/create"
;
}
// no need for save() handler method
// just set-up "articles/show" view
@GetMapping
(path =
"/{id}"
, param =
"edit"
)
String edit(
@PathVariable
(
"id"
) ... id, Model model) {
// Do something that is _not_ *default* behavior
// e.g. provide options for dropdowns, or use form-backing object/JavaBean
// ...
return
"articles/edit"
;
}
// no need for update() handler method
// just set-up "articles/show" view
}
The above will work because Spring MVC chooses the more specific mapping first. When a GET /articles?create
request is received, the ArticlesController
will be chosen to handle it (and not RepositoryCrudController
). But if ArticlesController
handler methods were not defined, the GET /articles?create
request would have been handled by the RepositoryCrudController
.
With this simple fall-back controller that has the default behavior, developers (team mates) can then focus on the domain, creating views, or creating controllers with custom behavior (e.g. Ajax, Server-generated JavaScript Responses).
That's all for now.
No comments:
Post a Comment
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK