# 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中获取对应的参数,执行对应的方法。
这样的**框架式项目结构**使得管理、维护和扩展项目变得非常的方便,低耦合的优点爽到爆炸。
**响应流程图:**
\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执行流程
看图和分析:
\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扫描加载。**