Model-View-Controller - 杂谈
经典的B/S结构+MVC模式:
注意:MVC层次均位于server中。
下面通过一个简单并且具体的例子来体会下这种MVC模式:
--- 功能:查询所有图书;涉及到数据库,JSP,Servlet等
Step 1: 创建数据库表用于存储book信息,插入测试数据(其实该步骤已经规划出javaBean里的各个属性了)
CREATE TABLE books ( bookid VARCHAR (10) NOT NULL, bookname VARCHAR (30), author VARCHAR (20), price FLOAT, publisher VARCHAR (20), PRIMARY KEY (bookid) ); INSERT INTO books VALUES('00001','Java','zhangsan',20,'电子工业'); INSERT INTO books VALUES('00002','JSP','lisi',22,'人民邮电'); INSERT INTO books VALUES('00003','Java EE','wang',30,'人民邮电');
Step 2:
整个查询过程可以规划为
浏览器借助jsp页面(View)发出请求-->Servlet拦截请求(Controller)-->调用业务逻辑方法(Controller)-->通过JDBC与数据库交互(Model)-->回到Servlet并设置jsp属性(View)-->查询结果展示(View)
下面一个个来说:
1) 浏览器借助jsp页面(View)发出请求
Navigator.jsp
<a href="${pageContext.request.contextPath}/findAllBooks">查看所有图书</a>
2) Servlet拦截请求(Controller)
web.xml
<servlet> <servlet-name>BookList</servlet-name> <servlet-class>com.chris.web.servlet.BookListServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>BookList</servlet-name> <url-pattern>/findAllBooks</url-pattern> </servlet-mapping>
3) 调用业务逻辑方法(Controller)
BookListServlet.java
public class BookListServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { BookQueryService bqs = new BookQueryService(); Set<Book> bookSet = bqs.findAllBooks(); req.setAttribute("allBooks",bookSet); RequestDispatcher rd = req.getRequestDispatcher("/jsp/books.jsp"); rd.forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
4) 通过JDBC与数据库交互(Model)
BookQueryService.java
public class BookQueryService { public Set<Book> findAllBooks() { Connection con = BasicJdbcOperation.getConnection(); String sql = "select * from books"; Set<Book> bookSet = new HashSet<Book>(); try { PreparedStatement ps = con.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while (rs.next()){ Book book = new Book(); book.setBookId(rs.getString(1)); book.setBookName(rs.getString(2)); book.setAuthor(rs.getString(3)); book.setPrice(rs.getFloat(4)); book.setPublisher(rs.getString(5)); bookSet.add(book); } } catch (SQLException e) { e.printStackTrace(); } finally { BasicJbdcOperation.close(); } return bookSet; } }
Book.java (JavaBean)
public class Book { private String bookId; private String bookName; private String author; private float price; private String publisher; public String getBookId() { return bookId; } public void setBookId(String bookId) { this.bookId = bookId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public String getPublisher() { return publisher; } public void setPublisher(String publisher) { this.publisher = publisher; } }
BasicJdbcOperation.java
public class BasicJdbcOperation { private static Connection con; public static Connection getConnection(){ Connection connection = null; try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/crud","root", ""); con = connection; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return connection; } public static void close() { try { if (con != null) { con.close(); } } catch (SQLException e) { e.printStackTrace(); } } public static void main(String[] args) { try { //加载驱动 Class.forName("com.mysql.jdbc.Driver"); //建立数据库连接 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/shiro","root", ""); //创建SQL语句 String sql = "select * from users where username=? and password=?"; //创建语句对象 PreparedStatement ps = connection.prepareStatement(sql); //变量赋值,替换? ps.setString(1, "Chris"); ps.setString(2, "123"); //执行SQL语句 ResultSet resultSet = ps.executeQuery(); //处理ResultSet while (resultSet.next()){ System.out.println(resultSet.getString(2)); } //关闭相关对象 resultSet.close(); ps.close(); connection.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } }
5) 回到Servlet并设置jsp属性(View)
回到3) 调用业务逻辑方法(Controller),
RequestDispatcher rd = req.getRequestDispatcher("/jsp/books.jsp");
rd.forward(req,resp);
==============MVC模式小结==============
对于任何Java Web应用,在完全确定需求之后,就需要考虑应用的整体架构。MVC模式提供了设计架构的良性思维方式,也符合项目通常的开发规则。
对于任何一个功能/应用,首先考虑用户怎么用?既然是Java Web应用,必然涉及浏览器,也就需要大量的页面支持,这也就构成了View部分。
那么大量页面之间如何发生关系,如何跳转,也就需要控制器(通常是Servlet),这也构成Controller部分。
页面有了,如何跳转也有了,但是页面里的数据呢?这个就涉及Model部分了,同数据库或者其他数据源交互。
View部分:
- 用户发送请求方式:超链接href;浏览器地址栏(get);表单提交(post);Ajax请求
- 页面展示:采用EL表达式+Jstl,比如输出提示信息${info};输出对象信息${book.bookid}。当前流行的方式还是采用javascript
Model部分:
- JavaBean层:包括entity和dto(可能也叫domain, model等),对应于数据库表实体和前端信息实体
- Service层:供Controller层调用的业务逻辑
- DAO层:数据库CRUD的封装
Controller部分:
通常采用Servlet实现,extends HttpServelt。按以下步骤实现一个控制器:
- 获取前端请求信息
- 调用Service,并与后台交互
- 保存服务器返回数据
- 转到响应界面
获取信息:
获取请求信息:
request.getParameter(paraname), request.getParameterValues(paraname)
获取session中信息:
session.getAttribute(属性名)
获取Cookie中信息:
request.getCookies()
获取请求头信息
获取Servlet的配置信息
保存信息:
保存到session中:
session.setAttribute(属性名,要保存的对象),供用户后续的所有访问过程使用。
保存到request中:
request.setAttribute(属性名,要保存的对象),供后续页面使用(页面与当前的Servlet属于同一次请求)
保存到cookie,Cookie cookie = new Cookie("name","value"); response.addCookie(cookie); 供客户端在以后的时间访问。
响应:
使用专门的文件进行响应:
方式一:
RequestDispatcher rd = request.getRequestDispatcher(目标文件);
rd.forward(request,response); // 完成跳转
方式二:
response.sendRedirect(目标文件);
注意:如果当前功能完成的是对数据库的插入操作,如果用户刷新就会产生错误。要避免错误,不能使用RequestDispatcher,可以使用方式二。如果使用方式二,就不能在当前文件和目标文件之间通过request共享信息了。如果要共享可以用session。使用session的时候,用完一定要删除这个对象session.removeAttribute(属性名)。