mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

作为经典MVC思想的spring实现,它能够帮我们开发灵活的JavaWeb应用。今天我们就来对它动刀,看看它的内部是怎么实现的,我们能不能仿写一份呢?首先我们通过一张时序图来看一下springMVC的运行流程。springMVC运行流程从上面的时序图

作为经典MVC思想的spring实现,它能够帮我们开发灵活的JavaWeb应用。今天我们就来对它动刀,看看它的内部是怎么实现的,我们能不能仿写一份呢?

首先我们通过一张时序图来看一下springMVC的运行流程。mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

springMVC运行流程

从上面的时序图,我们可看到,一个叫dispatcherServlet的家伙十分繁忙,几乎每一步都有它的参与,他怎么这么忙啊,这就和它的名字有关

dispatcher /dɪs’pætʃə/ n. 发报机,调度员

它就相当于在M-V-C三者之间的邮差,或者说是领导,负责调用各个组件。

我们来假设一下这个场景:

DispatcherServlet是MVC场景里的老大,而且亲力亲为,什么事都要他过目审批,这天他收到了一份用户请求,叫他给出一个网页页面。

他马上给他的副手HandlerMapping,说:“小刘,你看看这个活,谁来干合适?”小刘HandlerMapping一看员工花名册有一个叫小张的Controller能够胜任,小刘就对领导说:“Controller小张能干”。

这时候,领导DispatcherServlet不能直接找到小张,因为小张只负责实现具体业务,而用户的要求太抽象,小张看不懂,需要有个人帮他理一理,第一步该做什么,第二步该做什么。这时候项目经理HandlerAdaper就上线了,领导找到项目经理说:“帮小张理一理,这个活具体该咋做”。

项目经理三下五除二给整完了,之后,领导拿着处理好的任务,将任务交给里小张,我们的小张也很争气呀,也给干完了,而且,他干的工程不仅有业务(Model)还有漂亮的组件(View),不过小张同学的审美不太好,没办法把它们组合到一块。于是,领导DispatcherServlet就吭哧吭哧跑到学美术的viewRsolver身边,让她给渲染一下。viewRsolver画技高超,寥寥几笔渲染出来了一份既有业务资料,也很好看的页面出来。

至此一个项目完成了,DispatcherServlet就拿着成果(JSP等前端页面)展示给用户看,用户心满意足,大方的付了钱,于是,大家都有钱拿…

看完了Rod Johnson的springMVC的MVC 流程,里面组件分工明确,各司其职,能够完成很多复杂的业务,但是我们刚开始上手,肯定不能上来就整这么多,因此今天我们搭一个简单版的,只有领导(DispatcherServlet)和各类业务员等。业务员,还是只负责具体业务,其他的活全让领导干。

我们的流程:

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

在我们的流程中 DispatcherServlet领导 = 前端控制器 + 映射处理器

好了明确了我们要搭的任务,现在建哥来手把手教学,开搞!

详细步骤

1.新建webApp骨架的maven工程

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

2.在pox.xml中引入依赖

<!– 引入servlet jar –>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!– 引入反射jar包–>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>

3.新建包如图所示

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

4.编写配置文件

在resource目录下编写配置文件:
applicationContext.properties,内容为:指定扫描路径package,我们在这里指定controller所在的包

package=com.cloudwise.controller

5.更新web.xml文件

骨架用的还是2.0版本,我们在这里更新为4.0的。

并且注册我们的领导MyDispatcherServlet并为其指定配置文件所在位置contextConfigLocation,我们的领导凡事亲力亲为,在这里让他拦截所有请求。

<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<web-app xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\"
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd\"
version=\"4.0\">
<display-name>Archetype Created Web Application</display-name>

<!-- 配置我们自己的前端控制器,MyDispatcherServlet就是一个servlet,拦截前端发送的请求-->
<servlet>
<servlet-name>xxx</servlet-name>
<servlet-class>com.cloudwise.servlet.MyDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>xxx</servlet-name>
<!-- 拦截所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

6.自定义注解

注解在这里的作用就相当于给类/方法加上一个小尾巴,我们通过不同的尾巴辨识不同的Controller和Method

我们定义两个注解

@MyController
package com.cloudwise.annotition;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Teacher 陈
* @creat 2021-02-22-13:04
* @describ 我的Controller注解,用于模仿spring中的@Controller
* 能够作用于类上,标识该类是一个Controller
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
/**
* 没有用,但为了模仿spring中的@Controller,我们还是把它加上
* 我们的简单版采用默认的id:首字母小写的类名
*/
String value() default \"\";
}
@MyRequestMapping
package com.cloudwise.annotition;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Teacher 陈
* @creat 2021-02-22-13:11
* @describ 用于模仿spring中的@RequestMapping
* 能够作用于类和方法上,用于通过url指定对应的Controller和 Method
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
/**
* 简单版,域名只能有一段,只能是/controllerName/methodName
*/
String value() default \"\";
}

好了上面的就是一些准备性的工作,如果说把仿写springMVC看成是组成一个团队的话,上面的工作相当于给团队找工作场地,下面就是对人物的刻画了,首先有请我们的领导MyDispatcherServlet

编写前端控制器

编写前端控制器(一个Servlet),并重写init和service方法

MyDispatcherServlet

总览

整个过程围绕两个重写的方法而展开,其中init()是重点。

MyDispatcherServlet要做的事,用一句话来说:看前端的访问地址,然后调用匹配的处理器(Controller)的对应方法(method)

要完成这些,我们需要通过注解,为Controller和method绑定上一定的字符串,然后通过分析前端传过来的Url中的字符串,找到两者相同的,以此完成匹配。反射在此过程中发挥了巨大作用,不论是找到类头上的注解,还是找到注解中的值等诸多动作都需要反射。

具体流程

Init

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

Service 注:在此处Handler = controller + method

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

代码(分步)

创建一个dispatcherServlet继承httpservlet 并重写两个方法

public class MyDispatcherServlet extends HttpServlet {

@Override
public void init(ServletConfig config) throws ServletException {
}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}
}

接下来就是填充两个方法了,首先是init()方法

它大概可以分为4步

  1. 加载配置文件
  2. 扫描controller包
  3. 初始化controller
  4. 初始化Handler映射器(Handler = controller + method)

那我们开始吧,写加载配置文件的代码

1.加载配置文件

首先,我们在这里选用properties文件的形式进行配置,因此,需要有一个properties对象

/**
* 我们将需要扫描的包放在一个.properties文件中
* 需要在初始化的时候读取它
*/
private Properties properties = new Properties();
再写一个工具性的方法
/**
* 加载配置文件
* @param fileName
*/
private void loadConfigfile(String fileName) throws IOException {
//以流的方式获取资源
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(fileName);
properties.load(resourceAsStream);
resourceAsStream.close();
}
之后,我们在init()中调用该方法
@Override
public void init(ServletConfig config) throws ServletException {
//1. 加载配置文件
//这里我们在web.xml中配置的初始化参数contextConfigLocation就起到效果了
String initParameter = config.getInitParameter(\"contextConfigLocation\");
try {
loadConfigfile(initParameter);
} catch (IOException e) {
e.printStackTrace();
}
}

那么至此,我们的第一步加载配置文件部分的代码就写完啦

另外三步采用同样的思路

2.扫描controller包

定义所需属性

/**
* 我们需要一个Set,将所有能够响应的Controller存起来
*/
private Set<Class<?>> classSet = new HashSet<>();
写工具性方法
/**
* 扫描包,获取所有带MyController的类
* @param packageName
*/
private void scanPackage(String packageName){
Reflections reflections = new Reflections(packageName);
classSet = reflections.getTypesAnnotatedWith(MyController.class);
}

在init()中调用

@Override
public void init(ServletConfig config) throws ServletException {
//1. 加载配置文件
//这里我们在web.xml中配置的初始化参数contextConfigLocation就起到效果了
String initParameter = config.getInitParameter(\"contextConfigLocation\");
try {
loadConfigfile(initParameter);
} catch (IOException e) {
e.printStackTrace();
}

//2. 扫描controller包,存储所有能够响应的Controller
scanPackage(properties.getProperty(\"package\"));

}

3.初始化controller

定义所需属性

/**
* 类spring-mvc容器,存储Controller对象
*/
private Map<String,Object> mySpringMVCContext = new HashMap<>();
写工具性方法
/**
* 初始化Controller
*/
private void initController() throws IllegalAccessException, InstantiationException {
if(classSet.isEmpty()){
return;
}
for (Class<?> controller : classSet) {
mySpringMVCContext.put(firstWordToLowCase(controller.getSimpleName()),controller.newInstance());
}
}
/**
* 首字母转小写
* @param string
* @return 首字母为小写的String
*/
private String firstWordToLowCase(String string){
char[] chars = string.toCharArray();
//将大写转成小写
chars[0]+=32;
return String.valueOf(chars);
}
在init()中调用
@Override
public void init(ServletConfig config) throws ServletException {
//1. 加载配置文件
String initParameter = config.getInitParameter(\"contextConfigLocation\");
try {
loadConfigfile(initParameter);
} catch (IOException e) {
e.printStackTrace();
}
//2. 扫描controller包,存储所有能够响应的Controller
scanPackage(properties.getProperty(\"package\"));
//3. 初始化controller
try {
initController();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}

4.初始化Handler映射器

(Handler = controller + method)

定义所需属性

/**
* 存储所有方法的Map<url:method>
*/
private Map<String,Method> methodMap = new HashMap<>();
/**
* 存储所有Controller的Map
*/
private Map<String,Object> controllerMap = new HashMap<>();

具体实现方法

private void initHandlerMapping() {
if (mySpringMVCContext.isEmpty()){
return;
}
for (Map.Entry<String, Object> entry : mySpringMVCContext.entrySet()) {
Class<?> entryClass = entry.getValue().getClass();
if (!entryClass.isAnnotationPresent(MyController.class)){
continue;
}
//Controller类上的requestMapping值,如果有则获取
String baseUrl = \"\";
if (entryClass.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = entryClass.getAnnotation(MyRequestMapping.class);
baseUrl = annotation.value();
}
//获取所有方法
Method[] methods = entryClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
String url = annotation.value();
url = baseUrl + url;
//将该方法放入方法集
methodMap.put(url,method);
//将该controller方法处理器集
controllerMap.put(url,entry.getValue());
//至此,初始化完成,后端整装待发
}
}
}
}

在init()中调用

@Override
public void init(ServletConfig config) throws ServletException {
//1. 加载配置文件
String initParameter = config.getInitParameter(\"contextConfigLocation\");
try {
loadConfigfile(initParameter);
} catch (IOException e) {
e.printStackTrace();
}
//2. 扫描controller包,存储所有能够响应的Controller
scanPackage(properties.getProperty(\"package\"));
//3. 初始化controller
try {
initController();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
//4. 初始化Handler映射器
initHandlerMapping();
}

好了至此,我们的领导MyDispatcherServlet 的初始化部分就写完了,现在他已经对自己的团队成员:众多业务员们(Controller)已经了如指掌了(有同学可能会问:陈老师,你还没定义Controller呢!这个先不急)下面,我们就重写他的service()方法,让他能够到外面接活

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (methodMap.isEmpty()){
return;
}
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
//获取有效url
String url = uri.replace(contextPath,\"\");
//如果没有对应的url,返回404
if (!methodMap.containsKey(url)){
resp.getWriter().println(\"404\");
}else {
//有的话,通过invoke方法执行对应controller的method
Method method = methodMap.get(url);
Object controller = controllerMap.get(url);
try {
method.invoke(controller);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

至此,MyDispatcherServlet的所有代码都已经完成了,他也能够成为一个合格的领导了。

上面部分代码写的较为分散,文末放上MyDispatcherServlet的完整代码供同学们参考

最后,编写一个Controller进行测试

package com.cloudwise.controller;/**
* @author Teacher 陈
* @creat 2021-02-22-14:57
* @describ
*/
import com.cloudwise.annotition.MyController;
import com.cloudwise.annotition.MyRequestMapping;
/**
* @author :Teacher 陈
* @date :Created in 2021/2/22 14:57
* @description:我的实验性Controller
* @modified By:
* @version:
*/
@MyController
@MyRequestMapping(value = \"/test\")
public class MyFirstController {
@MyRequestMapping(value = \"/test1\")
public void test1(){
System.out.println(\"test1被调用了\");
}
@MyRequestMapping(value = \"/test2\")
public void test2(){
System.out.println(\"test2被调用了\");
}
@MyRequestMapping(value = \"/test3\")
public void test3(){
System.out.println(\"test3被调用了\");
}
}

测试

1.为本项目配置tomcat

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

2.运行

3.1浏览器地址栏输入对应网址

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

控制台成功打印日志信息

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

3.2浏览器地址栏输入无效网址,页面返回404

mvc架构图用什么画(mvc框架图书馆管理系统jdbc)

至此,今天的手写springMVC就全部完成了。

当然本项目还有很多待提升的地方,诸如不能返回json数据,controller不能有参数,等等。但是我们不可能一朝一夕建成罗马,应该一步一个脚印,通过这个项目掌握springMVC的运行流程,为以后更难的项目打下点基础。

代码(总览)

package com.cloudwise.servlet;/**
* @author Teacher 陈
* @creat 2021-02-22-13:44
* @describ
*/
import com.cloudwise.annotition.MyController;
import com.cloudwise.annotition.MyRequestMapping;
import org.reflections.Reflections;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author :Teacher 陈
* @date :Created in 2021/2/22 13:44
* @description:我的前端控制器dispather
* @modified By:
* @version:
*/
public class MyDispatcherServlet extends HttpServlet {
/**
* 我们将需要扫描的包放在一个.properties文件中
* 需要在初始化的时候读取它
*/
private Properties properties = new Properties();
/**
* 我们需要一个Set,将所有能够响应的Controller存起来
*/
private Set<Class<?>> classSet = new HashSet<>();
/**
* 类spring-mvc容器,存储Controller对象
*/
private Map<String,Object> mySpringMVCContext = new HashMap<>();
/**
* 存储所有方法的Map<url:method>
*/
private Map<String,Method> methodMap = new HashMap<>();
/**
* 存储所有Controller的Map
*/
private Map<String,Object> controllerMap = new HashMap<>();
/**
* @description: 初始化,要做什么事呢?
* 0. 接收到请求之后,首先将后端环境初始化好
* 1. 加载配置文件
* 2. 扫描controller包
* 3. 初始化controller
* 4. 初始化Controller映射器
* @create by: Teacher 陈
* @create time: 2021/2/22 13:47
* @param config
* @return void
*/
@Override
public void init(ServletConfig config) throws ServletException {
//1. 加载配置文件
String initParameter = config.getInitParameter(\"contextConfigLocation\");
try {
loadConfigfile(initParameter);
} catch (IOException e) {
e.printStackTrace();
}
//2. 扫描controller包,存储所有能够响应的Controller
scanPackage(properties.getProperty(\"package\"));
//3. 初始化controller
try {
initController();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
//4. 初始化Controller映射器
initHandlerMapping();
}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (methodMap.isEmpty()){
return;
}
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
String url = uri.replace(contextPath,\"\");
if (!methodMap.containsKey(url)){
resp.getWriter().println(\"404\");
}else {
Method method = methodMap.get(url);
Object controller = controllerMap.get(url);
try {
method.invoke(controller);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
/**
* 以下为工具性函数
*/
/**
* 加载配置文件
* @param fileName
*/
private void loadConfigfile(String fileName) throws IOException {
//以流的方式获取资源
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(fileName);
properties.load(resourceAsStream);
resourceAsStream.close();
}
/**
* 扫描包,获取所有带MyController的类
* @param packageName
*/
private void scanPackage(String packageName){
Reflections reflections = new Reflections(packageName);
classSet = reflections.getTypesAnnotatedWith(MyController.class);
}
/**
* 初始化Controller
*/
private void initController() throws IllegalAccessException, InstantiationException {
if(classSet.isEmpty()){
return;
}
for (Class<?> controller : classSet) {
mySpringMVCContext.put(firstWordToLowCase(controller.getSimpleName()),controller.newInstance());
}
}
/**
* 首字母转小写
* @param string
* @return 首字母为小写的String
*/
private String firstWordToLowCase(String string){
char[] chars = string.toCharArray();
//将大写转成小写
chars[0]+=32;
return String.valueOf(chars);
}
private void initHandlerMapping() {
if (mySpringMVCContext.isEmpty()){
return;
}
for (Map.Entry<String, Object> entry : mySpringMVCContext.entrySet()) {
Class<?> entryClass = entry.getValue().getClass();
if (!entryClass.isAnnotationPresent(MyController.class)){
continue;
}
//Controller类上的requestMapping值,如果有则获取
String baseUrl = \"\";
if (entryClass.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = entryClass.getAnnotation(MyRequestMapping.class);
baseUrl = annotation.value();
}
//获取所有方法
Method[] methods = entryClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
String url = annotation.value();
url = baseUrl + url;
//将该方法放入方法集
methodMap.put(url,method);
//将该controller方法处理器集
controllerMap.put(url,entry.getValue());
//至此,初始化完成,后端整装待发
}
}
}
}
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至 55@qq.com 举报,一经查实,本站将立刻删除。转转请注明出处:https://www.szhjjp.com/n/24892.html

(0)
nan
上一篇 2021-09-13
下一篇 2021-09-13

相关推荐

  • 网站升级什么意思(为什么要进行网站升级)

    网站升级什么意思,为什么要进行网站升级 内容导航: 网站升级是怎么一回事呀 今天办了个营业执照国家网站什么时候更新 运营商网站升级什么意思 网站升级多长时间 一、网站升级是怎么一回…

    2022-08-16
    0
  • 如何在ArchLinux上安装软件包

    在ArchLinux上安装软件包通常使用pacman包管理器。以下是安装软件包的步骤:更新软件包数据库:在终端中运行以下命令来更新软件包数据库:sudo pacman -Sy搜索软件包:如果你不确定软件包的名称,可以使用以下命令来搜索软件包:pacman -Ss keyword其中“keyword”是你要搜索的软件包名称或关键字。安装软件包:一旦找到了你要安装的软件包,可以使用以下命令来安装:su

    2024-03-22
    0
  • 美国独立IP网站空间有什么优势

    美国独立IP网站空间有以下优势:稳定性:独立IP网站空间通常会提供更稳定的网络连接和服务器性能,确保网站的稳定运行。安全性:独立IP网站空间通常会提供更高级的安全防护措施,如防火墙、DDoS防护等,保障网站数据的安全。自主性:独立IP网站空间拥有独立的服务器资源,用户可以自由配置服务器环境和运行软件,更加灵活自主。速度:独立IP网站空间通常会提供更高的带宽和更快的网络速度,确保网站访问速度更

    2024-04-23
    0
  • ps如何做网页设计(PS网页设计教程)

    ps如何做网页设计,PS网页设计教程内容导航:怎么用ps做网页设计如何用Photoshop制作网页模板如何用Photoshop做网页设计用PS如何做网页按钮图片一、怎么用ps做网页设计你是要做效果图,还是生成网页?生成网页的话,直接用切片工具,切好,然后存储为web所用格式,选择格式为html就可以啦。二、如何用Photoshop制作网页模板网页中的元素有很多,像Bann

    2022-04-23
    0
  • 电商网站如何选择日本服务器

    在选择日本服务器时,电商网站需要考虑以下因素:服务器性能:选择高性能的服务器,以确保网站能够快速加载并处理大量的访问请求。网络连接速度:选择具有高速网络连接的服务器,以确保网站能够快速响应用户请求。数据中心位置:选择位于日本境内的数据中心,可以提高网站访问速度,并符合日本国内数据存储的法规要求。可靠性和稳定性:选择有良好声誉的服务提供商,确保服务器能够保持稳定运行,避免因服务器故障而导致网站宕机。

    2024-04-03
    0
  • php中get的用法是什么

    在PHP中,$_GET是一个超全局变量,用于获取通过GET方法传递的变量。当用户通过URL传递参数时,可以使用$_GET来获取这些参数的值。例如,如果用户访问的URL是:http://example.com/index.php?id=123&name=John可以使用$_GET来获取id和name的值:$id = $_GET['id']; // 123$name = $

    2024-02-20
    0

发表回复

登录后才能评论