什么是MVC架构模式?
- MVC(Model View Controller)架构模式不属于23种设计模式,应该称其为软件开发架构模式,它的出现可以帮助后端程序员从繁杂的后端开发中抽离出来,在不同的层面只需要关注特定的需求即可。
- 如果用jsp+servlet进行开发,那么业务逻辑的编写,数据库的连接以及CRUD操作,都需要在一个Servlet中完成,这样代码复用性太低,而且看起来十分繁杂。
MVC架构模式的出现就解决了这种情况,就如其字面意思,MVC将后端业务的开发大体上分为了三个部分:- Controller层,主要进行业务的调度和页面的展示。(可以看为司令员,servlet)
- View层,展示页面。(可以看为展示秘书:jsp,thymeleaf等)
- Model层,负责处理业务。(可以看为业务秘书:dao,bean,service等)
- MVC和三层架构的关系。在三层架构模式中,分为web层(表示层servlet和jsp或theymeleaf等),业务逻辑层(service)以及持久化层(DAO)。而在MVC中Controller是servlet;Model包括pojo(bean),dao,service;View层包括jsp,thymeleaf等。
MVC架构模式的工作过程(例如银行转账)
- 首先提供一个Account类,用来封装账户信息。这种简单的Java类被称为bean(也有叫做pojo,domain的)。
**
* 账户实体类:封装账户信息的
*/
public class Account { // 这种简单的对象被称为pojo对象。
// 一般属性不建议设计为基本数据类型,使用包装类,防止空带来的问题
/**
* 主键
*/
private Long id;
/**
* 账号
*/
private String actno;
/**
* 余额
*/
private Double balance;
public Account() {
}
public Account(Long id, String actno, Double balance) {
this.id = id;
this.actno = actno;
this.balance = balance;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
}
- 其次准备一个DAO(Data Access Object)数据库访问对象,用来连接数据库,执行增删改查(CRUD)操作,DAO只负责数据库表的增删改查,没有任何业务逻辑在里面,这样的对象被称为DAO对象。
public class AccountDao {
/**
* 负责插入数据
*
* @param act 账户信息
* @return 1 表示插入成功
*/
public int insert(Account act) {
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "insert into t_act(actno,balance) values(?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, act.getActno());
ps.setDouble(2, act.getBalance());
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, null);
}
return count;
}
/**
* 根据主键删除账户
*
* @param id
* @return
*/
public int deleteByID(Long id) {
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "delete from t_act where id=?";
ps = conn.prepareStatement(sql);
ps.setLong(1, id);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, null);
}
return count;
}
/**
* 更改账户信息
*
* @param act 账户
* @return
*/
public int update(Account act) {
Connection conn = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "update t_act set actno=?,balance=? where id=?";
ps = conn.prepareStatement(sql);
ps.setString(1, act.getActno());
ps.setDouble(2, act.getBalance());
ps.setLong(3, act.getId());
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, null);
}
return count;
}
/**
* 根据账号查询账户信息
*
* @param actno
* @return
*/
public Account selectByActno(String actno) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Account act = null;
try {
conn = DBUtil.getConnection();
String sql = "select actno,balance from t_act where actno=?";
ps = conn.prepareStatement(sql);
ps.setString(1, actno);
rs = ps.executeQuery();
if (rs.next()) {
Long id = rs.getLong("id");
double balance = rs.getDouble("balance");
act = new Account();
act.setId(id);
act.setActno(actno);
act.setBalance(balance);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, rs);
}
return act;
}
/**
* 查询所有用户信息
*
* @return
*/
public List<Account> selectAll() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<Account> accountList = null;
try {
conn = DBUtil.getConnection();
String sql = "select id,actno,balance from t_act";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
Long id = rs.getLong("id");
String actno = rs.getString("actno");
Double balance = rs.getDouble("balance");
Account act = new Account(id, actno, balance);
accountList.add(act);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, rs);
}
return accountList;
}
}
- 在Model层编写相应的业务逻辑,Model层就是专门处理业务的,在该类中编写纯业务代码。
/**
* 专门处理Account业务的一个类
* 在该类中编写纯业务代码
*/
public class AccountService {
// 这里的方法起名要表明处理什么业务
private AccountDao accountDao = new AccountDao();
/**
* 完成转账的业务逻辑
*
* @param fromActno 转出账户
* @param toAcrno 转入账户
* @param money 转账金额
*/
public void transfer(String fromActno, String toAcrno, double money) throws MoneyNotEnoughException, AppException {
// 查询余额是否充足
try (Connection connection = DBUtil.getConnection()){
// 开启事务
connection.setAutoCommit(false);
Account fromAccount = accountDao.selectByActno(fromActno);
Double fromAccountBalance = fromAccount.getBalance();
if (fromAccountBalance < money) {
throw new MoneyNotEnoughException("对不起,余额不足");
}
Account toAccount = accountDao.selectByActno(toAcrno);
Double toAccountBalance = toAccount.getBalance();
// 从转出账户中减去money
fromAccount.setBalance(fromAccountBalance - money);
int count = accountDao.update(fromAccount);
// 向转入账户中添加money
toAccount.setBalance(toAccountBalance + money);
count += accountDao.update(toAccount);
if (count!=2) {
throw new AppException("账户转账异常!");
}
// 提交事务
connection.commit();
} catch (SQLException e) {
throw new AppException("账户转账异常!");
}
}
}
工作过程:当用户发送过来一个请求,首先会在Controller层进行处理,当用户发送转账需求的时候,Controller层首先会进行处理,并调用Model层相应的业务方法(transfer),在Model层执行transfer的时候,会自动创建AccounDao对象执行相应的操作,最终将结果返给Controller,然后Controller层根据Model层里面传来的结果,调用View层进行展示。
/**
* 账户小程序
* AccountServlet是一个Controller
*
* @author 琨哥
* @version 2.0
* @since 2.0
*/
@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
private AccountService actService = new AccountService();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 接收数据,调用业务方法(Model)处理业务
String fromActno = request.getParameter("fromActno");
String toActno = request.getParameter("toActno");
double money = Double.parseDouble(request.getParameter("money"));
try {
actService.transfer(fromActno, toActno, money);
// 执行到这里说明转账成功,调用view层展示响应的代码
response.sendRedirect(request.getContextPath()+"/success.jsp");
} catch (MoneyNotEnoughException e) {
// 调用view层进行展示
response.sendRedirect(request.getContextPath() + "/MoneyNotEnough.jsp");
} catch (Exception e) {
response.sendRedirect(request.getContextPath() + "/error.jsp");
}
}
}
这样Controller负责业务的调度和页面的展示,Model层负责编写业务逻辑,view层负责编写前端页面,分工合作极大的提高了代码的复用性!