当前位置:网站首页>手写mini版MVC框架

手写mini版MVC框架

2020-12-07 21:05:01 程序猿欧文

目录
  • 1, Springmvc基本原理流程
  • 2,注解开发
    • 编写测试代码:
    • 目录结构:
  • 3,编写自定义DispatcherServlet中的初始化流程:
    • 3.1 加载配置文件
    • 3.2 扫描相关的类,扫描注解
    • 3.3 初始化Bean对象(实现IOC容器,基于注解)
    • 3.4 实现依赖注入:
    • 3.5 构造一个handleMapping处理器映射器
  • 4,测试一下:
    • 完整的pom文件:
  • 5,改造initHandleMapping()
    • 5.1 为什么改造?
    • 5.2 新建Handler类
    • 5.3 修改initHandleMapping()
  • 6, 请求处理开发 doPost():
  • 7,测试:

1, Springmvc基本原理流程

image-20201207204748987
SpringMvc本质上就是对Servlet的封装。

因为创建一个Maven项目,然后在pom文件中增加一个依赖:

<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <!-- 部署在服务器时,不使用这个servlet-api 而使用tomcat的--> <scope>provided</scope></dependency>

2,创建DispatcherServlet,并注册到web.

package com.dxh.edu.mvcframework.servlet;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;public class DxhDispatcherServlet extends HttpServlet { /**  * 接收处理请求  */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   }}

web.

<!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>dxhmvc</servlet-name> <servlet-class>com.dxh.edu.mvcframework.servlet.DxhDispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>springmvc.properties</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dxhmvc</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping></web-app>

2,注解开发

因为要使用到注解,所以首先要自定义几个注解:

这里就不赘述如何自定义注解了,详情请看:https://www.cnblogs.com/peida/archive/2013/04/24/3036689.html

Controller注解:

@Documented@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface DxhController { String value() default "";}

Service注解:

@Documented@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface DxhService { String value() default "";}

RequestMapping注解:

@Documented@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface DxhRequestMapping { String value() default "";}

Autowired注解:

@Documented@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface DxhAutowired { String value() default "";}

编写测试代码:

测试代码我们放在同项目中的com.dxh.demo包中:

package com.dxh.demo.controller;import com.dxh.demo.service.IDemoService;import com.dxh.edu.mvcframework.annotations.DxhAutowired;import com.dxh.edu.mvcframework.annotations.DxhController;import com.dxh.edu.mvcframework.annotations.DxhRequestMapping;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@DxhController@DxhRequestMapping("/demo")public class DemoController { @DxhAutowired private IDemoService demoService; /** * URL:/demo/query */ @DxhRequestMapping("/query") public String query(HttpServletRequest request, HttpServletResponse response, String name){ return demoService.get(name); }}
package com.dxh.demo.service;public interface IDemoService { String get(String name);}
package com.dxh.demo.service.impl;import com.dxh.demo.service.IDemoService;import com.dxh.edu.mvcframework.annotations.DxhService;@DxhService("demoService")public class IDemoServiceImpl implements IDemoService { @Override public String get(String name) { System.out.println("Service实现类中的Name:"+ name); return name; }}

目录结构:

image-20201207204807765


3,编写自定义DispatcherServlet中的初始化流程:

在创建好的DxhDispatcherServlet中重写init()方法,并在init方法中做初始化配置:

  1. 加载配置文件  springmvc.properties
  2. 扫描相关的类——扫描注解
  3. 初始化Bean对象(实现IOC容器,基于注解)
  4. 实现依赖注入
  5. 构造一个handleMapping处理器映射器,将配置好的url和method建立映射关系
@Overridepublic void init(ServletConfig config) throws ServletException { //1. 加载配置文件 springmvc.properties String contextConfigLocation = config.getInitParameter("contextConfigLocation"); doLoadConfig(contextConfigLocation); //2. 扫描相关的类——扫描注解 doScan(""); //3. 初始化Bean对象(实现IOC容器,基于注解) doInstance(); //4. 实现依赖注入 doAutoWired(); //5. 构造一个handleMapping处理器映射器,将配置好的url和method建立映射关系 initHandleMapping(); System.out.println("MVC 初始化完成"); //6. 等待请求进入处理请求}

以及5个空方法,这篇文章自定义MVC框架其实就是需要对这5个步骤的编写。

//TODO 5,构造一个映射器private void initHandleMapping() {}//TODO 4,实现依赖注入private void doAutoWired() {}//TODO 3,IOC容器private void doInstance() {}//TODO 2,扫描类private void doScan(String scanPackage) {}//TODO 1,加载配置文件private void doLoadConfig(String contextConfigLocation) {}

3.1 加载配置文件

  1. 首先在resource目录中创建一个配置文件——springmvc.properties
    image-20201207204836251
    表示要扫描com.dxh.demo下的所有注解。

  2. 然后在web.中进行配置:
    image-20201207204850314

这样,就可以通过config.getInitParameter("contextConfigLocation")获得这个路径。

image-20201207204903303

  1. DxhDispatcherServlet中定义一个属性,我们把加载后的配置文件中的信息,存储在Properties 中
private Properties properties = new Properties();;
//1,加载配置文件private void doLoadConfig(String contextConfigLocation) { //根据指定路径加载成流: InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); try { properties.load(resourceAsStream); } catch (IOException e) { e.printStackTrace(); }}

3.2 扫描相关的类,扫描注解

  1. 上一步骤,我们已经把需要扫描的包存在Properties中,所以这里需要取出:
//2. 扫描相关的类——扫描注解doScan(properties.getProperty("scanPackage"));
  1. 把扫描到的类型的全类名存在一个List中缓存起来,等待使用,在DxhDispatcherServlet中定义一个list:
//缓存扫描到的类的全类名private List<String> classNames = new ArrayList<>();
  1. 从配置文件中我们得到了一个需要扫描的包名(com.dxh.demo),我们需要根据classPath+包名,来得到它实际上在磁盘上存的路径,然后递归,直到把所有的该包下(包括子包...)所有的类文件(.class结尾)。然后存在在List classNames中
//2,扫描类//scanPackage :com.dxh.demo package--->磁盘的文件夹(File)private void doScan(String scanPackage) { //1.获得classPath路径 String clasPath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); //2.拼接,得到scanPackage在磁盘上的路径 String scanPackagePath= clasPath + scanPackage.replaceAll("\\.","/"); File pack = new File(scanPackagePath); File[] files = pack.listFiles(); for (File file : files) { if (file.isDirectory()){ //package //递归 doScan(scanPackage+"."+file.getName()); //com.dxh.demo.controller }else if(file.getName().endsWith(".class")){ String className = scanPackage + "." + file.getName().replace(".class", ""); classNames.add(className); } }}

3.3 初始化Bean对象(实现IOC容器,基于注解)

上一步骤我们把扫描到的类的全类名放到了,list中,那么本次步骤需要遍历整个list:

  1. 遍历List,依次得到所有的全类名
  2. 通过反射得到类对象
  3. 根据类对象判断有没有注解,并区分controllerservice
    1. controller,它的id此处不做过多处理,不取value了,用类的首字母小写作为id,保存到IOC容器中。
    2. service,service层往往是有接口的,再以接口名为id再存入一份bean到ioc,便于后期根据接口类型注入
  4. 完成

代码实现:

//IOC容器private Map<String,Object> ioc = new HashMap<>();
//3,IOC容器//基于classNames缓存的类的全限定类名,以及反射技术,完成对象创建和管理private void doInstance() { if (classNames.size()==0) return; try{ for (int i = 0; i < classNames.size(); i++) { String className = classNames.get(i); //com.dxh.demo.controller.DemoController //反射 Class<?> aClass = Class.forName(className); //区分controller ,区分service if (aClass.isAnnotationPresent(DxhController.class)){ //controller的id此处不做过多处理,不取value了,用类的首字母小写作为id,保存到IOC容器中 String simpleName = aClass.getSimpleName();//DemoController String lowerFirstSimpleName = lowerFirst(simpleName); //demoController Object bean = aClass.newInstance(); ioc.put(lowerFirstSimpleName,bean); }else if (aClass.isAnnotationPresent(DxhService.class)){ DxhService annotation = aClass.getAnnotation(DxhService.class); //获取注解的值 String beanName = annotation.value(); //指定了id就以指定的id为准 if (!"".equals(beanName.trim())){ ioc.put(beanName,aClass.newInstance()); }else{ //没有指定id ,首字母小写 String lowerFirstSimpleName = lowerFirst(aClass.getSimpleName()); ioc.put(lowerFirstSimpleName,aClass.newInstance()); } //service层往往是有接口的,再以接口名为id再存入一分bean到ioc,便于后期根据接口类型注入 Class<?>[] interfaces = aClass.getInterfaces(); for (Class<?> anInterface : interfaces) { //以接口的类名作为id放入。 ioc.put(anInterface.getName(),aClass.newInstance()); } }else { continue; } } }catch (Exception e){ e.printStackTrace(); }}

3.4 实现依赖注入:

上一步骤把所有需要加载的bean,存入了ioc Map中,此时,我们就需要遍历这个map然后依次得到每个bean对象,然后判断对象中有没有被@****DxhAutowired修饰的属性。

  1. 遍历ioc这个map,得到每个.........

版权声明
本文为[程序猿欧文]所创,转载请带上原文链接,感谢
https://my.oschina.net/mikeowen/blog/4779994