标签:end ext XML art http bean 混合 proc ace
对于简单的Java Web项目,我们的项目仅仅包含几个jsp页面,由于项目比较小,我们通常可以通过链接方式进行jsp页面间的跳转。
但是如果是一个中型或者大型的项目,上面那种方式就会带来许多维护困难,代码复用率低等问题。因此,我们推荐使用MVC模式。
MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间。
MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C则是控制器。
使用的MVC的目的:在于将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。比如Windows系统资源管理器文件夹内容的显示方式,下面两张图中左边为详细信息显示方式,右边为中等图标显示方式,文件的内容并没有改变,改变的是显示的方式。不管用户使用何种类型的显示方式,文件的内容并没有改变,达到M和V分离的目的。
在网页当中:
下图说明了三者之间的调用关系:
用户首先在界面中进行人机交互,然后请求发送到控制器,控制器根据请求类型和请求的指令发送到相应的模型,模型可以与数据库进行交互,进行增删改查操作,完成之后,根据业务的逻辑选择相应的视图进行显示,此时用户获得此次交互的反馈信息,用户可以进行下一步交互,如此循环。
常见的服务器端MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF;常见前端MVC框架:vue、angularjs、react、backbone;由MVC演化出了另外一些模式如:MVP、MVVM。
注意:我们应该避免用户通过浏览器直接访问jsp页面。
最典型的MVC就是jsp+servlet+javabean模式:
每个控制器中可以定义多个请求URL,每个用户请求都发送给控制器,请求中的URL标识出对应的Action。Action代表了Web应用可以执行的一个操作。一个提供了Action的Java对象称为Action对象。一个Action类型可以支持多个Action(在Spring MVC以及Struts2中),或一个Action(在struts1中)。
注意:Struts1、Spring MVC和JavaServer Fces使用Servlet作为控制器,而Struts2使用Filter作为控制器。
Struts2框架:Struts2是基于MVC的轻量级的web应用框架。Struts2的应用范围是Web应用,注重将Web应用领域的日常工作和常见问题抽象化,提供一个平台帮助快速的完成Web应用开发。基于Struts2开发的Web应用自然就能实现MVC,Struts2着力于在MVC的各个部分为开发提供相应帮助。
下面通过代码来简单解释一下(这里只是简单使用):
Login.html(位于WebContent下)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <form id="form1" name="form1" action="/action/Login.action" method="post"> 登录<br> 用户名:<input name="username" type="text"><br> 密码:<input name="password" type="password"><br> <input type="submit" value="登录"> </form> </body> </html>
LoginAction.Java(位于包com.dc365.s2下)
if(username.equals("1") && password.equals("1")) { return "Success"; } return "Error";
struts.xml(位于src下)
<struts> <package name="default" namespcase="/action" extends="struts-default"> <action name="Login" class="com.dc365.s2.LoginAction"> <result name="Success">Success.jsp</result> <result name="Error">Error.jsp</result> </action> </package> </struts>
注意:除了上面代码,还需要在web.xml里面配置前端过滤器FilterDispatcher。
用户首先在Login.html中输入用户名和密码,点击登陆,用户请求(请求路径为/action/Login.action)首先到达前端控制器FilterDispatcher,FilterDispatcher根据用户请求的URL和配置在struts.xml找到对应的Login,然后根据对应的class的路径进入相应的login.Java,在这里判断之后,返回success或error,然后根据struts.xml中的result值,指向相应的jsp页面。
创建一个名为appdesign1的Dynamic Web Project项目,Servlet版本选择3.0,其功能设定为输入一个产品信息。具体为:
示例应用支持如下两个action(每个action对应一个URL):
示例应用由如下组件组成:
示例应用目录结构如下:
项目右键属性、部署路径设置如下:
Product类是一个封装了产品信息的JavaBean。Product类包含三个属性:name,description和price:
package appdesign1.model; import java.io.Serializable; import java.math.BigDecimal; public class Product implements Serializable { private static final long serialVersionUID = 748392348L; private String name; private String description; private BigDecimal price; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } }
Product类实现了java.io.Serializable接口,其实例可以安全地将数据保存到HttpSession中。根据Serializable的要求,Product类实现了一个serialVersionUID 属性。
表单类与HTML表单相对应,是后者在服务器的代表。ProductForm类看上去同Product类相似,这就引出一个问题:ProductForm类是否有存在的必要:
package appdesign1.form; public class ProductForm { private String name; private String description; private String price; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } }
实际上,表单对象会传递ServletRequest给其他组件,类似Validator(后面会介绍)。而ServletRequest是一个Servlet层的对象,不应当暴露给应用的其它层。
另一个原因是,当数据校验失败时,表单对象将用于保存和显示用户在原始表单上的输入。
注意:大部分情况下,一个表单类不需要事先Serializable接口,因为表单对象很少保存在HttpSession中。
ContrlooerServlet类继承自javax.servlet.http.HttpServlet类。其doGet()和doPost()方法最终调用process()方法,该方法是整个Servlet控制器的核心。
可能有人好奇,为何Servlet控制器命名为ControllerServlet,实际上,这里遵从了一个约定:所有Servlet的类名称都带有Servlet后缀。
package appdesign1.controller; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import appdesign1.action.SaveProductAction; import appdesign1.form.ProductForm; import appdesign1.model.Product; import java.math.BigDecimal; //Servlet3.0使用注解指定访问Servlet的URL @WebServlet(name = "ControllerServlet", urlPatterns = { "/input-product", "/save-product" }) public class ControllerServlet extends HttpServlet { private static final long serialVersionUID = 1579L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } private void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String uri = request.getRequestURI(); /* * uri is in this form: /contextName/resourceName, * for example: /appdesign1/input-product. * However, in the event of a default context, the * context name is empty, and uri has this form * /resourceName, e.g.: /input-product */ int lastIndex = uri.lastIndexOf("/"); String action = uri.substring(lastIndex + 1); // execute an action 根据不同的uri执行不同的action String dispatchUrl = null; if ("input-product".equals(action)) { // no action class, just forward dispatchUrl = "/jsp/ProductForm.jsp"; } else if ("save-product".equals(action)) { // create form 创建一个表单对象、保存表单信息 ProductForm productForm = new ProductForm(); // populate action properties productForm.setName(request.getParameter("name")); productForm.setDescription( request.getParameter("description")); productForm.setPrice(request.getParameter("price")); // create model 创建一个Model类 Product product = new Product(); product.setName(productForm.getName()); product.setDescription(productForm.getDescription()); try { product.setPrice(new BigDecimal(productForm.getPrice())); } catch (NumberFormatException e) { } // execute action method 保存表单 SaveProductAction saveProductAction = new SaveProductAction(); saveProductAction.save(product); // store model in a scope variable for the view request.setAttribute("product", product); dispatchUrl = "/jsp/ProductDetails.jsp"; } //请求转发 if (dispatchUrl != null) { RequestDispatcher rd = request.getRequestDispatcher(dispatchUrl); rd.forward(request, response); } } }
ControllerServlet的process()方法处理所有输入请求。首先是获取URI和action名称:
String uri = request.getRequestURI(); int lastIndex = uri.lastIndexOf("/"); String action = uri.substring(lastIndex + 1);
在本示例中,action值只会是input-product或sava-product。
接着,当action值为sava-product,process()方法执行如下步骤:
process()方法中判断action的if代码块如下:
if ("input-product".equals(action)) { // no action class, just forward dispatchUrl = "/jsp/ProductForm.jsp"; } else if ("save-product".equals(action)) { .... }
对于input-product,无需任何操作;而针对save-product,则创建一个ProductForm对象和Product对象,并将前者的属性值复制到后者。
再次,process()方法实例化SavaProductAction类,并调用其save()方法:
// create form 创建一个表单对象、保存表单信息 ProductForm productForm = new ProductForm(); // populate action properties productForm.setName(request.getParameter("name")); productForm.setDescription( request.getParameter("description")); productForm.setPrice(request.getParameter("price")); // create model 创建一个Model类 Product product = new Product(); product.setName(productForm.getName()); product.setDescription(productForm.getDescription()); try { product.setPrice(new BigDecimal(productForm.getPrice())); } catch (NumberFormatException e) { } // execute action method 保存表单 SaveProductAction saveProductAction = new SaveProductAction(); saveProductAction.save(product);
然后,将Product对象放入HttpServletRequest对象中,以便对应的视图可以访问到:
// store model in a scope variable for the view request.setAttribute("product", product); dispatchUrl = "/jsp/ProductDetails.jsp";
最后,process()方法转到视图,如果action是input-product,则转到ProductForm.jsp页面,否则转到ProductDetails.jsp页面:
//请求转发 if (dispatchUrl != null) { RequestDispatcher rd = request.getRequestDispatcher(dispatchUrl); rd.forward(request, response); }
这个应用这有一个action类,负责将一个product对象持久化,例如数据库。这个action类名为SaveProductAction:
package appdesign1.action; import appdesign1.model.Product; public class SaveProductAction { public void save(Product product) { // insert Product to the database } }
在这个示例中,SaveProductAction类的save()方法是一个空实现。
示例应用包含两个jsp页面。第一个页面ProductForm.jsp对应input-product操作,第二个页面ProductDetails.jsp对应sava-product操作。
ProductForm.jsp:
<!DOCTYPE html> <html> <head> <title>Add Product Form</title> <style type="text/css">@import url(css/main.css);</style> </head> <body> <form method="post" action="save-product"> <h1>Add Product <span>Please use this form to enter product details</span> </h1> <label> <span>Product Name :</span> <input id="name" type="text" name="name" placeholder="The complete product name"/> </label> <label> <span>Description :</span> <input id="description" type="text" name="description" placeholder="Product description"/> </label> <label> <span>Price :</span> <input id="price" name="price" type="number" step="any" placeholder="Product price in #.## format"/> </label> <label> <span> </span> <input type="submit"/> </label> </form> </body> </html>
注意:不要用HTML 标签来布局表单,使用CSS。
ProductDetails.jsp:
<!DOCTYPE html> <html> <head> <title>Save Product</title> <style type="text/css">@import url(css/main.css);</style> </head> <body> <div id="global"> <h4>The product has been saved.</h4> <p> <h5>Details:</h5> Product Name: ${product.name}<br/> Description: ${product.description}<br/> Price: $${product.price} </p> </div> </body> </html>
ProductForm页面包含了一个HTML表单。ProductDetails页面通过EL表达式语言访问HttpServletRequest所包含的product对象。
此外,该实例存在一个问题,即用户可以直接通过浏览器访问这两个jsp页面,我们可以通过以下方式避免这种直接访问:
main.css:
form { margin-left:auto; margin-right:auto; max-width: 450px; background: palegreen; padding: 25px 15px 25px 10px; border:1px solid #dedede; font: 12px Arial; } h1 { padding: 20px; display: block; border-bottom:1px solid grey; margin: -20px 0px 20px 0px; color: mediumpurple; } h1>span { display: block; font-size: 13px; } label { display: block; } label>span { float: left; width: 20%; text-align: right; margin: 14px; color: mediumpurple; font-weight:bold; } input[type="text"], input[type="number"] { border: 1px solid #dedede; height: 30px; width: 70%; font-size: 12px; border-radius: 3px; margin: 5px; } input[type="submit"] { background: mediumseagreen; font-weight: bold; border: none; padding: 8px 20px 8px 20px; color: black; border-radius: 5px; cursor: pointer; margin-left:4px; } input[type="submit"]:hover { background: red; color: yellow; }
将项目部署到tomcat服务器,然后启动服务器,假设示例应用运行在本机的8080端口上,则可以通过如下URL访问应用:
http://localhost:8008/appdesign1/input-product
完成表单输入后,表单提交到如下服务器URL上:
http://localhost:8008/appdesign1/save-product
参考文章
[1]MVC简介(部分转载)
[2]Spring MVC 学习总结(一)——MVC概要与环境配置(IDea与Eclipse示例)(推荐)
[3]Spring MVC 学习总结(三)——请求处理方法Action详解
[4]Spring MVC学习指南
标签:end ext XML art http bean 混合 proc ace
原文地址:https://www.cnblogs.com/zyly/p/10793201.html