欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

纯手写Springmvc框架 注释详解篇

程序员文章站 2022-04-07 18:01:20
项目场景:DispatcherServlet继承HttpServlet实现纯手写springmvc框架前言:提示:大神请绕道,此文章分享于一起共同奋斗的我们理解springmvc框架 在此感谢尚学堂雷老师!!! 进入主题:创建注解: Controller,RequestMapping,RequestParampackage com.yuanjw.annotation;import java.lang.annotation.*;/** * @Target(ElementType....

项目场景:

DispatcherServlet继承HttpServlet实现纯手写springmvc框架

前言:

提示:大神请绕道,此文章分享于一起共同奋斗的我们理解springmvc框架 在此感谢尚学堂雷老师!!!


进入主题:

创建注解: Controller,RequestMapping,RequestParam

package com.yuanjw.annotation;

import java.lang.annotation.*;

/**
 * @Target(ElementType.TYPE):元注解(类上起作用)
 * @Retention(RetentionPolicy.RUNTIME):元注解(运行时有效)
 * @Documented被Javadoc记录
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
    /**
     * 使用注解之后里面可以传一个值 @Conntroller
     * @return
     */
    String value() default "";
}


package com.yuanjw.annotation;

import java.lang.annotation.*;
/**
 * @Target({ElementType.TYPE,ElementType.METHOD})作用在类上 方法上
 * @Retention(RetentionPolicy.RUNTIME) 运行时有效
 * @Documented doc记录
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    /**
     * @RequestMapping("addUser")
     */
    String value() default "";
}


package com.yuanjw.annotation;

import java.lang.annotation.*;

/**
 * @Target(ElementType.PARAMETER)作用于参数
 * @Retention(RetentionPolicy.RUNTIME)运行时有效
 * @Documented 文档记录
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    /**
     * 表示参数的别名  必填
     *
     * @return
     */
    String  value();
}



创建前端控制器:DispatcherServlet

package com.yuanjw.core;

import com.yuanjw.annotation.Controller;
import com.yuanjw.annotation.RequestMapping;

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.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * 前端控制器
 */
public class DispatcherServlet extends HttpServlet {
    //存放配置文件
    private Properties properties=new Properties();
    //存放类的完全限定名
    private List<String> classNames = new ArrayList<>();
    //创建一个ioc容器
    private Map<String,Object> ioc = new HashMap<>();
    //控制器映射器
    private Map<String,Method> handlerMapping = new HashMap<>();
    //控制器对象集合
    private Map<String,Object> controllerMap = new HashMap<>();


    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        //1.加载application.properties
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2.初始化所有相关的类 扫描用户设定的包下面的所有类
        doScanner(properties.getProperty("basePackageScan"));
        //3.拿到扫描类利用反射机制实例化 创建对象 并放到ioc容器中 beanName默认是首字母小写
        doInstance();
        //4.初始化Urk和method(HandlerMapping)
        initHandlerMapping();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //处理请求
        Object doDispatch = doDispatch(req,resp);
        //此刻处理请求转发
        if(null!=doDispatch){
        String path ="/"+doDispatch.toString();
        req.getRequestDispatcher(path).forward(req,resp);
        }
    }

    /**
     * 请求分发
     * @param req
     * @param resp
     * @return
     */
    private Object doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        if(handlerMapping.isEmpty()) {
            throw new RuntimeException("url not found");
        }
        //得到请求Url
        String uri = req.getRequestURI();
        //得到发布项目的路径
        String contextPath = req.getContextPath();
        uri=uri.replace(contextPath+"/","");
        uri = uri.substring(0,uri.lastIndexOf("."));
        //根据Uri从handlerMapping取方法
        Method method = handlerMapping.get(uri);
        //获取方法的所有参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        //获取请求的参数
        Map<String,String[]> parameterMap=req.getParameterMap();
        //保存参数值
        Object[] paramValues = new Object[parameterTypes.length];
        //方法参数列表
        for (int i = 0; i < parameterTypes.length; i++) {
            //根据参数名称,做某些处理
            String simpleName = parameterTypes[i].getSimpleName();
            //类型已明确 这边强制转换类型
            if(simpleName.equals("HttpServletRequest")){
                paramValues[i]=req;
                continue;
            }
            if(simpleName.equals("HttpServletResponse")){
                paramValues[i]=resp;
                continue;
            }

            if(simpleName.equals("String")){
                for (Map.Entry<String,String[]> param: parameterMap.entrySet()) {
                    String value =Arrays.toString(param.getValue()).replaceAll("\\[|\\]","");
                    paramValues[i]=value;
                }
            }

        }
        //利用反射机制来调用
        Object obj = null;
        try {
            obj=  method.invoke(this.controllerMap.get(uri),paramValues);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 初始化Urk和method(HandlerMapping)
     */
    private void initHandlerMapping()  {
        //从ioc容器里面取创建好的对象,循环判断对象里面的类的和方法上面是否添加了注解
        if(ioc.isEmpty())return;
        for (Object o: ioc.values()) {
            Class<?> aClass = o.getClass();
            String baseUrl = "";
            //判断这个类里是否有@RequestMapping注解
            if(aClass.isAnnotationPresent(RequestMapping.class)){
                //得到这个注解对象,并取出里面的值
                RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
                baseUrl = annotation.value();

            }else{
                continue;
            }
            //解析对象里面的方法
            Method[] methods = aClass.getMethods();
            if(methods!=null&&methods.length!=0){
                for (Method method:methods){
                    //判断方法有没有注解
                    if (method.isAnnotationPresent(RequestMapping.class)){
                        RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                        String url = annotation.value();
                        url=baseUrl+"/"+url;
                        handlerMapping.put(url,method);
                        try {
                            controllerMap.put(url,aClass.newInstance());
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 拿到扫描类利用反射机制实例化 创建对象 并放到ioc容器中 beanName默认是首字母小写
     */
    private void doInstance() {
        //判断classNames不为空
        if(classNames.isEmpty())return;
        for (String className:classNames ) {
            Class<?> clazz = null;
            try {
                clazz = Class.forName(className);
                //此处判断是否有注解
                if(!clazz.isAnnotationPresent(Controller.class)){
                    continue;
                }else{
                    Object instance = clazz.newInstance();
                    String key =firstbeanNameToLowerCase(instance.getClass().getSimpleName());
                    //放到ioc容器
                    ioc.put(key,instance);
                }
            } catch (Exception e) {
                e.printStackTrace();
                //如果一个类出现异常要保证其他类也可以解析
                continue;
            }

        }
    }

    /**
     * 初始化所有相关联的类
     * @param basePackageScan com.yuanjw.controller
     *   com.yuanjw.controller要通过File找 需要替换为 com/yuanjw/controller
     */
    private void doScanner(String basePackageScan) {
        String packagePath=basePackageScan.replace(".","/");
        URL url = this.getClass().getResource("/"+packagePath);
        //得到类文件夹的路径
        String path= url.getFile();
        //构造File文件夹
        File file = new File(path);
        File[] files = file.listFiles();
        if(files!=null&&files.length>0){
            for (File f:files){
                if (f.isDirectory()){
                    doScanner(basePackageScan+"."+f.getName());
                }else{
                    //UserController.class  className=com.yuanjw.controller.UserController
                    String className = basePackageScan+"."+f.getName().replace(".class","");
                    classNames.add(className);
                }
            }
        }
    }

    /**
     * 加载application.properties并解析
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        String path = contextConfigLocation.substring(10, contextConfigLocation.length());
        InputStream is = this.getClass().getResourceAsStream("/"+path);
        //读取到properties中
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 把字符串的首字母小写
     */
    private String firstbeanNameToLowerCase(String beanName){
        char[] charArray = beanName.toCharArray();
        charArray[0] +=32;
        return String.valueOf(charArray);
    }
}




web.xml配置:

<?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">
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <!--配置前端控制器-->
    <servlet>
        <servlet-name>mySpringMvc</servlet-name>
        <servlet-class>com.yuanjw.core.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mySpringMvc</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
</web-app>

配置文件(properties):

#类似于springmvc.xml
basePackageScan=com.yuanjw.controller

测试:创建controller , jsp

package com.yuanjw.controller;

import com.yuanjw.annotation.Controller;
import com.yuanjw.annotation.RequestMapping;
import com.yuanjw.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
@RequestMapping("user")
public class UserController {
    @RequestMapping("addUser")
    public String addUser(HttpServletRequest request, HttpServletResponse response){
        System.out.println("addUser");
        return "adduser.jsp";
    }
    @RequestMapping("updateUser")
    public String updateUser(HttpServletRequest request, HttpServletResponse response, @RequestParam("name")String name){
        System.out.println("addUser");
        return "adduser.jsp";
    }
}

<%--
  Created by IntelliJ IDEA.
  User: yuanjw1
  Date: 2020/12/28
  Time: 21:24
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
success
</body>
</html>

结构:
纯手写Springmvc框架  注释详解篇
感谢查阅!

本文地址:https://blog.csdn.net/yuanjiangwei255/article/details/111875461