首页 > 编程语言 > 详细

自定义SpringMVC实现

时间:2020-07-02 22:33:10      阅读:55      评论:0      收藏:0      [点我收藏+]

首先要知道springmvc主要流程:

当用户,也是就是请求送达过来的时候,

1.前端控制器会获取,

2.请求处理映射器,返回执行链接

3.获取执行适配器适配,交给执行器

4.返回modelandview给其前端控制器

5.前端控制器把信息给视图解析器进行渲染

6.渲染的视图传给客户

所以根据这个流程进行以下操作:

1.在tomcat启动时加载web.xml也就要对前端控制器

DispatcherServlet进行初始化,也就是更具加载的springMVC.xml进行初始化
2.定义相关的注解,完成相关位置标记比如ioc和controller等标记(
我建议模仿Spring做一个init方法完成初始化包括以下
  public void init(ServletConfig config) throws ServletException {
        //1.加载配置文件  springmvc.properties
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);
        //2.扫描相关类
        System.out.println(properties.getProperty("scanPackge"));
        doScan(properties.getProperty("scanPackge"));
        //3.初始化bean对象(ioc)
        doInstance();
        //4.依赖注入(di)
        doAutoWired();
        //5构造一个Handler
        ininHandlerMapping();

        System.out.println("badfisher mvc 初始化 成功!!!");
    }

 

 
我是几个注解放在一个框里,但是一个注解其实是一个类
/**
 * author: Badfsiher(yhy)
 * date 2020/6/17 21:12
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
@Documented
public @interface MyAutowired {
    String value() default "";

}

/**
 * author: Badfsiher(yhy)
 * date 2020/6/28 20:21
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface MyController {
    String value() default "";
}

/**
 * author: Badfsiher(yhy)
 * date 2020/6/17 21:12
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface MyRequestMapping {
    String value() default "";
}

/**
 * author: Badfsiher(yhy)
 * date 2020/6/17 20:51
 */

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

}

 

对于注解的自定义可以看我spring内的随笔。
 
3.加载过程中我们同时需要实现spring的核心ioc和aop。
完成自定义ioc容器初始化以及类的注入。
ioc部分
    //文件获取
    private void doLoadConfig(String contextConfigLocation) {

        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //    扫描
//    pathPackage 会是全类名 com.badfisher.demo这样的递归扫描-->替换磁盘路径

    private void doScan(String pathPackage){
        String scanPathPackage = Thread.currentThread().getContextClassLoader().getResource("").getPath()+ pathPackage.replaceAll("\\.","/");
        File pack = new File(scanPathPackage);
        System.out.println(scanPathPackage);
        File[] files = pack.listFiles();
        for (File file :files) {
            if (file.isDirectory()){
                doScan(pathPackage +"."+ file.getName());
            }else if (file.getName().endsWith(".class")){
                String className = pathPackage +"."+file.getName().replaceAll(".class","");
                classNames.add(className);
            }

        }

    }


//反射完成对对象创建
    private void doInstance(){
        if(classNames.size() == 0) return;
        try{
            for (int i =0 ; i < classNames.size();i++){
                String className = classNames.get(i);

                //反射
                Class<?> myClazz = Class.forName(className);
                //区分注解
                if(myClazz.isAnnotationPresent(MyController.class)){
                    //MyController获取类名并直接那类名小写
                    String simpleName = lowerFirst(myClazz.getSimpleName());
                    Object o = myClazz.getDeclaredConstructor().newInstance();
                    iocMap.put(simpleName,o);

                }else if (myClazz.isAnnotationPresent(MyService.class)){
                    MyService myService = myClazz.getAnnotation(MyService.class);
                    //MyService 获取注解的值
                    String beanName = myService.value();
                    Object o = myClazz.getDeclaredConstructor().newInstance();
                    if (!"".equals(beanName.trim())){
                        iocMap.put(beanName,o);

                    }else {
                        String simpleName = lowerFirst(myClazz.getSimpleName());
                        iocMap.put(simpleName,o);
                    }
                    //service 往往存在接口,再以接口名称放入一份id到iocMap
                    Class<?>[] interfaces = myClazz.getInterfaces();
                    for(int j = 0;j < interfaces.length; j++){
                        Class<?> ainterface = interfaces[j];
                        //以接口名称放入
                        iocMap.put(ainterface.getName(),o);

                    }
                }else{
                    continue;
                }


            }
        }catch (Exception e){
            System.out.println("注解类加载失败!!!");
            e.printStackTrace();

        }



    }

接下来是对注解解析实现我们要的注解功能

   private void doAutoWired() {
        if (iocMap.isEmpty()) return;

        for (Map.Entry<String,Object> entry:iocMap.entrySet()) {
            //注入对象
            Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
            //便利并判断
            for (int i = 0; i < declaredFields.length; i++) {
                Field declaredField = declaredFields[i];
                //不存在跳出
                if(!declaredField.isAnnotationPresent(MyAutowired.class)){
                    continue;
                }

                MyAutowired autowired = declaredField.getAnnotation(MyAutowired.class);
                String beanName = autowired.value();
                if ("".equals(beanName.trim())){
                    beanName = declaredField.getType().getName();
                }
                //赋值
                declaredField.setAccessible(true);
                try {
                    declaredField.set(entry.getValue(),iocMap.get(beanName));
                } catch (IllegalAccessException e) {
                    System.out.println("MyAutowired 注入失败!!!");
                    e.printStackTrace();
                }

            }
            
        }

    }

 

4.初始化springMVC的组件,建立处理url和method的联系
也即对HandlerMapping的初始化。
//最重要的环节,完成url和methrd建立联系
    private void ininHandlerMapping() {
        if (iocMap.isEmpty()) return;
        for (Map.Entry<String,Object> entry:iocMap.entrySet()) {
            //获取ioc当前便利对象的class类型
            Class<?> myClass = entry.getValue().getClass();

            if(!myClass.isAnnotationPresent(MyController.class)){
                continue;
            }
            String baseUrl = "";
            if (myClass.isAnnotationPresent(MyRequestMapping.class)){
                MyRequestMapping myRequestMapping = myClass.getAnnotation(MyRequestMapping.class);
                baseUrl = myRequestMapping.value();//"/demo"

            }
            //获取方法
            Method[] methods = myClass.getMethods();
            for (int i = 0; i < methods.length; i++) {
                Method method = methods[i];
                if (!method.isAnnotationPresent((MyRequestMapping.class))){
                    continue;
                }
                MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
                String methodUrl = myRequestMapping.value();

                String URL = baseUrl + methodUrl;
                //建立联系
                //封装自定的hadler
                Handler handler = new Handler(entry.getValue(),method,Pattern.compile(URL));
//                handlerMap.put(URL,method);
                //计算参数位置
                Parameter[] parameters = method.getParameters();
                for (int j = 0; j < parameters.length; j++) {
                    Parameter parameter = parameters[j];

                    if (parameter.getType() == HttpServletResponse.class || parameter.getType() == HttpServletRequest.class){
//                       //HttpServletResponse  HttpServletRequest 获取类名
                        handler.getParaIndexMapping().put(parameter.getType().getSimpleName(),j);
                    }else{
                        handler.getParaIndexMapping().put(parameter.getName(),j);
                    }

                }
                handlerMap.add(handler);

            }

        }


    }

完成绑定之后实现dopost逻辑

   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //需求处理根据url找到对应的method方法进行调取
        //获取url
//        String requestURI = req.getRequestURI();
//        Method method = handlerMap.get(requestURI);
        //反射调用需要传入对象,需要传入参数,此处无法调用,没有缓存对象
//        method.invoke();

        Handler handler = getHandler(req);
        if (handler == null){
            resp.getWriter().write("404 not found !");
            return;
        }
        //参数绑定
        //获取所有参数
        Class<?>[] parameterTypes =handler.getMethod().getParameterTypes();
        //参数数组反射调用
        Object[] paraValues = new Object[parameterTypes.length];
        //参数数组放值
        Map<String,String[]> parameterMap = req.getParameterMap();
        //遍历request中的参数
        for (Map.Entry<String,String[]> param:parameterMap.entrySet()) {
            //name=1&name=2 name[1.2]
            String value = StringUtils.join(param.getValue(),","); //1,2
            //如果匹配成功,填充
            if (!handler.getParaIndexMapping().containsKey(param.getKey())){continue;}
            Integer index = handler.getParaIndexMapping().get(param.getKey());
            paraValues[index] = value;

        }

        //放入 indexHttpServletRequestr  indexHttpServletResponse
        int indexHttpServletRequestr = handler.getParaIndexMapping().get(HttpServletRequest.class.getSimpleName()); //0
        paraValues[indexHttpServletRequestr] = req;
        int indexHttpServletResponse = handler.getParaIndexMapping().get(HttpServletResponse.class.getSimpleName());//1
        paraValues[indexHttpServletResponse] = resp;




//        最后的handler事物·method属性
        try {
            handler.getMethod().invoke(handler.getController(),paraValues);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }


    }

    private Handler getHandler(HttpServletRequest req) {
        if (handlerMap.isEmpty()){ return null; }
        String requestURI = req.getRequestURI();
        for (Handler handler : handlerMap) {
            Matcher matcher = handler.getPattern().matcher(requestURI);
            if (!matcher.matches()){continue;}
            return handler;

        }
        return null;
    }

我的handler定义

package com.badfisher.mvcFramework.pojo;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * author: Badfsiher(yhy)
 * date 2020/6/29 23:17
 * 封装handler相关信息
 */
public class Handler {
    private  Object controller;//method.invoke

    private Method method;

    private Pattern pattern;//url 支持正则

    private Map<String,Integer> paraIndexMapping;//参数顺序 参数绑定

    public Handler(Object controller, Method method, Pattern pattern) {
        this.controller = controller;
        this.method = method;
        this.pattern = pattern;
        this.paraIndexMapping = new HashMap<>();
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Pattern getPattern() {
        return pattern;
    }

    public void setPattern(Pattern pattern) {
        this.pattern = pattern;
    }

    public Map<String, Integer> getParaIndexMapping() {
        return paraIndexMapping;
    }

    public void setParaIndexMapping(Map<String, Integer> paraIndexMapping) {
        this.paraIndexMapping = paraIndexMapping;
    }
}

如此就完成我们整体的简易SpringMVC的框架

照例我也会给上我的项目git地址  https://github.com/yhybadfisher/badfisherOwer     badfisherMVC.rar

自定义SpringMVC实现

原文:https://www.cnblogs.com/badfisher/p/13227509.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!