定义
Model-View-Controller(MVC)是一种软件架构模式,是软件设计模式的体现,用于组织代码并关注分离点,广泛应用于用户界面的开发中。该模式将相关程序逻划分为三个相互关联的组成部分: 模式、视图和控制器。MVC模式最初用于桌面图形用户界面(Graph User Interface,GUI),但后来因设计Web应用程序而变得流行。许多流行的编程语言都有MVC框架,这些框架促进了MVC模式的实现。
MVC分为三个主要组件: **模型(Model)、视图(View)和控制器(Controller)**。三个层次相互独立,它们之间通过一定的协议进行通信,以达到更好的松耦合效果。这对大型项目来说特别重要,可以方便地对各个模块进行单独的开发和测试,同时更容易管理和扩展。以下是对MVC框架的主要组件的简要介绍:
模型层(M,Model)
定义:模型层负责数据的处理与存储,包括业务逻辑层的实现。
作用:处理数据,如数据库查询、数据验证等。
示例:在JAVA中,模型通常是由POJO(Plain Old Java Object)类和DAO(Data Access Object)类组成。
视图层(V,View)
定义:视图层负责展示数据给用户,是用户界面的部分。
作用:接收用户的输入,并显示从模型层获取的数据。
示例:在web应用中,视图可以是JSP页面、HTML页面或Thymeleaf模板。
控制器层(C,Controller)
定义:控制器层负责从用户端接收各种类型的请求,包括HTTP请求、AJAX请求等,然后根据请求的特征和内容进行路由和处理。它可能会根据请求的类型选择不同的处理逻辑,以满足不同类型请求的处理需求。
作用:核心大脑,决定了如何处理用户的请求和数据。
示例:在Spring框架中,控制器通常是带有@Controller注解的类。
MVC的优缺点
优点
1.分离关注点:将业务逻辑、数据访问和用户界面分离,使得每个部分可以独立开发和测试。
2.提高可维护性:代码结构清晰,易于理解和维护。
3.提供可扩展性:可以轻松地添加新的功能或修改现有功能,而不会影响其他部分。
4.并行开发:MVC的分离允许团队中的开发人员并行工作,例如后端开发人员可以专注于模型和控制器,而前端开发人员专注于视图的实现。
缺点
1.增加了复杂性:对于简单的应用程序,引入MVC会增加不必要的复杂性,可能并不适合小规模项目。
2.学习曲线陡峭:MVC框架本身概念较为复杂,尤其对于新手开发者来说,理解并掌握这种模式可能需要一些时间。
3.视图和控制器之间的紧密耦合:虽然模型与视图解耦,但在某些实现中,控制器和视图的耦合性仍然较高,影响了代码的灵活性和可测试性。
4.性能问题:由于MVC框架将逻辑分为多个层次,导致数据在不同层次之间频繁传递,可能会导致一定的性能开销,特别是在高并发或需要即时响应的应用场景中。
SpringBoot中的MVC
模型层(M,Model)
模型层一般有Bean类/POJO类,Dao类和Util类
1 Util类
Util类通常用于提供一组常用且不复杂的帮助函数,它不属于业务逻辑模块,而是用于支撑整个应用系统的一些通用操作和工具函数的集合。
Util类的作用主要有以下方面:
(1)提供通用函数库: 比如字符串转换、时间格式转换、数字格式转换等。这类函数非常通用且常用,可以在整个应用系统中得到重复利用。
(2)提高程序复用度:Util类中的一些函数一般都是通用性非常高的,比如字符串处理函数、时间转换函数、加密解密函数等,这些函数可以被多个模块和程序重复使用,从而可以提高开发的效率和程序的复用度。
(3)简化代码编写:Util类可以封装一些常用的代码块,使得开发人员可以直接使用,从而减少代码编写的时间和工作量。
(4)提高程序性能:Util类中的函数有可能会进行一些性能优化,使得整个应用程序得以提高。同时,Util类可以减少代码中的重复计算,从而提高程序的效率和性能。
Util类可以提供一些通用性的函数、工具方法,封装一些常用的逻辑代码块,以提高程序复用度、简化代码编写、提高程序性能等,是一种非常实用和通用的开发工具类。
一般我们会将数据库连接的部分写在这个类里。
Util例子
package com.csc_filemanager.dev.utils;
import java.util.HashMap;
import java.util.Map;
//统一返回结果类
public class Result {
private Boolean success;
private Integer code;
private String message;
private Map<String,Object> data = new HashMap<>();
public Boolean getSuccess(){return success;}
public void setSuccess(Boolean success){this.success=success;}
public Integer getCode() {return code;}
public void setCode(Integer code){this.code=code;}
public String getMessage(){return message;}
public void setMessage(String message){this.message=message;}
public Map<String,Object> getData(){return data;}
public void setData(Map<String,Object> data){this.data=data;}
private Result(){}
//成功静态方法
public static Result ok(){
Result r = new Result();
r.setSuccess(true);
r.setCode(ResultCode.SUCCESS);
r.setMessage("成功");
return r;
}
//失败静态方法
public static Result error(){
Result r= new Result();
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
r.setMessage("失败");
return r;
}
public static Result error(String message){
Result r = new Result();
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
r.setMessage(message);
return r;
}
public static Result success(String message){
Result r = new Result();
r.setSuccess(false);
r.setCode(ResultCode.SUCCESS);
r.setMessage(message);
return r;
}
public Result success(Boolean success){
this.setSuccess(success);
return this;
}
public Result message(String message){
this.setMessage(message);
return this;
}
public Result code(Integer code){
this.setCode(code);
return this;
}
public Result data(String key, Object value){
this.data.put(key,value);
return this;
}
public Result data(Map<String,Object> map){
this.setData(map);
return this;
}
}2 Dao类/Mapper类
Dao(Data Access Object)类/Mapper类是用于访问数据库的对象,它通常负责封装和实现与数据库相关的操作,并且提供了对数据的CRUD操作。
Dao类/Mapper类的主要作用是将业务逻辑层与数据访问层进行分离,使得业务逻辑层专注于处理业务逻辑,而数据访问层专注于数据的存取,以提高代码的可维护性和可重用性。
DAO类/Mapper类的核心作用包括以下几个方面:
1.封装数据存储层:Dao类/Mapper类隐藏了数据库的底层实现细节,使得业务逻辑层无需关注数据的存储细节,从而简化了业务逻辑层的编写。
2.提供数据访问接口:Dao类//Mapper类Mapper类提供了对数据的CRUD操作接口,为业务逻辑层提供数据访问借口。
3.实现数据访问逻辑:Dao类实现了数据的实际逻辑,包括数据的新增、删除、修改和查询等操作。
4.支持多种数据源:Dao类/Mapper类可以支持多种数据源,包括关系型数据库、NoSQL数据库、文件系统等,使得应用程序可以更加灵活地处理多种数据和数据源场景。
5.简化数据库访问操作:Dao类/Mapper类可以对数据库访问操作进行封装,简化对数据库的访问操作,从而提高代码的可维护性和可重用性。
Dao类/Mapper类是负责封装数据存储层,提供数据访问接口,并实现数据访问逻辑的对象。它的主要作用是将业务逻辑层与数据访问层进行分离,帮助开发人员更加专注于业务逻辑的处理,提高代码的可维护性和可重用性,是实现数据访问及数据处理能力的关键所在。
一般我们会将CRUD增删改查方法放在这个类里。
Mapper例子
package com.csc_filemanager.dev.mapper;
import com.csc_filemanager.dev.pojo.Filee;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface FileMapper {
//查询所有文件
@Select("select * from files where File_id= #{File_id}")
Filee findfile(String File_id);
//查询某人发布的所有文件
@Select("select * from files where File_publisher=#{File_publisher}")
List<Filee> findfilesbypublisher(String File_publisher);
//查询时间范围内文件
@Select("select * from files where File_uptime between #{startt} and #{endt}")
List<Filee> findfilesbyuptime(String startt,String endt);
//上传文件
@Insert("insert into files values (#{File_id},#{File_type},#{File_uptime},#{File_retime},#{File_useto},#{File_address},#{File_name},#{File_author},#{File_authority},#{File_topping},#{File_publisher})")
int addfile(Filee filee);
//删除文件
@Delete("delete from files where File_id=#{File_id}")
int deletefile(String File_id);
}3 Bean类/POJO类
Bean类/POJO类是指一种轻量级的JAVA对象,其主要作用是封装业务逻辑和数据,以便可以在不同的程序模块中复用。
Bean类/POJO类通常有以下特点:
1.具有私有的属性和公有的Getter/Setter方法,用于控制对属性的访问和修改。
2.具有无参的构造方法,以便可以通过JAVA反射机制构造对象。
3.Bean类/POJO类可以实现”java.io.Serializable’接口,以便在网络传输和存储过程中将对象数据序列化和反序列化。
在Java Web应用程序中,Bean类通常用于封装表单数据、数据库查询结果等,以便于在应用程序的各个部分之间进行数据传递和共享。例如,当用户提交一个表单时,表单数据可以通过Bean类对象进行封装,然后在应用程序的各个模块中进行操作和处理。
举例来说,考虑一个简单的用户登录页面,用户需要输入用户名和密码,然后点击”登录”按钮进行验证。在这种情况下,可以创建一个名为’UserBean’的JavaBean类,封装这两个字段的数据。然后,您可以在登录页面中使用’UserBean’作为’form’元素的数据模型,将用户的输入数据传递到后台的Servelt或Controller中进行验证和处理。这样,就可以避免在servelt或controller中分别处理每个表单字段,而可以将它们封装为一个对象。
POJO例子
package com.csc_filemanager.dev.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Filee {
private String File_id;//文件id(年月日时分秒)
private String File_name;//文件标题
private String File_address;//文件地址
private String File_author;//文件作者
private String File_publisher;//文件发布者
private String File_type;//文件类型
private String File_retime;//更新时间(年月日时分秒)
private String File_uptime;//发布时间(年月日时分秒)
private Integer File_useto;//文件用途:0工作文件、1学习文件
private Integer File_authority;//文件所需权限
private Integer File_topping;//文件置顶 1为置顶 0不置顶
@Override
public String toString() {
return "File{" +
"File_id='" + File_id + '\'' +
", File_name='" + File_name + '\'' +
", File_address='" + File_address + '\'' +
", File_author='" + File_author + '\'' +
", File_publisher='" + File_publisher + '\'' +
", File_type='" + File_type + '\'' +
", File_retime='" + File_retime + '\'' +
", File_uptime='" + File_uptime + '\'' +
", File_useto=" + File_useto +
", File_authority=" + File_authority +
", File_topping=" + File_topping +
'}';
}
}控制器层(C,Controller)
一般来说这里就是后端的servelt,在这里处理前端的请求,将处理后的数据供给给视图层。
controller例子
package com.csc_filemanager.dev.controller;
import com.csc_filemanager.dev.mapper.FileMapper;
import com.csc_filemanager.dev.pojo.Filee;
import com.csc_filemanager.dev.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping("/file")
public class FileController {
@Autowired
private FileMapper fileMapper;
//根据文件id查询文件
@PostMapping("/findfilebyid")
public Result findfilebyid(String file_id){
Filee filee=fileMapper.findfile(file_id);
if(filee==null){
return Result.error("未查询到该文件");
}
return Result.ok().data("file",filee);
}
//根据发布者查询文件
@PostMapping("/findfilesbypublisher")
public Result findfilesbypublisher(String file_publisher){
List<Filee> filees= fileMapper.findfilesbypublisher(file_publisher);
if(filees==null){
return Result.error("未查询到这些文件");
}
return Result.ok().data("files",filees);
}
//根据时间范围查询文件 startt为年月日
@PostMapping("/findfilesbyuptime")
public Result findfilesbyuptime(String startt,String endt){
startt=startt+"000000";
endt=endt+"235959";
List<Filee> filees=fileMapper.findfilesbyuptime(startt,endt);
if(filees==null){
return Result.error("未查询到文件");
}
return Result.ok().data("file",filees);
}
//删除文件
@PostMapping("/deletefile")
public Result deletefile(String file_id, HttpServletRequest request){
if(fileMapper.findfile(file_id)==null){
return Result.error("要删除的文件已不存在");
}
File file = new File(request.getServletContext().getRealPath("/upload/")+fileMapper.findfile(file_id).getFile_address());
if(file.isFile()&&file.exists()) {
file.delete();
}
int result=fileMapper.deletefile(file_id);
if(result>0 && fileMapper.findfile(file_id)==null){
return Result.success("删除成功");
}else {
return Result.error("删除失败");
}
}
//上传文件
@PostMapping("/upload")
@ResponseBody //自动返回json格式
public Result up(Integer topping,Integer authority,Integer useto,String publisher,String author,MultipartFile thefile, HttpServletRequest request) throws IOException{
Filee filee = new Filee();
filee.setFile_topping(topping);
filee.setFile_authority(authority);
filee.setFile_useto(useto);
filee.setFile_author(author);
filee.setFile_publisher(publisher);
//获取图片的原始名称
filee.setFile_name(thefile.getOriginalFilename());
//取文件类型
System.out.println(thefile.getContentType());
filee.setFile_type(thefile.getContentType());
System.out.println(System.getProperty("user.dir"));
//获取文件类型
String fileName= thefile.getOriginalFilename();
assert fileName != null;
String suffix = fileName.substring(fileName.lastIndexOf(".")+1);
//设置保存路径
String path = request.getServletContext().getRealPath("/upload/");
String i="0000000";
i=saveFile(thefile,path,suffix);
filee.setFile_address("/upload/"+i);
// System.out.println("1-12:"+i.substring(0,12));
filee.setFile_id(i.substring(0,12));
filee.setFile_retime(i.substring(0,12));
filee.setFile_uptime(i.substring(0,12));
int flag=fileMapper.addfile(filee);
if(i.equals("0000000") || flag<0)
return Result.error("上传失败");
else
return Result.success("上传成功");
}
public String saveFile(MultipartFile thefile, String path,String suffix) throws IOException{
File dir = new File(path);
//判断存储的目录是否存在,如果不在则创建
if(!dir.exists()){
//创建目录
dir.mkdir();
}
//文件id 日时分秒
Date date = new Date();
SimpleDateFormat df=new SimpleDateFormat("yyMMddhhmmss");
String File_id=df.format(date);
for(;fileMapper.findfile(File_id)!=null;){
File_id = df.format(date);
}
System.out.println(path+File_id+"."+suffix);
File file = new File(path+File_id+"."+suffix);
thefile.transferTo(file);
return File_id+"."+suffix;
}
}视图层(V,View)
这里就是我们前端使用的HTML和JSP页面,是直接和用户交互的层面。
工作流程
1.用户请求:用户通过浏览器发生HTTP请求到服务器。
2.前端控制器:Spring的前端控制器DispatcherServlet接收到请求。
3.处理器映射:DispatcherServlet根据请求URL和配置的映射规则,找到对应的控制器方法。
4.控制器处理:控制器方法被调用,处理用户的请求。
5.调用服务层:控制器调用服务层的方法,进行业务逻辑处理。
6.调用数据访问层:服务层调用数据访问层的方法,进行数据操作。
7.返回数据:数据访问层返回数据给服务层,服务层处理完业务后返回数据给控制器。
8.选择视图:控制器选择合适的视图进行渲染,并将数据传递给视图。
9.视图渲染:视图生成最终的HTML页面。
10.响应用户:DispatcherServlet将生成的HTML页面返回给用户。
Spring框架通过@Controller、@Service和@Repository注解,清晰地划分了MVC三层架构的各个层次,使得开发人员能够更高效地构建和维护复杂的Web应用。
文件目录

上图为Springboot编写的M层和C层,V层为HTML格式等前端代码。