# MVC架构 **Repository Path**: bloomcool/mvc-architecture ## Basic Information - **Project Name**: MVC架构 - **Description**: 从零搭建的MVS架构模式项目,拿来即用 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-09-16 - **Last Updated**: 2021-10-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、MVC架构介绍 前面已经有文章提到了 **[mvc分层架构](https://blog.csdn.net/yeahPeng11/article/details/120289852)** 的特点,这里就来使用Java实现一个mvc架构。 > 参考SpringMVC架构,简单的实现MVC架构搭建。 本文提供一个从零搭建一个**MVC架构**项目的教程,涉及到**Java反射**的技术。没有使用任何的框架,实现了把Controller交给容器去管理,底层使用请求作为key去管理controller方法的map中获取对应的参数,执行对应的方法。 这样的**框架式项目结构**使得管理、维护和扩展项目变得非常的方便,低耦合的优点爽到爆炸。 **响应流程图:** ![](D:\JavaEE\笔记(20210316)\Java Web\images\mvc0.png)
# 二、快速启动 **所需环境**:maven、jdk1.8+、mysql(本地没有数据库的可以引入h2数据库进行操作,[点击我引入h2数据库](https://blog.csdn.net/yeahPeng11/article/details/120257881))。 **项目地址:https://gitee.com/pdh_gitee/mvc-architecture.git** 把此项目git clone到本地之后,即可: 1. 执行sql脚本,即项目目录下的sql包中的mvc_demo.sql文件。 2. 运行项目,打开浏览器,访问`localhost:8080`即可。 # 三、MVC架构搭建(核心) ## 1.新建Maven项目 在maven下创建java web项目,需要在maven中引入需要的配置文件: **pom.xml** ```xml 4.0.0 org.example mvc 1.0-SNAPSHOT war mvc Maven Webapp http://www.example.com UTF-8 1.7 1.7 javax.servlet javax.servlet-api 4.0.1 provided junit junit 4.11 test com.alibaba druid 1.0.9 mysql mysql-connector-java 5.1.40 org.json json 20160810 com.google.code.gson gson 2.8.2 mvc org.apache.tomcat.maven tomcat7-maven-plugin 2.2 8080 / ```
## 2.方法类型定义 方法分为ResponseBody、ResponseView两类,以ResponseType作为判断依据。 > 具体的作用还需要在之后的代码编写中实现。 ### 2.1 ResponseBody和ResponseView ResponseBody、ResponseView都是注解类,用于判断类型。在把方法存入MVCMapping的时候,判断此方法是什么类型。 - **ResponseBody返回的是字符串类型数据。** - **ResponseView返回的是视图类型,重定向到视图。** ```java @Target(ElementType.METHOD) //作用于方法上 @Retention(RetentionPolicy.RUNTIME) //作用在运行时 @Documented //生成到文档里 public @interface ResponseBody { String value(); } ``` ```java @Target(ElementType.METHOD) //作用于方法上 @Retention(RetentionPolicy.RUNTIME) //作用在运行时 @Documented //生成到文档里 public @interface ResponseView { String value(); } ``` ### 2.2 ResponseType ResponseType是枚举类,枚举返回的类型是**TXET**还是**VIEW**视图。 > **它会存入Map集合中,在根据uri获取对应的方法的时候**,会根据它的类型进行对应的处理。 ```java public enum ResponseType { TEXT,VIEW; } ```
## 3.映射器HandlerMapping ### 3.1 HandlerMapping核心 映射器HandlerMapping的作用是 **在DispatcherServlet初始化init()的时候,调用HandlerMapping的`load()`方法,加载配置文件中的类里的方法到HandlerMapping的`Map`中,并且提供接口`get()`给DispatcherServlet在HandlerMapping中找到与之匹配的请求,返回请求绑定的方法**。 关于HandlerMapping内部封装的映射类**MVCMapping**: 1. 包含三种参数:**obj、method和type,obj表示表示类对象,method表示方法对象,type表示方法对象的类型**。 2. 作为Map的value,存储所有的请求和对应的方法。 关于**Map**: 1. map里存储的是配置文件中的所有类里的注解修饰的**请求和方法**。 2. key是注解绑定的请求名,value是内部封装的MVCMapping映射对象。 3. 在整个项目运行过程中,经过DispatcherServlet初始化调用HandlerMapping的load()方法加载到map后,此map会一直保留。 4. 之后DispatcherServlet的每次`get()`请求都是在获取map中的方法。 ### 3.2 方法描述 **void load(InputStream is)**:被DispatcherServlet调用。用于加载配置文件application.properties中的所有类信息到map中,会在DispatcherServlet初始化init()方法执行期间被调用,当全部类的方法都加载到map中之后,才会进行相应的请求处理。 **MVCMapping get(String uri) **:被DispatcherServlet调用。用于获取请求对应的MVCMapping封装对象,之后使用Method执行`invoke()`方法,能够去执行到对应的方法。
## 4.DispatcherServlet 它继承HttpServlet,作为此项目的Servlet服务器,会接收到用户每次发起的每次请求。 **DispatcherServlet重写了HttpServlet的`init()`、`service()`两个方法:** 1. `init()`方法是Servlet初始化方法。初始化的时候会执行此方法,我们在inint()方法里面获取到配置文件的路径,加载配置文件信息,之后再执行HandlerMapping的`load()`方法,加载类到map容器中。 2. `service()`方法是接收用户请求的方法。用户发起的所有类型的请求(get、post、... ... ),都会被service()接收到,而这里,service就直接根据获取到的请求uri,调用HandlerMapping的`get()`方法获取MVCMapping封装对象,之后就执行封装对象里面的方法。最后根据MVCMapping里面的方法类型,使用重定向或转发把结果返回。 **再来看一下DispatcherServlet响应的流程:** 当用户发起第一次请求之后,DispatcherServlet会调用init()方法加载配置文件,加载完毕以后,在由service()方法接收对应的请求uri,根据uri获取封装对象MVCMapping,最后执行方法返回结果。 > DispatcherServlet接收到请求可不是随便说就能接收到的,需要在web.xml配置文件中进行配置,还能做一定的过滤操作。 当用户不是第一次发起请求,此时HandlerMapping已经把所有的请求方法映射到map容器中,此时获取到uri请求直接获取对应的方法,执行并返回结果即可。
## 5.application.properties类加载控制器 application.properties是用作配置需要被HandlerMapping加载的类的,把需要加载到map容器中的类都以**键值对**的方式写到此配置文件中(key是随意的,因为load()的时候,读取的是value)。 **application.properties加载原理:** 编写好application.properties类加载控制器配置文件后,在web项目中,webapp包下会有一个**web.xml**配置文件,配置application.properties文件路径。当DispatcherServlet加载的时候,去web.xml配置文件中找到application.properties路径,之后进行初始化加载类方法操作。 当我们的控制器类非常多的时候,只需要把每一个控制器类都以键值对的方式写入此文件,就能实现加载,这样管理起来变得非常的方便! 示例: ```properties abc=com.pdh.controller.UserController ```
## 6.web.xml配置文件 配置DispatcherServlet作为此项目的servlet(包含application.properties文件配置)。 ```xml DispatcherServlet com.pdh.mvc.DispatcherServlet contentConfigLocation application.properties 0 DispatcherServlet *.do ``` 此配置文件在早期的Java web项目中是必不可少的配置文件,许多核心的配置都在此处进行加载。
## 7.测试MVC 至此,MVC架构核心搭建已经完毕,之后就是编写controller、service、dao和前端页面。下面进行一个简单的测试: UserController ```java public class UserController { @ResponseBody("/login.do") public String login(HttpServletRequest req, HttpServletResponse rsp){ return "登陆成功"; } @ResponseView("/reg.do") public String reg(HttpServletRequest req, HttpServletResponse rsp){ return "success.jsp"; } } ``` 之后启动服务器,在浏览器输入:`localhost:8080/login.do`和`localhost:8080/reg.do`测试两个不同响应请求的操作: 1. `@ResponseBody("/login.do")`:会返回数据包。 2. `@ResponseView("/reg.do")`:会跳转页面。
## 8.MVC执行流程 看图和分析: ![](D:\JavaEE\笔记(20210316)\Java Web\images\mvc0.png) 1. web.xml指定servelt,用户发起请求找到DispatchServlet。 2. 初次响应请求DispatchServlet执行init()方法加载**application.properties**配置文件信息,调用HandlerMapping的`load()`方法,加载 **类控制器中的方法和请求** 到HandlerMapping中的map中。 3. DispatchServlet的service()接收请求,通过HandlerMapping的`get()`方法获取请求对应的MVCMapping封装对象,之后执行方法返回数据。 4. DispatchServlet中获取到MVCMapping封装对象后,获取到封装对象里面的 **类控制器对象、方法对象、方法类型** ,之后调用Method的`invoke()`执行到Controller里对应的方法,Controller调用service,service调用dao,dao由德鲁伊连接池获取得到连接返回数据库。之后逐层放回数据。
# 四、以MVC为基础的JavaWeb项目 在之前都已经把MVC架构搭建完毕后,剩下的就是简单的Controller、Service、Dao层编写,其中的数据库交互部分使用德鲁伊连接池是非常合适的。 ## 1.配置德鲁伊连接池 **druid.properties配置文件** ```properties url=jdbc:mysql://localhost:3306/mvc_demo username=root password= # driverClassName=com.mysql.jdbc.Driver #连接池初始连接数,通过getConnection获取连接 initialSize=5 #最大进程数 maxActive=10 #最小闲置 minIdle=5 #最长等待时间 maxWait=30000 ``` **DruidUtil类** 它会提供一个数据库连接池,每次有与数据库交互的请求的时候,dao从德鲁伊连接池获取到一个连接,进行数据库交互操作,使用结束以后,又把此连接关闭,交还给德鲁伊连接池。 DruidUtil类提供两个方法: **`Connection getConnection()`**:获取连接。从德鲁伊连接池获取到一个连接。 **`void close()`**:关闭方法。关闭Connection、Statement和ResultSet,一个请求处理完成以后,需要将此请求对应的连接关闭掉,返回到连接池。
## 2.Model ### 2.1 实体类entity 与数据库`demo_mvc`对应的表是 `student` 表,字段也与数据表对应。 **Student类** ```java public class Student { private Integer id; private String name; private Integer age; private char sex; // 手动生成getter,setter,无参,全参的构造方法 } ``` ### 2.2 dao dao层与数据库的交互的方法:**通过DruidUtil从连接池获取到数据库连接,之后编译sql语句,执行获取数据。** 接口提供一个方法UserDao,这里只是提供一个`getAll()`方法获取所有的数据。实现类UserDaoImpl实现UserDao接口,重写它的方法。 ### 2.3 service > service层定义业务逻辑,但是我们实现的业务逻辑相对简单,service这里之上作为一个中转,在dao和controller之间,降低它们之间的耦合。 编写与dao层对应的方法,调用dao层方法即可,这里只是调用dao的`getAll()`方法获取所有的数据。
## 3.Controller ### 3.1 controller 接收前端发送过来的请求,返回相应的数据。调用service的方法,这里调用service的`getAll()`方法获取所有的数据,返回到前端。
## 4.View 前端部分不做要求。 编写一个index.html页面和list.html页面,**list.html展示数据。**
## 5.拓展 **在此基础只是,可以编写出一个项目了,编写好对应controller,service,dao和前端页面。记得要把controller类添加到`application.properties`文件中,才会被map扫描加载。**