最近又捡起了对Java
的学习,通过对一个实例的介绍,总结下此次对Web
开发中MVC
三层架构的学习,以便用于日后的复习。
一、 MVC
简单的先介绍下MVC模式:
-
M(Model):
JavaBean
。用于完成具体的业务操作。JavaBean
:Java
中特殊的类.JavaBean
满足条件:public
修饰的类,并提供public
无参构造方法- 所有的属性都是
private
修饰 - 提供
getter
和setter
方法
使用层面:
- 封装业务逻辑:
dao
层封装对数据库的底层操作 - 封装数据:
domain
层。对数据库中所要查询对象的封装
-
V(View):视图。用于数据的展示。
- 页面的展示
- 与用户的交互
-
C(Controller):控制器。由Servlet实现控制器。
主要功能:
- 获取用户的输入
- 调用模型,将请求交给模型进行处理
- 将数据交给视图进行展示
首先浏览器(通过View页面)向服务器端进行请求(可以是表单请求、超链接、AJAX请求等),Controller
层获取浏览器请求的数据进行解析,调用模型;模型进行业务逻辑的操作,并将处理结果返回给Controller
层;Controller
层再将相应的数据交给View
层,进行数据展示到客户端。
二、三层架构
三层架构:视图层View、服务层Service、与持久层Dao。
View
:用于接收用户提交请求的代码。Service
:用于编写系统的业务逻辑。(最重要的一层)Dao
:对数据库进行最直接的操作。即:对数据库的增删改查。
dao层中,定义了对数据库的增删改查的接口。而service层中即对数据的具体业务操作,用于组合dao层中的接口方法。web层中则用于对用户数据的接收和发送。上图很好的解释了MVC与三层架构之间的关系。
三、案例
通过servlet
、jsp
、Mysql
、JDBCTempleat
、Duird
、BeanUtils
、Tomcat
等技术完成用户信息列表展示的实例。此部分着重解释后端代码的实现。
1.查询所有用户信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2QtbEHOm-1600526471558)(https://s1.ax1x.com/2020/08/16/dEGRSJ.png)]
当点击前端页面查询按钮时,此时通过对服务器端的请求,到web
层中的Controller
层,触发FindUserServlet
。由Controller
调用Service
层中的模型,继而Service
层通过dao
层获取全部的用户信息,封装到List<User>
集合中。返回给Service
层,再通过Service
层将用户信息返回给Controller
层。Web
层中的Controller
将数据进行存储转发给View
,进行数据的解析展示,返回给客户端。
findAll方法:
@Override
public List<User> findAll() {
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
String sql = "select * from user";
List<User> users = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
return users;
}
service层中findAll接口的实现
@Override
public List<User> findAll() {
UserDao userDao = new UserDaoImpl();
List<User> users = userDao.findAll();
return users;
}
web层中UserListServlet
@WebServlet("/userListServlet")
public class UserListServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
UserService userService = new UserServiceImpl();
List<User> users = userService.findAll();
request.setAttribute("users", users);
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
2.增加用户信息
当客户端点击提交按钮时,此次对服务器端的请求带着表单的数据。根据我们上面对三层架构以及Controller
层的介绍可知,由其进行数据的接收。这里即为AddUserServlet
对获取的数据进行处理、封装。将封装好的数据传给Service层(Model),即为UerService
,进行业务逻辑的操作。再通过dao
层对数据库进行相应的访问。
@Override
public void addUser(User user) {
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
String sql = "insert into user values(null,?,?,?,?,?,?,null,null)";
template.update(sql, user.getName(), user.getGender(), user.getAge(), user.getAddress(), user.getQq(), user.getEmail());
}
Service层中add接口的实现
@Override
public void addUser(User user) {
UserDao userDao = new UserDaoImpl();
userDao.addUser(user);
}
web层中addServlet的实现
@WebServlet("/addServlet")
public class AddServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
User user = new User();
Map<String, String[]> parameterMap = request.getParameterMap(); // 将请求参数进行Map集合的封装
try {
BeanUtils.populate(user, parameterMap);//将获取到的值封装到User对象中
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
UserService service = new UserServiceImpl();
service.addUser(user);
HttpSession session = request.getSession(); // 再进行重定向之前,将需要添加的User对象设置到session中,以便后续对其的获取使用
session.setAttribute("addUser", user);
response.sendRedirect(request.getContextPath() + "/findUserByPageServlet");//重定向至View层
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
思考:
在上一个方法查找所有的用户时,在最后,我们将获取到的数据传给view层,采用的是存储转发,而在这里,对用户的添加,我们却采用的是重定向(实际上,增、删、改都是采用的重定向)。这是为什么呢?
这里先对重定向和存储转发的几个特点进行简单的比较:
-
存储转发:一种在服务器内部的资源跳转方式。
特点:
- 浏览器地址栏不会发生变化
- 只能转发到当前服务器内部资源中
- 转发是一次请求
最重要的一点便是转发只在当前服务器内部进行,请求也只有一次。这样做带来的一点用处便是,request域中带有的请求是可以在多次资源跳转中进行共享的。在使用时,只需要添加资源路径即可。
-
重定向:具有多次的请求。其跳转在于客户端与服务器端之间,每次请求都是独立的,存在新的
request
与response
。特点:
- 重定向之后地址栏会发生变化
- 重定向可以访问其他站点(服务器)的资源
- 重定向是两次请求。故不能使用
request
域共享数据。
其中,重定向可避免在用户重新加载页面时,两次调用相同的动作。即,访问数据库时,增删改使用重定向。当前我的理解是:转发只有一次请求,故当重新加载页面时,会沿着之前的请求再请求一次(这句话可能比较绕),在页面进行展示全部用户数据之前,又会将数据库添加用户的操作进行一遍,所以会造成表单的重复提交;而重定向中是多次(两次)请求,所以只复用”最近一次“请求(因为每次请求是独立的,之前的请求都不在了),这里只是对页面进行全部用户的展示。
重定向:
存储转发:
3.用户信息更改
思路:用户信息的更改相对前面的用户显示的展示和增加要多了一些步骤。首先,在我们需要对信息进行更改时,需要将原信息展示给我们,在原信息的基础上进行修改,即:是查询到所要修改的用户,将其信息进行回显。其次,当我们对用户信息更改完毕以后,通过对数据库的访问,将对应的数据库信息进行Update操作。最后,通过view层,对列表信息进行展示传送给客户端。这里,每个用户都有一个唯一标识符–id主键,所以当我们拿到主键id时,其实也就获得了对应的数据库中的User对象,后面的操作也就顺理成章了。
FindUserServlet
– 查询需要修改的用户,用于信息的回显
@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String id = request.getParameter("id"); // 获取id
UserService service = new UserServiceImpl();
User user = service.findUser(Integer.parseInt(id));// 通过id返回User对象
request.setAttribute("user", user);// 查找操作所以存储-转发,将用户信息封装传至view层
request.getRequestDispatcher("/update.jsp").forward(request,response);// 即传至update.jsp页面进行解析
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
service层中findUser的实现
@Override
public User findUser(int id) {
UserDao userDao = new UserDaoImpl();
User user = userDao.findUser(id);
return user;
}
dao层中findUser接口的实现
@Override
public User findUser(int id) {
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
String sql = "select * from user where id = ?";
User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
return user;
}
以上是获取到需要修改的对象,然后对用户信息进行一个回显操作。
下面,便是对用户信息进行修改。
UpdateUserServlet
– 修改用户信息,对数据库数据进行修改
@WebServlet("/updateUserServlet")
public class UpdateUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
Map<String, String[]> parameterMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user, parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace(<