自己编写Spring mvc简易框架

学习java web开发的小伙伴,对spring MVC 不会陌生的。最近没事看了一下spring的源码,了解了一些框架的实现方法,再结合网上一些资料,自己编写了一个简易的MVC框架,现将记录下开发工程。
一、首先,我们先来介绍一下Spring的三个阶段,配置阶段、初始化阶段和运行阶段
配置阶段:主要是完成application.properties配置和Annotation配置。

初始化阶段:主要是加载并解析配置信息,然后,初始化IOC容器,完成容器的DI操作,已经完成HandlerMapping的初始化。

运行阶段:主要是完成Spring容器启动以后,完成用户请求的内部调度,并返回响应结果。
二、配置阶段
我采用的是maven管理项目。先来看pom.xml文件中的配置,只引用了servlet-api的依赖。

<dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>provided</scope>
    </dependency>

在web.xml文件中配置以下信息:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <servlet>
        <servlet-name>spring MVC</servlet-name>
        <servlet-class>com.xiao.servlet.MyDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLoaction</param-name>
            <param-value>application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring MVC</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

application.properties 配置

 scanPackage=com.xiao.demo

接下来,配置注解。
创建MyController 注解:

package com.xiao.annotation;

import java.lang.annotation.*;

/**
 * 注解在类上
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
    String value() default "";
}

创建MyService 注解:

package com.xiao.annotation;

import java.lang.annotation.*;

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

创建MyRequestMapping注解:

package com.xiao.annotation;

import java.lang.annotation.*;

/**
 * 注解在类和方法上
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
    String value() default "";
}

创建MyAutowired注解:

  package com.xiao.annotation;
   import java.lang.annotation.*;
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface MyAutowired {
        String value() default "";
    }

创建MyRequestParam注解:

   package com.xiao.annotation;

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
    /**
     * 表示参数的别名,必填
     * @return
     */
    String value();
}

三、初始化、运行阶段
创建GPDispatcherServlet类并继承HttpServlet,重写init()、doGet()和doPost()方法。

package com.xiao.servlet;

import com.xiao.annotation.MyAutowired;
import com.xiao.annotation.MyController;
import com.xiao.annotation.MyRequestMapping;
import com.xiao.annotation.MyService;

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

/**
 * @author xiaoxiaoping
 */
public class MyDispatcherServlet  extends HttpServlet {

    private static final long serialVersionUID = 1L;

    /**
     * 跟web.xml中param-name的值一致
     */
    private static final String LOCATION = "contextConfigLoaction";

    /**
     * 保存所有的配置信息
     */
    private Properties properties = new Properties();

    /**
     * 核心IOC容器,保存所有初始化的bean
     */
    private Map<String ,Object> ioc = new HashMap<String,Object>();

    /**
     * 保存所有的URL和方法的映射关系
     */
    private Map<String, Method> handelerMapping = new HashMap<String,Method>();

    /**
     * 保存所有臊面到的相关类名
     */
    private List<String> classNames = new ArrayList<String>();

    public MyDispatcherServlet() { super(); }

    /**
     * 初始化,加载配置文件
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {

        //加载配置文件
        doLoadConfig(config.getInitParameter(LOCATION));

        //扫描所有相关类
        doScanner(properties.getProperty("scanPackage"));

        //初始化所有相关类的实例,并保存到IOC容器
        doInstance();

        //依赖注入
        doAutowired();

        //构造HandleMapping
        initHandlerMapping();

        //等待请求,匹配URL,定位方法,反射调用执行
        //调用doGet或者doPost方法

        //提示信息
        System.out.println("xiaoMvc is init");
    }

    private void initHandlerMapping() {
        if(ioc.isEmpty()){return;}
        for(Map.Entry<String,Object> entry : ioc.entrySet()){
            Class <?> clazz = entry.getValue().getClass();
            if(!clazz.isAnnotationPresent(MyController.class)){continue;}
            String baseUrl = "";
            //获取controller的url配置
            if(clazz.isAnnotationPresent(MyRequestMapping.class)){
                MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                baseUrl = requestMapping.value();
            }

            Method [] methods = clazz.getMethods();
            for(Method method : methods){
                if(!method.isAnnotationPresent(MyRequestMapping.class)){continue;}
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
                String url = ("/"+baseUrl+"/"+requestMapping.value()).replaceAll("/+", "/");
                handelerMapping.put(url,method);
                System.out.println("mapped"+url+" , "+method);
            }

        }
    }

    private void doAutowired() {
        if(ioc.isEmpty()){return;}
        for(Map.Entry<String,Object> entry : ioc.entrySet()){
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for(Field field : fields){
                if(!field.isAnnotationPresent(MyAutowired.class)){continue;}
                MyAutowired autowired = field.getAnnotation(MyAutowired.class);
                String beanName = autowired.value();
                if("".equals(beanName.trim())){
                    beanName = lowerFirstCase(field.getType().getSimpleName());
                }
                //设置私有属性的访问权限
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    private void doInstance() {
        if(classNames.size()==0){return;}
        try {
            for(String className : classNames) {
                Class<?> clazz = Class.forName(className);
                if(clazz.isAnnotationPresent(MyController.class)){
                    //默认将首字母小写作为beanName
                    String beanName = lowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName,clazz.newInstance());
                }else if(clazz.isAnnotationPresent(MyService.class)){
                    MyService service = clazz.getAnnotation(MyService.class);
                    String beanName = service.value();
                    //如果用户设置了名字,就用用户自己设置的
                    if("".equals(beanName)){
                        beanName = lowerFirstCase( clazz.getSimpleName() );
                    }
                    ioc.put(beanName,clazz.newInstance());
                }else{
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doScanner(String scanPackage) {

        URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.","/"));
        File dir = new File(url.getFile());
        for(File file : dir.listFiles()){
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else{
                classNames.add(scanPackage+"."+file.getName().replace(".class", ""));
            }
        }

    }

    private void doLoadConfig(String location) {
        //把web.xml中的contextConfigLocation对应value值的文件加载到流里面
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location);
        try {
            //用Properties文件加载文件里的内容
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关流
            if(null!=resourceAsStream){
                try {
                    resourceAsStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

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

    /**
     *执行业务处理
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500!! Server Exception");
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        if(this.handelerMapping.isEmpty()){ return; }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath,"").replaceAll("/+","/");
        if(!this.handelerMapping.containsKey(url)){
            resp.getWriter().write("404 not Found");
            return;
        }
        Method method = this.handelerMapping.get(url);
        Class<?>[] parameterTypes = method.getParameterTypes();
        //获取请求的参数
        Map<String,String[]> parameterMap = req.getParameterMap();
        //保存参数值
        Object[] paramValues = new Object[parameterTypes.length];
        for(int i = 0;i < parameterTypes.length; i++){
            Class parameterType = parameterTypes[i];
            if(parameterType == HttpServletRequest.class){
                paramValues[i] = req;
                continue;
            }else if(parameterType == HttpServletResponse.class){
                paramValues[i] = resp;
                continue;
            }else if(parameterType == String.class){
                for(Map.Entry<String,String[]> param : parameterMap.entrySet()){
                    String value =Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                    paramValues[i]=value;
                }
            }
        }
        try {
            String beanName = lowerFirstCase(method.getDeclaringClass().getSimpleName());
            method.invoke(this.ioc.get(beanName), paramValues);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            System.out.println("此处接收被调用方法内部未被捕获的异常");
            Throwable t = e.getTargetException();// 获取目标异常
            t.printStackTrace();
        }

    }

    private String lowerFirstCase(String str){
        char [] chars = str.toCharArray();
        chars [0] += 32;
        return String.valueOf(chars);
    }

}

四、测试实例

service类

    package com.xiao.demo.service.impl;
    
    import com.xiao.annotation.MyService;
    
    @MyService
    public class TestServiceImpl{
        public String query(String name, String age) {
            return name+"年龄"+age;
        }
    }
controller类
package com.xiao.demo;

import com.xiao.annotation.*;
import com.xiao.demo.service.impl.TestServiceImpl;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@MyController
@MyRequestMapping("/test")
public class HelloController {
    @MyAutowired
    TestServiceImpl testService;

    @MyRequestMapping("/hello")
    public void test1(HttpServletRequest request, HttpServletResponse response,
                      @MyRequestParam("param") String param){
        System.out.println( testService.query("xiaoping","30"));

        try {
            response.getWriter().write(  " param:"+param);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

至此,一个简单的spring mvc 简易框架完成。
在此给出源码地址:https://gitee.com/longsis/xiaoMVC.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值