自定义的springMvc框架简单实现

最近通过学习前辈们的自定义的mini版springMvc框架实现案例,自己也模仿着学习了一遍,以此记录下来:

SpringMVC运行流程

 执行过程如图所示: 
⑴用户发送请求至前端控制器DispatcherServlet。 
⑵ DispatcherServlet收到请求调用HandlerMapping处理器映射器。 
⑶ 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。 
⑷ DispatcherServlet通过HandlerAdapter处理器适配器调用处理器。 
⑸ 执行处理器(Controller,也叫后端控制器)。 
⑹ Controller执行完成返回ModelAndView。 
⑺ HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。 
⑻ DispatcherServlet将ModelAndView传给ViewReslover视图解析器。 
⑼ ViewReslover解析后返回具体View。 
⑽ DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。 
⑾ DispatcherServlet响应用户。 
从上面可以看出,DispatcherServlet有接收请求,响应结果,转发等作用。有了DispatcherServlet之后,可以减少组件之间的耦合度。
 另外大家也可以看下我的另一个文章,SpringMVC中的九大组件的理解和源码

不多说直接上代码:

整体工程架构如下图:

这个项目是一个maven项目,下面是具体类的代码:

package com.lyh.annotation;

import java.lang.annotation.*;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 自动注入的注解
 * @create 2018-11-01 8:41
 * @since 1.7
 */
@Documented
@Target(ElementType.FIELD)//(字段)标志此注解可以修饰在哪些地方,类,成员变量,方法.
@Retention(RetentionPolicy.RUNTIME)//Annotation的生命周期,一般情况下,我们自定义注解的话,显然需要在运行期获取注解的一些信息。
public @interface MyAutowired {
    public String value() default "";
}

 

package com.lyh.annotation;

import java.lang.annotation.*;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 控制层的注解
 * @create 2018-11-01 8:36
 * @since 1.7
 */
@Documented
@Target(ElementType.TYPE)//作用于类上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
    public String value() default "";
}
package com.lyh.annotation;

import java.lang.annotation.*;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description  @Qualifier提供依赖注入
 * @create 2018-11-01 8:52
 * @since 1.7
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyQualifier {
    public String value();
}
package com.lyh.annotation;

import java.lang.annotation.*;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 对持久层的注解
 * @create 2018-11-01 8:50
 * @since 1.7
 */
@Documented//JavaDoc文档
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRepository {
    public String value() default "";
}
package com.lyh.annotation;

import java.lang.annotation.*;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 方法映射的注解
 * @create 2018-11-01 8:39
 * @since 1.7
 */
@Documented
@Target({ElementType.METHOD,ElementType.TYPE})//作用于类或者方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
    public String value();
}
package com.lyh.annotation;

import java.lang.annotation.*;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 对于映射参数的注解
 * @create 2018-11-01 8:55
 * @since 1.7
 */
@Documented
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestParam {
    public String value();
}
package com.lyh.annotation;

import java.lang.annotation.*;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description Service层的注解
 * @create 2018-11-01 8:38
 * @since 1.7
 */
@Documented
@Target(ElementType.TYPE)//作用于类上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
    public String value() default "";
}
package com.lyh.controller;

import com.lyh.annotation.MyAutowired;
import com.lyh.annotation.MyController;
import com.lyh.annotation.MyRequestMapping;
import com.lyh.annotation.MyRequestParam;
import com.lyh.service.TestService;

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

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 对类的描述
 * @create 2018-11-01 14:48
 * @since 1.7
 */
@MyController
@MyRequestMapping("test1")
public class TestController1 {

    @MyAutowired
    private TestService testService;

    @MyRequestMapping("test")
    public void myTest(HttpServletRequest req, HttpServletResponse resp, @MyRequestParam("param") String param) {
        try {
            resp.getWriter().write("TestController1: the param you send is :" + param);
            testService.printParam(param);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.lyh.dao;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 对类的描述
 * @create 2018-10-31 15:56
 * @since 1.7
 */
public interface UserDao {
    void insert();
}


package com.lyh.dao.impl;

import com.lyh.annotation.MyRepository;
import com.lyh.dao.UserDao;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 对类的描述
 * @create 2018-10-31 15:57
 * @since 1.7
 */
@MyRepository("userDaoImpl")
public class UserDaoImpl implements UserDao{

    @Override
    public void insert() {
        System.out.println("execute UserDaoImpl insert()");
    }
}
package com.lyh.service;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 对类的描述
 * @create 2018-11-01 14:41
 * @since 1.7
 */
public interface TestService {

    void printParam(String param);
}



package com.lyh.service.impl;

import com.lyh.annotation.MyAutowired;
import com.lyh.annotation.MyService;
import com.lyh.dao.UserDao;
import com.lyh.service.TestService;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description 对类的描述
 * @create 2018-11-01 14:43
 * @since 1.7
 */
@MyService
public class TestServiceImpl implements TestService {

    @MyAutowired
    UserDao userDao;


    @Override
    public void printParam(String param) {
        System.out.println("接收到的参数为:" + param);
        userDao.insert();
    }
}
package com.lyh.servlet;

import com.lyh.annotation.*;

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.Method;
import java.net.URL;
import java.util.*;
import java.util.logging.Logger;

/**
 * @author 633805 LYH
 * @version V1.0
 * @description MVC框架的请求分转
 * @create 2018-11-01 8:58
 * @since 1.7
 * Remark:继承HttpServlet,重写init方法、doGet、doPost方法
 */
public class MyDispatcherServlet extends HttpServlet {

    private Logger logger = Logger.getLogger("init");

    private Properties properties = new Properties();

    private List<String> classNames = new ArrayList<String>();

    private Map<String, Object> ioc = new HashMap<String, Object>();

    //handlerMapping的类型可以自定义为Handler

    private Map<String, Object> handlerMapping = new HashMap<String, Object>();

    private Map<String, Object> controllerMap = new HashMap<String, Object>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init();
        logger.info("初始化MyDispatcherServlet");
        //1.加载配置文件,填充properties字段
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        //2.根据properties,初始化所有相关联的类,扫描用户设定的包下面所有的类
        doScanner(properties.getProperty("scanPackage"));

        //3.拿到扫描到的类,通过反射的机制,实例化,并且放到ioc容器中(k-v  beanName  bean)
        doInstance();

        //4.自动化注入
        doAutowired();

        //5.初始化HandlerMapping(将url和method对应上)
        initHandlerMapping();

        doAutowired2();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        //注释掉父类的实现,不然会报错:405 Http method  POST  is not supported by ...
        //super.doPost(req, resp);
        logger.info("执行MyDispatcherServlet的doPost()方法");
        try {
            //处理请求
            doDispatch(req, resp);
        } catch (Exception e) {
            try {
                resp.getWriter().write("500!!! Server Exception");
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        //注释掉父类的实现,不然会报错:405 Http method  GET  is not supported by ...
        //super.doGet(req, resp);
        logger.info("在执行MyDispatcherServlet的doGet()方法");
        try {
            //处理请求
            doDispatch(req, resp);
        } catch (Exception e) {
            try {
                resp.getWriter().write("500!!! Server Exception");
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        if (handlerMapping.isEmpty()){
            return;
        }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        //去掉url前面的斜杆“/”,所有的@Myrequestmapping可以不用写斜杆“/”
        if (url.lastIndexOf('/') != 0) {
            url = url.substring(1);
        }
        if (!this.handlerMapping.containsKey(url)) {
            try {
                resp.getWriter().write("404 NOT FOUND");
                logger.info("404 NOT FOUND!");
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        Method method = (Method) this.handlerMapping.get(url);
        //获取方法的参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();

        //获取请求的参数
        Map<String, String[]> parameterMap = req.getParameterMap();

        //保存的参数值
        Object[] paramValues = new Object[parameterTypes.length];
        //方法的参数列表
        for (int i =0, len = parameterTypes.length; i < len; i ++) {
            //根据参数名称,做某些处理
            String requestParam = parameterTypes[i].getSimpleName();
            if ("HttpServletRequest".equals(requestParam)) {
                //参数类型已经明确,这边强转类型
                paramValues[i] = req;
                continue;
            }
            if ("HttpServletResponse".equals(requestParam)) {
                paramValues[i] = resp;
                continue;

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

                }
            }
        }
        //利用反射机制来调用
        try {
            //第一个参数是method所对应的实例  在ioc容器中
            method.invoke(this.controllerMap.get(url),paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 根据配置文件位置,读取配置文件中的配置信息,将其填充到properties字段
     * @param location  配置文件的位置
     */
    private void doLoadConfig(String location) {

        //将web.xml中的contextConfigLocation对应value值得文件加载到流里面
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location);
        try {
            //用Properties文件加载文件里的内容
            logger.info("读取"+location+"里面的文件");
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关流
            if (null != resourceAsStream) {
                try {
                    resourceAsStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 将指定包下扫描得到的类,添加到classNames字段中;
     * @param packageName 需要扫描的包名
     *
     */
    private void doScanner(String packageName) {
        URL url  =this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                //递归取包
                doScanner(packageName+"."+file.getName());
            } else {
                String className =packageName +"." +file.getName().replace(".class", "");
                classNames.add(className);
            }
        }
    }

    /**
     * 将classNames中的类实例化,经key-value:类名(小写)-类对象放入ioc字段中
     * Params:
     */
    private void doInstance() {

        if (classNames.isEmpty()) {
            return;
        }
        for (String className : classNames) {
            try {
                //把类搞出来,反射来实例化(只有加@Controller需要实例化)
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(MyController.class)) {
                    ioc.put(toLowerFirstWord(clazz.getSimpleName()),clazz.newInstance());
                } else if (clazz.isAnnotationPresent(MyService.class)){
                    MyService myService=clazz.getAnnotation(MyService.class);
                    String beanName=myService.value();
                    if ("".equals(beanName.trim())) {
                        beanName = toLowerFirstWord(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                    Class[] interfaces = clazz.getInterfaces();
                    for (Class<?> i : interfaces) {
                        ioc.put(i.getName(), instance);
                    }
                } else if(clazz.isAnnotationPresent(MyRepository.class)) {

                    MyRepository myRepository=clazz.getAnnotation(MyRepository.class);
                    String beanName=myRepository.value();
                    if ("".equals(beanName.trim())) {
                        beanName = toLowerFirstWord(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                    Class[] interfaces = clazz.getInterfaces();
                    for (Class<?> i : interfaces) {
                        ioc.put(i.getName(), instance);
                    }
                } else {
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    /**
     * 自动化的依赖注入
     */
    private void doAutowired() {
        if (ioc.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //包括私有的方法,在spring中没有隐私,@MyAutowired可以注入public、private字段
            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().trim();
                if ("".equals(beanName)) {
                    beanName = field.getType().getName();
                }
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    private void doAutowired2() {
        if (controllerMap.isEmpty()) {
            return;
        }
        for (Map.Entry<String,Object> entry : controllerMap.entrySet()) {
            //包括私有的方法,在spring中没有隐私,@MyAutowired可以注入public、private字段
            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().trim();
                if ("".equals(beanName)) {
                    beanName = field.getType().getName();
                }
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    /**
     * \初始化HandlerMapping(将url和method对应上)
     */
    private void initHandlerMapping() {
        if (ioc.isEmpty()) {
            return;
        }

        try {
            for (Map.Entry<String, Object> entry : ioc.entrySet()) {
                Class<? extends Object> clazz = entry.getValue().getClass();
                if(!clazz.isAnnotationPresent(MyController.class)){
                    continue;
                }
                //拼接url时,是Controller头的url拼上方法的url
                String baseUrl = "";
                if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                    MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
                    baseUrl = annotation.value();
                }
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                        continue;
                    }
                    MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                    String url = annotation.value();

                    url = (baseUrl + "/" +url).replaceAll("/+", "/");
                    handlerMapping.put(url, method);
                    controllerMap.put(url, clazz.newInstance());
                    System.out.println(url + "," + method);
                }
            }
        } catch (Exception e) {

            e.printStackTrace();
        }
    }

    /**
     * 将字符串中的首字母小写
     * @param name
     * @return
     */
    private String toLowerFirstWord(String name) {
        char[] charArray = name.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }
}

 application.properties文件:

scanPackage=com.lyh

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>MiniServlet</servlet-name>
		<servlet-class>com.lyh.servlet.MyDispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>application.properties</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>MiniServlet</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>

</web-app>

pom.xml主要引入的jar包 

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

 主要是dispatcherServlet分发管理器类的实现,接下来是使用本地tomcat搭载项目测试:

 

 到此springmvc的简单功能就模拟实现了。。。。

 

 

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值