Java Spring MVC分层设计



Java Spring MVC分层设计

第一次尝试着用Java做Web开发,使用了Java Spring框架,顺便说一句,如果使用Spring开发,建议使用STS(Spring Tool Suite) IDE,它很好的集成了Spring、Maven等框架,使用起来特别方便,尤其是第一次使用Spring框架进行开发,它极大的方便了开发人员,通过快捷菜单及可很简单的配置好Spring开发环境,自动下载、更新Maven依赖包。话不多讲,回到文章的正题。

Spring是一个在Java业界很流行的MVC框架,所谓MVC即模型-视图-控制器,将应用程序的逻辑层与展现层进行分离的一种设计模式。

  • 模型(Model)代表数据控制器。数据的读取,插入,更新都是由模型来负责。
  • 视图(View)是展示给用户的最终页面。视图负责将数据以用户友好的形式展现出来。
  • 控制器(Controller)是模型,视图以及其他任何处理 HTTP 请求所必须的资源之前的中介

 

概述

一个典型的页面浏览行为在程序端的流程是这样的:

  1. 控制器最先被调用,并被赋予外部输入
  2. 控制器根据外部输入向模型请求数据
  3. 模型从数据库获取数据并发送数据到控制器
  4. 控制器处理该数据并发送封装好的数据到视图
  5. 视图根据接到的数据最终展示页面给用户浏览

使用Java进行MVC模式开发时,往往将数据模型分为两部分,即DAO(Data Access Object,数据访问对象)和Service(业务逻辑模型)。在第2步中,控制器向模型请求数据时,并不是直接向DAO请求数据,而是通过Service向DAO请求数据。这样做的好处是,可以将业务逻辑与数据库访问独立开,为将来系统更换数据保存介质(如目前系统使用文件系统存储数据,将来可以更换为使用数据库存储,又或者是现在使用了MSSQL存储数据,将来更换为Oracle或是Mysql等)提供了很大的灵活性。

下图给出了分层设计模型。控制器只需要调用Service接口中的方法获取或是处理数据,Service层对控制器传入的数据进行业务逻辑处理封装后,传给DAO层,由DAO层负责将处理后的数据写入数据库中。

在Service层使用了抽象工厂模式来实现Service层与DAO层的低耦合,Service层并不知道DAO层是如何实现的,实际上也不需要知道系统使用了哪种数据库或是文件系统。

在DAO层使用工厂模式来创建数据模型的实体对象。

 

Service层设计

接口代码,这里使用了泛型技巧,确保每个Service只处理一种数据类型。

 

[java] view plain copy

  1. package com.emerson.etao.service;
  2. import java.sql.SQLException;
  3. import java.util.List;
  4. /**
  5.  * 业务实现层接口
  6.  *
  7.  * @author Chris Mao(Zibing)
  8.  *
  9.  * @param <T>
  10.  */
  11. public interface IBaseService<T> {
  12.     /**
  13.      * 将实体类对象持久化,写入到数据表中
  14.      *
  15.      * @param T
  16.      * @return 返回新写入记录的自增ID
  17.      * @throws SQLException
  18.      */
  19.     public long insert(T entity);
  20.     /**
  21.      * 根据Id值,将实体类数据回写到数据库
  22.      *
  23.      * @param id
  24.      * @param T
  25.      * @return 返回更新的记录笔数
  26.      * @throws SQLException
  27.      */
  28.     public int update(long id, T entity);
  29.     /**
  30.      * 根据Id值从数据库中删除实体类数据
  31.      *
  32.      * @param id
  33.      * @return 返回删除的记录笔数
  34.      * @throws SQLException
  35.      */
  36.     public int delete(long id);
  37.     /**
  38.      * 根据Id查询具体的实体类信息,并返回实体类对象
  39.      * 若查询不到数据则返回null
  40.      *
  41.      * @param id
  42.      * @return T
  43.      */
  44.     public T getById(long id);
  45.     /**
  46.      * 获取列表
  47.      *
  48.      * @return List
  49.      * @throws SQLException
  50.      */
  51.     public List<T> getAll();
  52. }

下面是具体的Service层接口代码,

 

 

[java] view plain copy

  1. package com.emerson.etao.service.base;
  2. import java.util.List;
  3. import com.emerson.etao.entity.base.BusinessApp;
  4. import com.emerson.etao.entity.base.Communicator;
  5. import com.emerson.etao.entity.base.Customer;
  6. import com.emerson.etao.service.IBaseService;
  7. /**
  8.  * 客服类操作接口
  9.  *
  10.  * @author Chris Mao(Zibing)
  11.  * @param <T>
  12.  *
  13.  * @param <T>
  14.  */
  15. public interface ICommunicatorService extends IBaseService<Communicator> {
  16.     public List<Communicator> getAll(Customer customer);
  17.     /**
  18.      * 为客服分配商业应用
  19.      *
  20.      * @param c
  21.      * @param appList
  22.      * @see BusinessApp
  23.      */
  24.     public void assignBusinessApp(Communicator communicator, List<BusinessApp> appList, boolean deleteExists);
  25.     /**
  26.      * 为客服分配客户
  27.      *
  28.      * @param c
  29.      * @param customerList
  30.      * @see Customer
  31.      */
  32.     public void assingCustomer(Communicator communicator, List<Customer> customerList, boolean deleteExists);
  33. }

实现接口。

 

 

[java] view plain copy

  1. /**
  2.  *
  3.  */
  4. package com.emerson.etao.service.imp;
  5. import java.sql.SQLException;
  6. import java.util.List;
  7. import com.emerson.etao.dao.IBaseDao;
  8. import com.emerson.etao.dao.IDaoFactory;
  9. import com.emerson.etao.service.IBaseService;
  10. /**
  11.  * 业务层实现类基类
  12.  *
  13.  * 为了降低与数据访问层的耦合,在构造函数中传入DaoFactory接口用于创建具体的数据访问对象实例
  14.  *
  15.  * @author Chris Mao(Zibing)
  16.  *
  17.  */
  18. public abstract class BaseServiceImp<T> implements IBaseService<T> {
  19.     private IBaseDao<T> dao = null;
  20.     protected IBaseDao<T> getDao() {
  21.         return this.dao;
  22.     }
  23.     /**
  24.      *
  25.      * @param factory 降低耦合,传入DaoFactory接口
  26.      * @see IDaoFactory
  27.      */
  28.     public BaseServiceImp(IDaoFactory<T> factory) {
  29.         super();
  30.         this.dao = factory.getDao();
  31.     }
  32.     @Override
  33.     public long insert(T entity) {
  34.         try {
  35.             return this.getDao().insert(entity);
  36.         } catch (SQLException e) {
  37.             e.printStackTrace();
  38.         }
  39.         return 0;
  40.     }
  41.     @Override
  42.     public int update(long id, T entity) {
  43.         try {
  44.             return this.getDao().update(id, entity);
  45.         } catch (SQLException e) {
  46.             e.printStackTrace();
  47.         }
  48.         return 0;
  49.     }
  50.     @Override
  51.     public int delete(long id) {
  52.         try {
  53.             return this.getDao().delete(id);
  54.         } catch (SQLException e) {
  55.             e.printStackTrace();
  56.         }
  57.         return 0;
  58.     }
  59.     @Override
  60.     public List<T> getAll() {
  61.         try {
  62.             return this.getDao().getAll();
  63.         } catch (SQLException e) {
  64.             e.printStackTrace();
  65.         }
  66.         return null;
  67.     }
  68.     @Override
  69.     public T getById(long id) {
  70.         try {
  71.             return this.getDao().getById(id);
  72.         } catch (SQLException e1) {
  73.             e1.printStackTrace();
  74.         }
  75.         return null;
  76.     }
  77. }

 

 

[java] view plain copy

  1. package com.emerson.etao.service.base.imp;
  2. import java.sql.PreparedStatement;
  3. import java.sql.SQLException;
  4. import java.text.SimpleDateFormat;
  5. import java.util.ArrayList;
  6. import java.util.Date;
  7. import java.util.Iterator;
  8. import java.util.List;
  9. import com.emerson.etao.dao.IDaoFactory;
  10. import com.emerson.etao.entity.base.BusinessApp;
  11. import com.emerson.etao.entity.base.Communicator;
  12. import com.emerson.etao.entity.base.Customer;
  13. import com.emerson.etao.service.base.ICommunicatorService;
  14. import com.emerson.etao.service.imp.BaseServiceImp;
  15. /**
  16.  *
  17.  * @author Chris Mao(Zibing)
  18.  *
  19.  */
  20. public class CommunicatorServiceImp extends BaseServiceImp<Communicator>implements ICommunicatorService {
  21.     @Override
  22.     public List<Communicator> getAll(Customer customer) {
  23.         List<Communicator> result = new ArrayList<Communicator>();
  24.         try {
  25.             result = this.getDao()
  26.                     .getAll(“SELECT a.* FROM communicator AS a INNER JOIN customer_communicator cc USING(communicator_id) WHERE cc.customer_id = ”
  27.                             + customer.getCustomerId());
  28.         } catch (SQLException e) {
  29.             e.printStackTrace();
  30.         }
  31.         return result;
  32.     }
  33.     public CommunicatorServiceImp(IDaoFactory<Communicator> factory) {
  34.         super(factory);
  35.     }
  36.     @Override
  37.     public void assignBusinessApp(Communicator communicator, List<BusinessApp> appList, boolean deleteExists) {
  38.         try {
  39.             if (true == deleteExists) {
  40.                 this.getDao().getStatement().executeUpdate(“DELETE FROM communicator_application WHERE communicator_id = ” + communicator.getCommunicatorId());
  41.             }
  42.             if (null == appList || appList.isEmpty()) {
  43.                 return;
  44.             }
  45.              PreparedStatement pstmt = this.getDao().getConnection().prepareStatement(“INSERT IGNORE INTO communicator_application(communicator_id, application_id, created_time) VALUES(?, ?, ?)”);
  46.              BusinessApp app = null;
  47.              SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);// 设置日期格式
  48.              Iterator<BusinessApp> ite = appList.iterator();
  49.              while (ite.hasNext()) {
  50.              app = ite.next();
  51.              pstmt.setLong(1, communicator.getCommunicatorId());
  52.              pstmt.setLong(2, app.getApplicationId());
  53.              pstmt.setString(3, sdf.format(new Date()));
  54.              pstmt.executeUpdate();
  55.              }
  56.              pstmt.close();
  57.         } catch (SQLException e) {
  58.             e.printStackTrace();
  59.         }
  60.     }
  61.     /**
  62.      * 为客服人员分配客户
  63.      *
  64.      * 如果需要删除客服人员名下所有客户,只需将customers设为null或是空列表
  65.      *
  66.      * @param communicator
  67.      * @param apps
  68.      * @param deleteExists
  69.      */
  70.     @Override
  71.     public void assingCustomer(Communicator communicator, List<Customer> customerList, boolean deleteExists) {
  72.         try {
  73.             if (true == deleteExists) {
  74.                 this.getDao().getStatement().executeQuery(“DELETE FROM customer_communicator WHERE communicator_id = ” + communicator.getCommunicatorId());
  75.             }
  76.             if (null == customerList || customerList.isEmpty()) {
  77.                 return;
  78.             }
  79.              PreparedStatement pstmt = this.getDao().getConnection().prepareStatement(“INSERT IGNORE INTO customer_communicator(communicator_id, customer_id, created_time) VALUES(?, ?, ?)”);
  80.              Customer customer = null;
  81.              SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);// 设置日期格式
  82.              Iterator<Customer> ite = customerList.iterator();
  83.              while (ite.hasNext()) {
  84.              customer = ite.next();
  85.              pstmt.setLong(1, communicator.getCommunicatorId());
  86.              pstmt.setLong(2, customer.getCustomerId());
  87.              pstmt.setString(3, sdf.format(new Date()));
  88.              pstmt.executeUpdate();
  89.              }
  90.              pstmt.close();
  91.         } catch (SQLException e) {
  92.             e.printStackTrace();
  93.         }
  94.     }
  95. }

 

DAO层的设计

 


这里需在为DAO层定义一个通个的基础接口IBaseDao,这里包含了对数据的增、删、改、查基础操作。抽象类BaseDao实现接口IBaseDao,并添加了访问限制为protected的数据库连接对象,方便子类使用。

 

 

 

DAO接口代码。

 

[java] view plain copy

  1. /**
  2.  *
  3.  */
  4. package com.emerson.etao.dao;
  5. import java.sql.Connection;
  6. import java.sql.SQLException;
  7. import java.sql.Statement;
  8. import java.util.List;
  9. /**
  10.  *
  11.  * 数据访问层接口
  12.  *
  13.  * @author Chris Mao(Zibing)
  14.  *
  15.  */
  16. public interface IBaseDao<T> {
  17.     /**
  18.      *
  19.      * @return Connection
  20.      */
  21.     public Connection getConnection();
  22.     /**
  23.      *
  24.      * @return Statement
  25.      */
  26.     public Statement getStatement();
  27.     /**
  28.      * 将值对象写入到数据表中,并返回其自增ID值
  29.      *
  30.      * @param entity
  31.      * @return 返回新写入记录的自增ID
  32.      * @throws SQLException
  33.      */
  34.     public long insert(T entity) throws SQLException;
  35.     /**
  36.      * 将值对象修改后的内容写入到数据表中,并返回其影响的记录笔数
  37.      *
  38.      * @param id
  39.      * @param entity
  40.      * @return 返回更新的记录笔数
  41.      * @throws SQLException
  42.      */
  43.     public int update(long id, T entity) throws SQLException;
  44.     /**
  45.      * 删除ID值,并返回其删除的记录笔数
  46.      *
  47.      * @param id
  48.      * @return 返回删除的记录笔数
  49.      * @throws SQLException
  50.      */
  51.     public int delete(long id) throws SQLException;
  52.     /**
  53.      * 依据Id值到数据表中查询数据,并返回值对象
  54.      *
  55.      * @param id
  56.      * @return
  57.      */
  58.     public T getById(long id) throws SQLException;
  59.     /**
  60.      * 返回数据表中所有记录
  61.      *
  62.      * @return List<T>
  63.      * @throws SQLException
  64.      */
  65.     public List<T> getAll() throws SQLException;
  66.     /**
  67.      * 返回符合条件的所有记录
  68.      *
  69.      * @param queryStr
  70.      * @return List<T>
  71.      * @throws SQLException
  72.      */
  73.     public List<T> getAll(String queryStr) throws SQLException;
  74. }

 

 

抽象工厂接口。

 

[java] view plain copy

  1. package com.emerson.etao.dao;
  2. /**
  3.  * 数据访问类工厂接口
  4.  *
  5.  * 负责创建具体的数据访问对象实例
  6.  *
  7.  * @author Chris Mao(Zibing)
  8.  *
  9.  * @param <T>
  10.  */
  11. public interface IDaoFactory<T> {
  12.     /**
  13.      * 创建数据访问对象实例
  14.      *
  15.      * @return
  16.      * @see IBaseDao
  17.      */
  18.     public  IBaseDao<T> getDao();
  19. }

 

抽象类BaseDao实现接口IBaseDao。

 

[java] view plain copy

  1. package com.emerson.etao.dao;
  2. import java.sql.Connection;
  3. import java.sql.ResultSet;
  4. import java.sql.SQLException;
  5. import java.sql.Statement;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import com.emerson.etao.db.DBUtils;
  9. /**
  10.  *
  11.  * 数据访问层基类
  12.  *
  13.  * 所有数据访问对象都需要继承此类
  14.  *
  15.  * @author Chris Mao(Zibing)
  16.  *
  17.  */
  18. public abstract class BaseDao<T> implements IBaseDao<T> {
  19.     private static final Logger logger = LoggerFactory.getLogger(BaseDao.class);
  20.     public Connection getConnection() {
  21.         return DBUtils.getConnection();
  22.     }
  23.     public Statement getStatement() {
  24.         Statement stmt = null;
  25.         try {
  26.             Connection conn = DBUtils.getConnection();
  27.             stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
  28.         } catch (SQLException e) {
  29.             logger.error(“创建 Statement 对象发生错误!!”);
  30.             e.printStackTrace();
  31.         }
  32.         return stmt;
  33.     }
  34. }

 

 

实体类创建工厂接口。

 

[java] view plain copy

  1. package com.emerson.etao.entity;
  2. import java.sql.ResultSet;
  3. /**
  4.  *
  5.  * 实体类工厂接口
  6.  *
  7.  * 所有实体类对象实例需要通过此工厂接口创建
  8.  *
  9.  * @author Chris Mao(Zibing)
  10.  *
  11.  */
  12. public interface IEntityFactory<T> {
  13.     /**
  14.      * 创建空的实体类
  15.      *
  16.      * @return
  17.      */
  18.     public T createEntity();
  19.     /**
  20.      * 创建实体类,并将参数rs中的内容赋值到实体类属性当中
  21.      *
  22.      * @param rs
  23.      * @return
  24.      */
  25.     public T createEntity(ResultSet rs);
  26. }

 

具体的DAO对象,继承BaseDao,并实现实体类创建工厂接口。这里使用了内部匿名类实现DAO的抽象工厂接口。

 

[java] view plain copy

  1. package com.emerson.etao.dao.base;
  2. import java.sql.PreparedStatement;
  3. import java.sql.ResultSet;
  4. import java.sql.SQLException;
  5. import java.sql.Statement;
  6. import java.text.SimpleDateFormat;
  7. import java.util.ArrayList;
  8. import java.util.Date;
  9. import java.util.List;
  10. import com.emerson.etao.dao.BaseDao;
  11. import com.emerson.etao.dao.IBaseDao;
  12. import com.emerson.etao.dao.IDaoFactory;
  13. import com.emerson.etao.entity.IEntityFactory;
  14. import com.emerson.etao.entity.base.Communicator;
  15. /**
  16.  * 客服人员数据访问对象
  17.  *
  18.  * @author Chris Mao(Zibing)
  19.  *
  20.  */
  21. public class CommunicatorDao extends BaseDao<Communicator>implements IEntityFactory<Communicator> {
  22.     public static IDaoFactory<Communicator> factory = new IDaoFactory<Communicator>() {
  23.         @Override
  24.         public IBaseDao<Communicator> getDao() {
  25.             return new CommunicatorDao();
  26.         }
  27.     };
  28.     @Override
  29.     public Communicator createEntity() {
  30.         return new Communicator();
  31.     }
  32.     @Override
  33.     public Communicator createEntity(ResultSet rs) {
  34.         Communicator c = this.createEntity();
  35.         try {
  36.             c.setCommunicatorId(rs.getInt(“communicator_id”));
  37.             c.setCommunicatorName(rs.getString(“communicator_name”));
  38.             c.setPhone(rs.getString(“phone”));
  39.             c.setFax(rs.getString(“fax”));
  40.             c.setEmail(rs.getString(“email”));
  41.             c.setReportTo(rs.getInt(“report_to”));
  42.             c.setReportToName(rs.getString(“report_to_name”));
  43.             c.setValid(rs.getByte(“is_valid”));
  44.             c.setCreatedTime(rs.getTimestamp(“created_time”));
  45.             c.setUpdatedTime(rs.getTimestamp(“update_time”));
  46.         } catch (SQLException e) {
  47.             e.printStackTrace();
  48.         }
  49.         return c;
  50.     }
  51.     @Override
  52.     public Communicator getById(long id) throws SQLException {
  53.         Communicator result = null;
  54.         ResultSet rs = this.getStatement().executeQuery(“SELECT * FROM vw_communicator WHERE communicator_id = ” + id);
  55.         while (rs.next()) {
  56.             result = this.createEntity(rs);
  57.         }
  58.         rs.close();
  59.         return result;
  60.     }
  61.     @Override
  62.     public long insert(Communicator entity) throws SQLException {
  63.         Long newId = (long) 0;
  64.         StringBuilder sql = new StringBuilder();
  65.         sql.append(“INSERT IGNORE INTO communicator”);
  66.         sql.append(“(communicator_name, phone, fax, email, report_to, created_time) ”);
  67.         sql.append(“VALUES(?, ? ,? ,?, ?, ?)”);
  68.         PreparedStatement pstmt = this.getConnection().prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
  69.         pstmt.setString(1, entity.getCommunicatorName());
  70.         pstmt.setString(2, entity.getPhone());
  71.         pstmt.setString(3, entity.getFax());
  72.         pstmt.setString(3, entity.getFax());
  73.         pstmt.setString(4, entity.getEmail());
  74.         pstmt.setInt(5, entity.getReportTo());
  75.         SimpleDateFormat df = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);// 设置日期格式
  76.         pstmt.setString(6, df.format(new Date()));
  77.         pstmt.executeUpdate();
  78.         ResultSet rs = pstmt.getGeneratedKeys();
  79.         if (rs.next()) {
  80.             newId = rs.getLong(1);
  81.             entity.setCommunicatorId(rs.getInt(1));
  82.             // System.out.println(“新增客服记录ID为:” + newId);
  83.         }
  84.         rs.close();
  85.         pstmt.close();
  86.         return newId;
  87.     }
  88.     @Override
  89.     public int update(long id, Communicator entiry) throws SQLException {
  90.         int result = 0;
  91.         StringBuffer sql = new StringBuffer();
  92.         Communicator c = (Communicator) entiry;
  93.         // System.out.println(c);
  94.         sql.append(“UPDATE communicator”);
  95.         sql.append(“ SET communicator_name = ?, phone = ?, fax = ?, email = ?, report_to = ?, is_valid = ?”);
  96.         sql.append(“ WHERE communicator_id = ?”);
  97.         PreparedStatement pstmt = this.getConnection().prepareStatement(sql.toString());
  98.         pstmt.setString(1, c.getCommunicatorName());
  99.         pstmt.setString(2, c.getPhone());
  100.         pstmt.setString(3, c.getFax());
  101.         pstmt.setString(3, c.getFax());
  102.         pstmt.setString(4, c.getEmail());
  103.         pstmt.setInt(5, c.getReportTo());
  104.         pstmt.setInt(6, c.getIsValid());
  105.         pstmt.setLong(7, c.getCommunicatorId());
  106.         result = pstmt.executeUpdate();
  107.         // System.out.println(“更新客服记录数为:” + result);
  108.         pstmt.close();
  109.         return result;
  110.     }
  111.     @Override
  112.     public int delete(long id) throws SQLException {
  113.         int result = 0;
  114.         String sql = ”DELETE FROM communicator WHERE communicator_id = ?”;
  115.         PreparedStatement pstmt;
  116.         pstmt = this.getConnection().prepareStatement(sql);
  117.         pstmt.setLong(1, id);
  118.         result = pstmt.executeUpdate();
  119.         // System.out.println(“删除客服记录数为:” + result);
  120.         pstmt.close();
  121.         return result;
  122.     }
  123.     @Override
  124.     public List<Communicator> getAll() throws SQLException {
  125.         List<Communicator> result = null;
  126.         ResultSet rs = this.getStatement().executeQuery(“SELECT * FROM vw_communicator”);
  127.         result = new ArrayList<Communicator>();
  128.         while (rs.next()) {
  129.             result.add(this.createEntity(rs));
  130.         }
  131.         rs.close();
  132.         return result;
  133.     }
  134.     @Override
  135.     public List<Communicator> getAll(String queryStr) throws SQLException {
  136.         List<Communicator> result = new ArrayList<Communicator>();
  137.         ResultSet rs = this.getStatement().executeQuery(queryStr);
  138.         while (rs.next()) {
  139.             result.add(this.createEntity(rs));
  140.         }
  141.         rs.close();
  142.         return result;
  143.     }
  144. }