2[springMvc]模拟Spring MVC实现web框架

Tomcat

前一篇笔记中介绍了Servlet以及Tomcat,servlet是一个行业的标准,tomcat在sevlet的标准上实现了它的标准,在tomcat中就是一堆的容器,其中servlet容器就是context容器,我们看下tomcat的sever.xml配置文件就可以知道一般我们不熟的应用都在context下面,context下面部署的就是我们的web应用,也就是sevlet容器,tomcat中的容器是这样分类的:
server容器—>Service容器–>Connector容器、Engine容器
Engine容器又有许多servlet容器也就是context容器,所以tomcat的启动过程是:
1.server容器;
2.Service容器;
3.Conector容器(BIO、NIO、NIO2);
4.context容器;
比如我们来看一段代码:

public static void main(String[] args) throws LifecycleException {


        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        Context context = tomcat.addWebapp("/", "D:\\study\\idea\\dev-project\\dev-springmvvc");
//        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Test.class);
//        Tomcat.addServlet(context, "dispatcherServlet", new DispatcherServlet(ac));
//        context.addServletMapping("/*", "dispatcherServlet");
        tomcat.start();
        tomcat.getServer().await();
    }

addWeApp

public Context addWebapp(String contextPath, String docBase) {
    //添加一个conext容器到tomcat中,这里是使用的内嵌版本的tomcat
    return this.addWebapp(this.getHost(), contextPath, docBase);
}
public Context addWebapp(Host host, String contextPath, String docBase) {
    LifecycleListener listener = null;

    try {
        //得到一个tomat非常重要的监听器,ConfigClass
        Class<?> clazz = Class.forName(this.getHost().getConfigClass());
        listener = (LifecycleListener)clazz.getConstructor().newInstance();
    } catch (ReflectiveOperationException var6) {
        throw new IllegalArgumentException(var6);
    }

//添加context(servlet)容器
    return this.addWebapp(host, contextPath, docBase, listener);
}
public Context addWebapp(Host host, String contextPath, String docBase, LifecycleListener config) {
    this.silence(host, contextPath);
    //创建一个Context容器
    Context ctx = this.createContext(host, contextPath);
    //设置路径
    ctx.setPath(contextPath);
    ctx.setDocBase(docBase);
    if (this.addDefaultWebXmlToWebapp) {
        ctx.addLifecycleListener(this.getDefaultWebXmlListener());
    }

    ctx.setConfigFile(this.getWebappConfigFile(docBase, contextPath));
    //将刚刚反射得到的监听器添加到context容器中
    ctx.addLifecycleListener(config);
    if (this.addDefaultWebXmlToWebapp && config instanceof ContextConfig) {
        ((ContextConfig)config).setDefaultWebXml(this.noDefaultWebXmlPath());
    }

//下面就表示context的上层容器是否是空的,如果是空的,这里先要去添加上层容器,再添加context容器
    if (host == null) {
        this.getHost().addChild(ctx);
    } else {
        host.addChild(ctx);
    }

    return ctx;
}

添加Enine容器

public Host getHost() {
    //先去得到Enine容器,没有就创建
    Engine engine = this.getEngine();
    if (engine.findChildren().length > 0) {
        return (Host)engine.findChildren()[0];
    } else {
        Host host = new StandardHost();
        host.setName(this.hostname);
        this.getEngine().addChild(host);
        return host;
    }
}
public Engine getEngine() {
    //Enine容器是在Service容器里面的,所以这里先去添加Service容器(不存在就添加)
    Service service = this.getServer().findServices()[0];
    if (service.getContainer() != null) {
        return service.getContainer();
    } else {
        Engine engine = new StandardEngine();
        engine.setName("Tomcat");
        engine.setDefaultHost(this.hostname);
        engine.setRealm(this.createDefaultRealm());
        service.setContainer(engine);
        return engine;
    }
}
//这里就是添加最上层的Server容器,然后把Service容器也添加进去
//tomcat这里的容器都是以Stardardxxx的,比如StardardServer、StardardService等等
public Server getServer() {
    if (this.server != null) {
        return this.server;
    } else {
        System.setProperty("catalina.useNaming", "false");
        this.server = new StandardServer();
        this.initBaseDir();
        this.server.setPort(-1);
        Service service = new StandardService();
        service.setName("Tomcat");
        this.server.addService(service);
        return this.server;
    }
}

所以tomcat启动就是一层一层的去添加容器,按照最上面说的容器启动顺序去添加,第一次添加容器的时候可能都需要去创建容器,一层一层的创建,当后面再调用addWebapp的时候就只需要把当前的context容器添加到Enine容器里面就行了,因为第一次都会创建Server 、Service、Engine容器。

Tomcat启动

public void start() throws LifecycleException {
    //得到Sever对象
    this.getServer();
    //得到Connector对象,作为http访问请求连接的对象,做监听接受请求
    this.getConnector();
    //容器启动
    this.server.start();
}
public Connector getConnector() {
    Service service = this.getService();
    if (service.findConnectors().length > 0) {
        return service.findConnectors()[0];
    } else if (this.defaultConnectorCreated) {
        return null;
    } else {
        Connector connector = new Connector("HTTP/1.1");
        connector.setPort(this.port);
        service.addConnector(connector);
        this.defaultConnectorCreated = true;
        return connector;
    }
}

下面这个方法是tomcat启动容器的核心方法,容器一层一层的启动,就是向内引爆的模式
先启动sever容器,再启动Service、Engine容器(Connector、Context),核心方法就是调用
startInternal,这个是一个模板设计模式,star方法是fianl的,每个容器启动都会调用这个star方法

public final synchronized void start() throws LifecycleException {
    if (!LifecycleState.STARTING_PREP.equals(this.state) && !LifecycleState.STARTING.equals(this.state) && !LifecycleState.STARTED.equals(this.state)) {
        if (this.state.equals(LifecycleState.NEW)) {
            this.init();
        } else if (this.state.equals(LifecycleState.FAILED)) {
            this.stop();
        } else if (!this.state.equals(LifecycleState.INITIALIZED) && !this.state.equals(LifecycleState.STOPPED)) {
            this.invalidTransition("before_start");
        }

        try {
            this.setStateInternal(LifecycleState.STARTING_PREP, (Object)null, false);
            this.startInternal();
            if (this.state.equals(LifecycleState.FAILED)) {
                this.stop();
            } else if (!this.state.equals(LifecycleState.STARTING)) {
                this.invalidTransition("after_start");
            } else {
                this.setStateInternal(LifecycleState.STARTED, (Object)null, false);
            }
        } catch (Throwable var2) {
            this.handleSubClassException(var2, "lifecycleBase.startFail", this.toString());
        }

    } else {
        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()}), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", new Object[]{this.toString()}));
        }

    }
}

其余的代码就不多说了,就是每个容器依次启动,server启动了,调用Service启动,依次启动。

模拟SpringMvc编写web框架

我们知道了sevlet的一些概念,我们可以通过内嵌的tomcat来编写一个简单的spring mvc的容器框架,首先我们知道spring mvc发展到现在创建要给Controller有很多中模式,比如:
1.实现Controller接口;
2.实现HttpRequestHandler ;
3.通过注解@Controller的方式;

那么这么多方式,它的处理肯定是很复杂的,首先你要清楚的是请求的是url中对应的是哪个类型的Controller,那么你才会去执行相对应的Controller中的方法,现在用的比较多的方式就是通过@Controller注解的方式,说白了注解的方式就是说spring在启动的过程中把所有的加了@Controller的注解的bean拿出来,循环里面的方法,如果加了@RequestMapping的注解的封装一个对象,然后加入一个map,key=请求的URI地址,value=就是封装的mapping方法,然后如果前端请求过来得到URI地址,然后去拿到这个封装的mapping方法,然后根据HttpServletRequest中的请求参数进行参数封装,最后封装完成了执行反射调用,调用完成过后通过HttpServletResponse将结果回写;这其中涉spring有一个非常重要的servlet就是DispatcherServlet,是它来完成的所有请求,只是请求的业务逻辑部分是交给了开发者,也就是实现了@Controller注解的类的处理方法。
但是spring从早期的版本到现在,mvc的实现已经经过了很多版本的迭代,出现了很多中不同的方式,那么它是怎么来兼容的呢?其实这里涉及到一个设计模式,就是适配器模式,就是spring会定义一个适配器接口,让所有的请求来适配,如果你适配到了某种模式,就进入这种模式的处理,所以里面涉及两个比较重要的概念就是HandlerMapping和HandlerAdapter,HandlerMapping就是去找到所有符合条件的Controller,然后放入map,HandlerAdapter就是通过请求的url找到对应的mapping,然后开始适配,适配到了就进入相对应的处理流程,去处理具体的业务,最后返回。简单来书主流程就是这样,我这里简单写了一个spring mvc的web框架,就是学习研究使用,首先项目结构如下:
在这里插入图片描述
代码放在gitee上,地址是:https://gitee.com/scjava/dev-project.git
有兴趣的可以去下载看下,这里主要看下几个比较重要的类:

handermapping:

//匹配mapping的接口
public interface HandlerMapping {


    Object gethandlerMapping(String requestUrl);

}

我这里实现了两种方式,一种以/beanName的方式,一种是注解的方式

@Component
public class BeanNameHandlerMapping implements InstantiationAwareBeanPostProcessor,HandlerMapping {

    private final Map<String, Controller> controllerMap = new ConcurrentHashMap<>(8);
    @Override
    public Object gethandlerMapping(String requestUrl) {
        return controllerMap.get(requestUrl);
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {

        if(beanName.startsWith("/")){
            controllerMap.put(beanName,(Controller)bean);
        }

        return true;
    }
}
@Component
public class AnnotationHandlerMapping implements InstantiationAwareBeanPostProcessor, HandlerMapping {

    private final Map<String, RequestMethodInfo> requestMethodInfoMap = new ConcurrentHashMap<>(8);

    @Override
    public Object gethandlerMapping(String requestUrl) {
        return requestMethodInfoMap.get(requestUrl);
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();
        if (beanClass.isAnnotationPresent(Controller.class)) {
            Method[] declaredMethods = beanClass.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
                    RequestMethodInfo info = new RequestMethodInfo();
                    info.setMethod(declaredMethod);
                    info.setTarget(bean);
                    info.setUrl(declaredMethod.getAnnotation(RequestMapping.class).value());
                    requestMethodInfoMap.put(info.getUrl(), info);
                }
            }
        }
        return true;
    }
}

利用bean的后置处理器来完成的

Adapter

//适配的接口
public interface HandlerAdapter {

    boolean isSuuport(Object object);

    Object handler(HttpServletRequest request, HttpServletResponse response, Object handler);
}

@Component
public class BeanNameHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean isSuuport(Object object) {
        return object instanceof Controller;
    }
    @Override
    public Object handler(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if(handler != null && handler instanceof Controller){
            Controller controller = (Controller) handler;
            return controller.handler(request,response);
        }

        return null;
    }
}

@Component
public class AnnotationHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean isSuuport(Object object) {
        return object instanceof RequestMethodInfo;
    }

    @Override
    public Object handler(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler == null || !(handler instanceof RequestMethodInfo)) {
            return null;
        }
        RequestMethodInfo methodInfo = (RequestMethodInfo) handler;
        Method method = methodInfo.getMethod();
        Object target = methodInfo.getTarget();

        Parameter[] targetPameters = method.getParameters();
        Map<String, String[]> paramMap = request.getParameterMap();//请求携带的参数
        Object[] argument = new Object[method.getParameterCount()];
        for (int i = 0; i < targetPameters.length; i++) {
            Parameter parameter = targetPameters[i];
            String parName = parameter.getName();
            for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
                if (parameter.isAnnotationPresent(RequestParam.class)) {
                    if (parameter.getAnnotation(RequestParam.class).value().equals(entry.getKey())) {
                        argument[i] = entry.getValue()[0];
                    }
                } else if (parName.equals(entry.getKey())) {
                    argument[i] = entry.getValue()[0];
                }

                if (ServletRequest.class.isAssignableFrom(parameter.getType())) {
                    argument[i] = request;
                }
                if (ServletResponse.class.isAssignableFrom(parameter.getType())) {
                    argument[i] = response;
                }
            }
        }
        try {
            Object result = method.invoke(target, argument);
            if (result instanceof String) {
                if ("forward".equals(((String) result).split(":")[0])) {
                    request.getRequestDispatcher(((String) result).split(":")[1]).forward(request, response);
                } else {
                   return result;
                }
            }
            if (method.isAnnotationPresent(ResponseBody.class)) {
                return JSON.toJSONString(result);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

return  null;
    }
}

DispatcherServlet


public class DispatcherServlet extends HttpServlet {

    Collection<HandlerMapping> handlerMappings;
    Collection<HandlerAdapter> handlerAdapters;

    public DispatcherServlet(){
        System.out.println("1111111111111111111111111111");
    }

    public DispatcherServlet(AnnotationConfigApplicationContext ac) {
        handlerMappings = ac.getBeansOfType(HandlerMapping.class).values();
        handlerAdapters = ac.getBeansOfType(HandlerAdapter.class).values();
    }

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

        Object handlerMapping = getHandlerMapping(req);
        if (handlerMapping == null) {
            throw new RuntimeException("未找到指定的mapping");
        }

        HandlerAdapter adapter = getHandlerAdapter(handlerMapping);
        if (adapter == null) {
            throw new RuntimeException("未找到指定的HandlerAdapter");
        }
        Object result = adapter.handler(req, resp, handlerMapping);

        PrintWriter writer = resp.getWriter();
        writer.println(result);
        writer.flush();
        writer.close();
    }


    private Object getHandlerMapping(HttpServletRequest request) {
        for (HandlerMapping handlerMapping : handlerMappings) {
            Object mapping = handlerMapping.gethandlerMapping(request.getRequestURI());
            if (mapping != null) {
                return mapping;
            }
        }
        return null;
    }

    private HandlerAdapter getHandlerAdapter(Object handlerMapping) {
        for (HandlerAdapter handlerAdapter : handlerAdapters) {
            boolean falg = handlerAdapter.isSuuport(handlerMapping);
            if (falg) {
                return handlerAdapter;
            }
        }
        return null;
    }
}

controller来测试:

@Controller
public class HellController {


    @RequestMapping("/test.do")
    public String test(@RequestParam("id")String id){
        System.out.println(id);
        return "ok";
    }

    @RequestMapping("/map.do")
    @ResponseBody
    public Map<String,String> map(){
        Map<String,String> data = new HashMap<>();
        data.put("id", "11");
        data.put("name", "abc");
        data.put("age", "34");
        return data;
    }

    @RequestMapping("/user.do")
    @ResponseBody
    public User user(){
        User user = new User();
        user.setAge(23);
        user.setId("123");
        user.setName("bml");
        return user;
    }
}
@Component("/index")
public class IndexController implements Controller {
    @Override
    public Object handler(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("controller test...");
        return "beanNameControllerTeesst";
    }
}

然后test代码:


@ComponentScan("com.springmvc")
public class Test {



    public static void main(String[] args) throws LifecycleException {


        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        Context context = tomcat.addWebapp("/", "D:\\study\\idea\\dev-project\\dev-springmvvc");
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Test.class);
        Tomcat.addServlet(context, "dispatcherServlet", new DispatcherServlet(ac));
        context.addServletMapping("/*", "dispatcherServlet");
        tomcat.start();
        tomcat.getServer().await();
    }
}

简单来说就是利用spring的ioc功能,将我的handlermapping注册到ioc中,是在bean的后置处理器中去完成的处理,然后请求过来会到DispatcherServlet中的doGet方法,然后doGet先去找到mapping,然后根据mapping去适配是那种模式的Controller,然后适配好了过后,就调用对应controller中的适配方法handler,
测试结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

SPI机制

这里有个SPI的机制,就是什么呢?我们文章的最开始不是说了tomcat启动加了一个监听器吗,这个监听器在tomcat容器启动完成过后,会触发一个事件,就是告诉spring,它启动完成了,如果它启动完成了,spring就可以启动了,所以这里利用了spi的机制,服务发现机制,我这里简单说下,就是在我们的项目里面写一个这样的类,实现了ServletContainerInitializer,然后tomcat启动完成会找到所有的ServletContainerInitializer实现类,然后回调里面的方法onStartup

public class SpringInit implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext servletContext) throws ServletException {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Test.class);

        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic dy = servletContext.addServlet("app",servlet);
        dy.setLoadOnStartup(1);
        dy.addMapping("*.do");
    }
}

我们把spring容器启动的逻辑放在这里,然后在resources下面配置一个服务的配置:
在这里插入图片描述
然后tomcat在启动完成过后就会在conext下部署的容器中去找到所有的sevices,然后回调实现了ServletContainerInitializer 的方法,这个tomcat是在那里调用的呢?
在这里插入图片描述
this.ok就表示容器启动完毕了,然后调用spi的接口,这个在tomcat的源码里面,有兴趣的可以根据tomcat启动容器的概念和顺序一步一步的去分析源码,我这个专题不是分析tomcat,是分析了springmvc,所以tomat中的源码只是大概找下有些相关的地方然后去简单分析下。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值