IntroductionIntroductionIntroductionIntroduction
As you know, the MVC in Spring is composed from the dispatcher servlet, which turns
the HTTP requests into the method’s calls, and from various annotations, which help to
bind the parameters. I kept this idea in Lime MVC. The whole concept of Lime MVC
describes image below:
Let’s assume a book store web application. In the application, we’ve got BookStore
controller class and we want to execute the controller’s getBooks method by the
following request:
http://server.com/bookstoreapp/bookstorecontroller/getbooks?author=Gosling
public class BookStoreController {
private BookStoreDao dao;
BookStoreController(BookStoreDao dao) {
this.dao = dao;
}
public List getBooks(String author) {
//code do some processing and fill a list of books
...
return booksList;
}
public Book getBook(String bookId) {
//code do some processing and get a concrete book
return book;
}
}
How can we turn this class into a web controller for the HTTP request? There are two
steps required to do so – to annotate the controller class and map the controller class to
some URI and view. It’s good to be familliar with the Google Guice Servlet, because
Lime configuration is built upon it.
Let’s do the first step. We need to annotate the controller class, because we need to
somehow tell Lime how the parameters are bound and what’s the name of the model. As
you can see, we need to inject a DAO implementation as well.
@Controller
@Session
public class BookStoreController {
private BookStoreDao dao;
@Inject
BookStoreController(BookStoreDao dao) {
this.dao = dao;
}
@Path("/books") @Model("books") @View("view.jsp")
public List getBooks(@RequestParameter("author") String author){
//code do some processing and fill a list of books
...
return booksList;
}
}
All controller classes must be annotated with @Controller. It’s a mark which tells Lime
that the class is a controller. @Session annotation is Guice’s annotation. This annotation
specifies the controller’s scope. All the controllers exist in Guice. It means that all
controller’s objects are constructed via Guice. So the controller can be injected, and we
can use injection in the controller as well. As you can see, we’ve used this technique for
Dao. Controller class is some kind of container for the actions which we can perform and
which produce the model’s data. So the most important annotations are used around
methods. The @Path specifies for what URL is the action executed and under which
name is the method’s result stored. @RequestParameter binds the value from the POST
parameter to the method’s parameter. It means that ?author=Gosling will be forwarded to
the method’s ‘author’ parameter.
As I mentioned above, the second step is to put the controller into the configuration and
link it to the view. If you’re familiar with the Guice Servlet, you know that we need to
extend GuiceServletContextListener, and to implement the createInjector() method. I
recommend to read the Guice Servlet documentation if you’re not familiar with this.
Lime’s core is represented by the MvcModule class which is constructed when we create
the Guice Injector. The MvcModule has abstract method configureControllers(), and we
put all configuration around controllers here. It’s the same principle like in the Guice’s
ServletModule.
public class BookStoreConfiguration extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
Injector injector = Guice.createInjector(
new MvcModule() {
@Override
protected void configureControllers() {
control("/bookstorecontroller/*")
.withController(BookStoreController.class);
}
}
});
return injector;
}
}
This configuration tells Lime that all URLs for /bookstorecontroller are processed by the
BookStoreController class and that the model is shown in view.jsp.
• Controllers
o Controllers as interfaces
o Parameters in controller’s methods
� POST/GET
� Parse values from URI
� Values from session
� Request scoped attributes
� Special types
ControllersControllersControllersControllers
The controller class may be any class or interface annotated by @Controller annotation.
Basically all your logic is placed into the controller class. Controller classes produce data
model and selects the view, which renders the data. Controller classes composes from
various methods. You will annotate these methods and tell to the Lime MVC for what
request the method will be invoked and how will be parameter's values picked up from
request/sessione etc. Each controller must be binded to some path in MvcModule.
ControllersControllersControllersControllers asasasas interfacesinterfacesinterfacesinterfaces
In Introduction you saw a simple example of the controller. The controller has been
directly binded to some URL in MvcModule. There is also another usage of controller.
You may split up controller's implementation and definition. You may have definition
described as interface and implementation in separated class which implements the
interface.
Example:
@Controller
public interface IBookstoreController {
@Path("/allbooks") @View("allbooks.jsp")
public Model allBooksInStore();
@Path("/bookinfo/(.*)") @View("bookinfo.jsp")
public Model bookInfo(@UriParameter(1) String bookId);
@Path("/reserver/(.*)") @View("bookreservation.jsp")
public String reserveBook(@UriParameter(1) String bookId);
}
public class BookstoreControllerImpl implements IBookstoreController{
public ModelMap allBooksInStore() {
ModelMap m = new ModelMap();
...
return m;
}
public ModelMap bookInfo(String bookId) {
ModelMap m = new ModelMap();
...
return m;
}
public String reserveBook(String bookId) {
String result = ...;
return result;
}
}
We've got the fully annotated IBookstoreController and BookstoreControllerImpl which
implements this interface. Next step is tell to Lime MVC how to use the controller. You
will put the configuration code into your WebAppModule.
public class WebAppModule extends MvcModule {
protected void configureControllers() {
...
bind(IBookstoreController.class).to(BookstoreControllerImpl.clas
s);
control("/bookstore/*")
.withController(IBookstoreController.class);
}
}
ParametersParametersParametersParameters inininin controllercontrollercontrollercontroller’’’’ssss methodsmethodsmethodsmethods
Only in a few cases we’ve got the controller’s methods without parameters. The main
goal of this section is to describe and show how the data are bound and forwarded from
the HTTP Requests to the method’s parameters.
POST/GETPOST/GETPOST/GETPOST/GET
Since long ago, Web developers are using these two methods for posting data to server.
Also it was the first implemtented binding in Lime. For that purpose serves the
@RequestParameter annotation. In our BookStore example, we used it in a simple form.
Let’s see it on advanced examples like conversions and arrays.
Method expects integer parameter:
public void someMethod( @RequestParameter(“somenumber”) int somenumber)
Method expects date parameter, if the parameter is missing, then it is set to default
value:
public void someMethod( @RequestParameter(“somedate”)
@DateConv(value="yyyyMMdd", defaultValue="20010101") Date somedate) {
}
Method expects boolean parameter where in HTTP request ‘N’ means false and ‘Y’
means true:
public void someMethod( @RequestParameter(“somebool”)
@BooleanConv(trueVal=”Y”, falseVal=”N”) boolean somebool) {
}
Method expects array of integers (note: in HTTP request all values must be named like
somearray0, somearray1 … somearrayn):
public void someMethod( @RequestParameter(“somearray”) int[] numbers)
{
}
Actually only conversions to boolean, date, long, float, double and String values and
arrays are supported.
ParseParseParseParse valuesvaluesvaluesvalues fromfromfromfrom URIURIURIURI
This is a nice technique adapted especially in the REST APIs. Let’s imagine following
request: ‘http://www.someserver.com/app/article/group/123/id/45’. As you can see, the
last number means the ID of the article and the previous means the group. In Lime, we
can parse these IDs from URI and forward them to the method’s parameters. Let’s
consider the fact that we can use regular expressions in path mapping and the existence
of @UriParameter annotation. For our illustration case we can use the method below:
@Path("/article/group/(\\d+)/id/(\\d+)")
public Article getArticle(@UriParameter(1) int articleGroup,
@UriParameter(2) int articleId)
{
...
}
Note: the same conversion like in POST/GET method is used here.
ValuesValuesValuesValues fromfromfromfrom sessionsessionsessionsession
Sometimes we want to forward a value from the session into the method’s parameter.
For that purpose there is @SessionParameter annotation and its usage is simple:
@Path("/userprofile")
public User showUserProfile( @SessionParameter("loggeduser") User
loggedUser) {
...
}
The logged user’s object is stored in the session under the name ‘loggeduser’.
@SessionParameter picks up this object from the session and forwards it to the
method’s parameter loggedUser.
RequestRequestRequestRequest scopedscopedscopedscoped attributesattributesattributesattributes
This term means request's attributes data. If you want extract data from request
attributes and fill up into method's parameter, just use the annotation
@RequestScopedAttribute with attribute's name. This is useful when you have some
special interceptor which fill in something into attribute and you want use this value in
you method.
Let's assume we've got security interceptor which prepare the 'USER' request attribute.
class SecutiryInterceptor implements InterceptorHandler {
...
public void preHandle(HttpRequest request, ...) {
User actualUser = ...;
request.setAttribute("USER", actualUser);
}
}
In your controller then you could access to this attribute:
@Controller
class MyController {
...
@Path("/somepath");
public voidhandleRequest( @RequestScopedAttribute("USER") User
actualUser ) {
...
}
}
The parameter actualUser will be filled in with value from our security interceptor.
SpecialSpecialSpecialSpecial typestypestypestypes
There is a set of special parameter types in the methods. These special types are
automatically filled by Lime. It’s Model, HttpServletRequest, HttpServletResponse.
Following method automatically obtains the request from the servlet and actual model
object:
@Path("/books")
public List getBooks( HttpServletRequest request, ModelMap m) {
...
}
Maybe you’re asking yourself: why Model? The main idea was to create some kind of
chain. Let’s imagine the following situation: In the books page, we want to show all the
books and also the user’s information. In the User view, we want to show only the user’s
information:
@Controller
@Session
public class BookStoreController {
private Dao someDao = ...;
@Path("/books")
public ModelMap showBooks(@SessionParameter("loggeduser") String
userId) {
ModelMap m = new ModelMap();
...
m.put("books", books);
User user = getUserProfile(userId);
m.put("userprofile", user);
return m;
}
private User getUserProfile(String userId) {
return someDao.getUser(userId);
}
@Path("/user")
public ModelMap showUserProfile(@SessionParameter("loggeduser")
String userId) {
ModelMap m = new ModelMap();
User user = getUserProfile(userId);
m.put("userprofile", user);
return m;
}
}
Because there’s the possibility to forward the model object into the method, we can
remove one method and reduce the code to:
@Controller
@Session
public class BookStoreController {
private Dao someDao = ...;
@Path("/books")
public ModelMap showBooks(@SessionParameter("loggeduser") String
userId) {
ModelMap m = new ModelMap();
...
m.put("books", books);
...
return getUserProfile(userId, m);
}
@Path("/user")
private ModelMap getUserProfile(@SessionParameter("loggeduser")
String userId, ModelMap m) {
User user = someDao.getUserProfile(userId);
m.put("userprofile", user);
return m;
}
}
ModelModelModelModel
The model represents all the data produced in the controller, which are forwarded to the
view. The BookStore example shows us how we can forward a list of books to the view.
What if we need to forward more data from the method? For that purpose we can return
the ModelMap object as a result of the method. Let’s assume that we want to return
various data (author’s books and also the user’s information). The controller’s method
will then be:
@Controller
@Session
public class BookStoreController {
@Path("/books")
public ModelMap getBooks(@RequestParameter("author") String
author) {
ModelMap m = new ModelMap();
…
m.put("books", books);
…
m.put("loggedUser", loggedUser);
…
return m;
}
}
By default, all the model’s data are stored like the request’s attributes. In JSP we can
access the data:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
${loggedUser.userName}
${book.title} - ${book.author}
The data’s scope is limited to one request. Fortunately, we can implicitly set the attribute
to be stored into the session. Let’s assume that we want to store loggedUser into the
session:
@Controller(sessionAttributes={"loggedUser"})
@Singleton
public class BookStoreController {
@Path(path = "/login")
public ModelMap login(@RequestParameter("user") String user,
@RequestParameter("password") String password) {
ModelMap m = new ModelMap();
…
m.put("loggedUser", user);
…
return m;
}
@Path(path = "/login")
public ModelMap logout() {
ModelMap m = new ModelMap();
…
m.put("loggedUser", null);
…
return m;
}
}
• View
o In annotation
o Method returns View
o Redirecting view
• View Resolver
ViewViewViewView
In Lime, the view is a object, which takes care how the data are shown. All views in Lime
are implementing a View interface. Probably one of the most used representations of
data is the JSP. In Lime, there exists JspView, which forwards model data to this view
and the view is shown like a result of the request. There are several ways how we can
specify the view for the controller.
InInInIn annotationannotationannotationannotation
Lime automatically creates NamedView for each method which annotated by @View.
Example below shown how to use this parameter.
@Controller
public class BookStoreController {
...
@Path("/logout") @View("logout.jsp")
public void logout() {
...
}
}
You might define a named view for whole Controller class:
@Controller
@View("main.jsp")
public class BookStoreController {
...
@Path("/action1")
public void action1(...) {
...
}
...
@Path("/action2")
public void action1(...) {
...
}
}
MethodMethodMethodMethod returnsreturnsreturnsreturns ViewViewViewView
In many cases we want to specify different views for each method in controller. You will
eturn the view object as a result.
@Controller
public class BookStoreController {
@Path("/logout")
public View logout() {
return new JspView("logout.jsp");
}
@Path("/basket")
public ModelAndView showBasket() {
ModelMap ml = new ModelMap();
...
return ModelAndView(m, new JspView("basket.jsp"));
}
}
RedirectingRedirectingRedirectingRedirecting viewviewviewview
You could annotate your method with @RedirectView. This annotation etermines that the
view should redirect to some URL. The annotation's value can be absolute URL in form
'http://someserver:port/path' or also can be relative path in form '/somepath'. Relative
path can be relative onto controller context when contextRelative is set to true or onto
server when contextRelative is set to false.
Model data are transformed as URL parameters. Let's assume controller's method:
...
@Path("/somepath")
@Model("param")
@RedirectView("http://server/path")
public String redirectToSomewhere()
{
return "value";
}
...
The redirection URL will looks like 'http://server/path?param=value'. You could also
returns the redirect view directly from method:
...
@Path("/logout")
public ViewPoint logout() {
return new RedirectViewPoint("/somepath");
}
...
ViewViewViewView ResolverResolverResolverResolver
Concept of the View Resolver is very simple and is used only for NamedViews.
NamedView is special view, which purpose is only for identify. Views are identified by
name. View resolver then resolves the names to a cocrete views.
Let's assume situation. The controller produce data model and select some NamedView
'main.jsp'. The NamedView is translated by View Resolver to the JspView. You can
defined different JSP for this view, and you don't need change every occurence of the
'main.jsp' in code. You will bind in your WebAppModule for name 'main.jsp' a different
JSP or even different View implementation.
Example:
@Controller
class BookstoreController {
@Path("/login") @View("main.jsp");
public void login(String user, String pass) {
...
}
}
...
public class WebAppModule extends MvcModule {
protected void configureControllers() {
...
bindViewName("main.jsp").toJsp("/path/to/jsps/main.jsp");
control("/bookstore").withController(BookStoreController.class);
}
}
• View Extensions (JSilver, Velocity, Freemarker)
o JSilver
o Velocity
o Freemarker
ViewViewViewView ExtensionsExtensionsExtensionsExtensions (JSilver,(JSilver,(JSilver,(JSilver, Velocity,Velocity,Velocity,Velocity,
Freemarker)Freemarker)Freemarker)Freemarker)
The JSPs are basically fine, but they have one disadvantage. You might put into the JSP
a Java code. This ability is completely out of the MVC. Ok, let's cancel the JSPs. But how
we will generate dynamic content? My personal opinion is that a template engine is right
answer. I've created 3 extensions which are implementing 3 different template engines.
They are similar to each other.
JSilverJSilverJSilverJSilver
JSilver is lightweight, pure java implementation of ClearSilver which is suitable for HTML
very well. JSilver is used in the Doclava as well. If you want to use JSilver as the
template engine, you will download the JSilver extension, install the JSilverModule and
you will annotate your methods with @JSilverView annotation.
Module:
public class SomeModule extends MvcModule {
protected void configureControllers() {
...
install(new JSilverModule(getServletContext()));
//example of how to bind JSilver view
bindViewName("someview").toViewInstance(
new JSilverViewPoint("page1.jsilver"));
}
}
Controller:
public class SomeController{
@Inject
private JSilver jsilver;
@Path("/path") @Model("data") @JSilverView("page2.jsilver")
public void Data doAction() {
Data data = jsilver.createData();
...
return data;
}
}
VelocityVelocityVelocityVelocity
If you prefer the Velocity template engine, you will download the Velocity extension for
the Lime MVC. Again, you will install the VelocityModule in your MvcModule and after
that you can use the @VelocityView and VelocityView in you web app.
Module:
public class SomeModule extends MvcModule {
protected void configureControllers() {
...
install(new FreemarkerModule(getServletContext()));
bindViewName("someview").toViewInstance(new
VelocityViewPoint("page1.velocity"));
}
}
Controller:
public class SomeController{
@Path("/path") @Model("data") @VelocityView("test.velocity")
public void HashMap doAction() {
HashMap data = new HashMap();
data.put("param", "Lime MVC");
return data;
}
}
Velocity template (test.velocity):
Hello $data
FreemarkerFreemarkerFreemarkerFreemarker
The last extensions is implementing the Freemarker template engine. You will do same
steps as in JSilver and Velocity case.Download the Freemarker and install the
FreemarkerModule. After that you can use the @FreemarkerView.
Module:
public class SomeModule extends MvcModule {
protected void configureControllers() {
...
install(new FreemarkerModule(getServletContext()));
//example of how to bind JSilver view
本文档为【limemvc】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。