首页IT科技spring mvc底层原理(day05-SpringMVC底层机制简单实现-01)

spring mvc底层原理(day05-SpringMVC底层机制简单实现-01)

时间2025-05-04 23:49:37分类IT科技浏览3528
导读:SpringMVC底层机制简单实现-01 主要完成:核心分发控制器+Controller和Service注入容器+对象自动装配+控制器方法获取参数+视图解析+返回JSON格式数据...

SpringMVC底层机制简单实现-01

主要完成:核心分发控制器+Controller和Service注入容器+对象自动装配+控制器方法获取参数+视图解析+返回JSON格式数据

1.搭建开发环境

创建 Maven 项目            ,File-New-Project-Maven

将 pom.xml 文件中的编译版本改为1.8

在 src 目录下创建以下目录:

java 代码放在 java 目录下              ,相关的资源文件放在 resource 目录下      ,对 maven 的 web 项目而言         ,resource 就是类路径            。前端页面放在 webapp 下              ,该目录对应之前的 web 目录              。test/java 目录用于存放测试文件        ,测试需要的资源文件放在 test/resource 目录下      。

在 pom.xml 中引入基本的 jar 包

<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--引入原生servlet依赖的jar包--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <!-- 1.scope表示引入jar包的作用范围      , 2.provided表示项目在打包放到生产环境时               ,不需要打上servlet-api.jar 3.因为 tomcat本身就有该jar包          ,使用tomcat的即可   ,防止版本冲突 --> <scope>provided</scope> </dependency> <!--引入dom4j,用于解析xml--> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!--引入常用的工具类jar包,该jar含有很多常用的类--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> </dependency> </dependencies>

2.任务1-开发MyDispatcherServlet

说明:编写 MyDispatcherServlet                ,充当原生的 DispatcherServlet(即核心控制器)

2.1分析

2.2代码实现

创建 src/main/java/com/li/myspringmvc/servlet/MyDispatcherServlet.java            ,充当原生的前端控制器         。

package com.li.myspringmvc.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author 李 * @version 1.0 * 1.MyDispatcherServlet 充当原生的 DispatcherServlet,它的本质就是一个Servlet * 因此继承 HttpServlet */ public class MyDispatcherServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyDispatcherServlet doGet() 被调用.."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyDispatcherServlet doPost() 被调用.."); } }

创建 src/main/resources/myspringmvc.xml              ,充当原生的 applicationContext-mvc.xml(即 spring 容器配置文件)

配置 src/main/webapp/WEB-INF/web.xml

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>MyDispatcherServlet</servlet-name> <servlet-class>com.li.myspringmvc.servlet.MyDispatcherServlet</servlet-class> <!--给前端控制器指定配置参数              ,指定要操作的spring容器文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:myspringmvc.xml</param-value> </init-param> <!--要求该对象在tomcat启动时就自动加载--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>MyDispatcherServlet</servlet-name> <!--作为前端控制器   ,拦截所有请求--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>

配置 Tomcat            ,进行测试

浏览器访问 http://localhost:8080/li_springmvc/aaa

3.任务2-实现客户端/浏览器可以请求控制层

3.1分析

任务2的总目标是:

实现自己的 @Controller 注解和 @RequestMapping 注解              ,当浏览器访问指定的 URL 时      ,由前端控制器         ,找到 Controller 的某个方法              ,然后通过 tomcat 将数据返回给浏览器              。

3.2代码实现

步骤一:两个注解和测试Controller

(1)Controller 注解

package com.li.myspringmvc.annotation; import java.lang.annotation.*; /** * @author 李 * @version 1.0 * 该注解用于标识一个控制器组件 * 1.@Target(ElementType.TYPE) 指定自定义注解可修饰的类型 * 2.@Retention(RetentionPolicy.RUNTIME) 作用范围        ,RUNTIME使得可以通过反射获取自定义注解 * 3.@Documented 在生成文档时      ,可以看到自定义注解 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Controller { String value() default ""; }

(2)RequestMapping 注解

package com.li.myspringmvc.annotation; import java.lang.annotation.*; /** * @author 李 * @version 1.0 * RequestMapping 注解用于指定控制器-方法的映射路径 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestMapping { String value() default ""; }

(3)用于测试的控制器 MonsterController.java

package com.li.controller; import com.li.myspringmvc.annotation.Controller; import com.li.myspringmvc.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * @author 李 * @version 1.0 */ @Controller public class MonsterController { //编写方法               ,可以列出妖怪列表 //springmvc支持原生的servlet api          ,为了看到底层机制   ,这里直接放入两个参数 @RequestMapping(value = "/list/monster") public void listMonster(HttpServletRequest request, HttpServletResponse response) { //设置编码 response.setContentType("text/html;charset=utf-8"); //获取writer                ,返回提示信息 try { PrintWriter printWriter = response.getWriter(); printWriter.print("<h1>妖怪列表信息</h1>"); } catch (IOException e) { e.printStackTrace(); } } }

步骤二:配置容器文件 springmvc.xml            ,指定扫描的包

指定扫描的包是为了之后使用注解获取需要反射的类

如果需要添加新的扫描包,在base-package添加包路径              ,用逗号表示间隔        。

<?xml version="1.0" encoding="utf-8" ?> <beans> <!--指定要扫描的包及其子包的java类--> <component-scan base-package="com.li.controller,com.li.service"/> </beans>

步骤三:编写 XMLParse 工具类              ,用于解析 springmvc.xml   ,得到要扫描的包

package com.li.myspringmvc.xml; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.List; /** * @author 李 * @version 1.0 * XMLParse用于解析spring配置文件 */ public class XMLParse { public static String getBasePackage(String xmlFile) { SAXReader saxReader = new SAXReader(); //maven的类路径是在target/li-springmvc/WEB-INF/classes/目录下 //通过类的加载路径-->获取到spring配置文件[对应的资源流] InputStream inputStream = XMLParse.class.getClassLoader().getResourceAsStream(xmlFile); try { //得到配置文件的文档 Document document = saxReader.read(inputStream); Element rootElement = document.getRootElement(); Element componentScanElement = rootElement.element("component-scan"); Attribute attribute = componentScanElement.attribute("base-package"); String basePackage = attribute.getText(); return basePackage; } catch (Exception e) { e.printStackTrace(); } return ""; } }

步骤四:开发MyWebApplicationContext            ,充当原生Spring容器              ,得到扫描类的全路径列表

即把指定目录包括子目录下的 java 类的全路径扫描到 ArrayList 集合中      ,以便之后反射      。是否需要反射         ,还要取决于类中是否添加了@Controller注解

(1)MyWebApplicationContext.java 实现自定义的 spring 容器              ,目前先完成扫描工作

package com.li.myspringmvc.context; import com.li.myspringmvc.xml.XMLParse; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.List; /** * @author 李 * @version 1.0 * MyWebApplicationContext 是我们自定义的spring容器 */ public class MyWebApplicationContext { //属性classFullPathList用于保存扫描包/子包的类的全路径 private List<String> classFullPathList = new ArrayList<>(); //该方法完成对自己的 spring容器的初始化 public void init() { //返回的是我们在容器文件中配置的base-package的value String basePackage = XMLParse.getBasePackage("myspringmvc.xml"); //这时你的 basePackage是像 com.li.controller,com.li.service 这样子的 //通过逗号进行分割包 String[] basePackages = basePackage.split(","); if (basePackages.length > 0) { //遍历这些包 for (String pack : basePackages) { scanPackage(pack); } } System.out.println("扫描后的路径classFullPathList=" + classFullPathList); } /** * 该方法完成对包的扫描 * @param pack 表示要扫描的包        ,如 "com.li.controller" */ public void scanPackage(String pack) { //得到包所在的工作路径[绝对路径] // (1)通过类的加载器      ,得到指定包的工作路径[绝对路径] // (2)然后用斜杠代替点=>如 com.li.controller=>com/li/controller URL url = this.getClass().getClassLoader() .getResource("/" + pack.replaceAll("\\.", "/")); // url=file:/D:/IDEA-workspace/li-springmvc/target/li-springmvc // /WEB-INF/classes/com/li/controller/ //System.out.println("url=" + url); //根据得到的路径               ,对其进行扫描          ,把类的全路径保存到 classFullPathList属性中 String path = url.getFile(); System.out.println("path=" + path); //在io中   ,把目录也视为一个文件 File dir = new File(path); //遍历 dir目录,因为可能会有[多个文件/子目录] File[] files = dir.listFiles(); for (File file : files) { if (file.isDirectory()) { //如果是目录                ,需要递归扫描 //pack加上下一级的目录名继续下一层的扫描 scanPackage(pack + "." + file.getName()); } else { //这时得到的文件可能是.class文件            ,也可能是其他文件 //就算是class文件,还需要考虑是否要注入到容器的问题 //目前先把所有文件的全路径都保存到集合中              ,后面注入对象到spring容器时再考虑过滤 String classFullPath = pack + "." + file.getName().replaceAll(".class", ""); classFullPathList.add(classFullPath); } } } }

(2)通过 MyDispatcherServlet 前端控制器来调用并初始化 spring 容器

//添加init方法              ,用于初始化spring容器 @Override public void init() throws ServletException { MyWebApplicationContext myWebApplicationContext = new MyWebApplicationContext(); myWebApplicationContext.init(); }

(3)启动tomcat   ,后台成功获取到了路径            ,测试成功               。

tomcat启动--加载了MyDispatcherServlet--通过该Servlet的init()生命周期方法初始化自定义的 spring 容器              ,同时调用自定义 spring 容器的 init 方法去扫描包

步骤五:完善MyWebApplicationContext(自定义 spring 容器)      ,实例化对象到容器中

将扫描到的类         ,在满足添加了注解的情况下              ,通过反射注入到 ioc 容器

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
网站应该怎么优化(网站怎样进行优化)