美文网首页Spring源码走读
Spring之动手实现SpringMVC功能

Spring之动手实现SpringMVC功能

作者: 南乡清水 | 来源:发表于2018-08-25 21:00 被阅读433次

    1 简介

    SpringMVC大家应该耳熟能详,只要是做Java网站开发的小伙伴,都会使用的框架。SpringMVC以 DispatcherServlet 为核心,负责协调和组织不同组件以完成请求处理并返回响应的工作,实现了MVC模式。接下来我们从 该框架的流程 来整理设计思路,最后自己实现一个mvc框架.

    2 SpringMVC运行流程

    springmvc的流程如下:

    流程图

    API说明

    DispatcherServlet

    Spring提供的前端控制器,所有的请求都有经过它来统一分发。在DispatcherServlet将请求分发给Controller之前,需要借助于Spring提供的HandlerMapping定位到具体的Controller。

    HandlerMapping

    完成客户请求到Controller映射,包括拦截器部分内容

    Controller

    Controller将处理用户请求,调用业务层接口分析,并返回ModelAndView对象给DispatcherServlet前端控制器,ModelAndView中包含了模型(Model)和视图(View)

    ViewResolver

    Spring提供的视图解析器(ViewResolver)在Web应用中查找View对象,会根据当前的视图渲染引擎(JSP FreeMarker Thymeleaf)来渲染视图,返回给前端

    流程概述

    • 1 用户发送请求至前端控制器DispatcherServlet
    • 2 DispatcherServlet收到请求调用HandlerMapping处理器映射器
    • 3 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
    • 4 DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
    • 5 执行处理器(Controller,也叫后端控制器)。
    • 6 Controller执行完成返回ModelAndView
    • 7 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
    • 8 DispatcherServlet将ModelAndView传给ViewReslover视图解析器
    • 9 ViewReslover解析后返回具体View
    • 10 DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
    • 11 DispatcherServlet响应用户

    3 MVC之九大组件

    SpringMVC中的Servlet一共有三个层次,分别是HttpServletBean、FrameworkServlet和 DispatcherServlet。
    HttpServletBean直接继承自java的HttpServlet,其作用是将Servlet中配置的参数设置到相应的属性;
    FrameworkServlet初始化了WebApplicationContext,DispatcherServlet初始化了自身的9个组件。

    DispatcherServlet初始化方法

    protected void onRefresh(ApplicationContext context) {
            initStrategies(context);
        }
    
    protected void initStrategies(ApplicationContext context) {
       initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            initHandlerMappings(context);
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            initViewResolvers(context);
            initFlashMapManager(context);
    }
    

    具体9个组件介绍如下:

    • 1 HandlerMapping

    用来查找Handler,在SpringMVC中会有很多请求,每个请求都需要一个Handler处理,具体接收到一个请求之后使用哪个Handler进行处理呢?这就是HandlerMapping需要做的事。

    • 2 HandlerAdapter

      从名字上看,它就是一个适配器。因为SpringMVC中的Handler可以是任意的形式,只要能处理请求就ok,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。

    • 3 HandlerExceptionResolver

    其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?这就需要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。具体来说,此组件的作用是根据异常设置ModelAndView,之后再交给render方法进行渲染。

    • 4 ViewResolver

    ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。

    • 5 RequestToViewNameTranslator

    ViewName是根据ViewName查找View,但有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。RequestToViewNameTranslator在Spring MVC容器里只可以配置一个,所以所有request到ViewName的转换规则都要在一个Translator里面全部实现。

    • 6 LocaleResolver

      解析视图需要两个参数:一是视图名,另一个是Locale。视图名是处理器返回的,Locale是从哪里来的?这就是LocaleResolver要做的事情。LocaleResolver用于从request解析出Locale,Locale就是zh-cn之类,表示一个区域,有了这个就可以对不同区域的用户显示不同的结果。SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。

    • 7 ThemeResolver

      用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、如图片、css样式等。SpringMVC的主题也支持国际化,同一个主题不同区域也可以显示不同的风格。SpringMVC中跟主题相关的类有 ThemeResolver、ThemeSource和Theme。主题是通过一系列资源来具体体现的,要得到一个主题的资源,首先要得到资源的名称,这是ThemeResolver的工作。然后通过主题名称找到对应的主题(可以理解为一个配置)文件,这是ThemeSource的工作。最后从主题中获取资源就可以了。

    • 8 MultipartResolver

    用于处理上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File,如果上传多个文件,还可以调用getFileMap得到FileName->File结构的Map。此组件中一共有三个方法,作用分别是判断是不是上传请求,将request包装成MultipartHttpServletRequest、处理完后清理上传过程中产生的临时资源。

    • 9 FlashMapManager

    用来管理FlashMap的,FlashMap主要用在redirect中传递参数。

    3.2 SpringMVC的设计思路

    MVC类图

    为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息。通过web.xml中加载我们自己写的DispatcherServlet和读取配置文件。

    DispatcherServlet是整个Spring MVC的核心。它负责接收HTTP请求组织协调Spring MVC的各个组成部分。其主要工作有以下三项:

    1.截获符合特定格式的URL请求。
    2.初始化DispatcherServlet上下文对应的WebApplicationContext,并将其与业务层、持久化层的WebApplicationContext建立关联。
    3.初始化Spring MVC的各个组成组件,并装配到DispatcherServlet中。

    4 动手实现SpringMVC

    项目目录结构如下:


    项目结构.PNG

    注解

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    public @interface ComponentScan {
    
        String[] basePackages() default {};
    }
    
    @Target({java.lang.annotation.ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Controller {
        String value() default "";
    }
    
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
    @Documented
    public @interface Order {
    
        int value() default Integer.MAX_VALUE;
    
    }
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RequestMapping {
    
        String value() default "";
    
        RequestMethod[] method() default {};
    }
    
    public enum RequestMethod {
    
        GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
    
    }
    
    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RequestParam {
    
        String value() default "";
    }
    
    @Target({java.lang.annotation.ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Service {
    
        String beanName() default "";
    }
    
    

    这些注解没什么好解释的,平时大家开发中常用

    IOC方法

    @ComponentScan(basePackages = "com.nicky")
    public class ApplicationContext {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        private volatile static boolean beanIsInitialized = false;
    
        private List<String> classNames = new CopyOnWriteArrayList<>();
    
        private Map<String, Object> beanFactory = new ConcurrentHashMap<>();
    
        private ApplicationContext() {
            synchronized (ApplicationContext.class) {
                if (!beanIsInitialized) {
                    beanIsInitialized = true;
                    //扫描包
                    scanPackage(this.getClass().getAnnotation(ComponentScan.class).basePackages()[0]);
                    //实例化
                    instance();
                    //依赖注入
                    dependencyInjection();
    
                } else {
                    throw new RuntimeException("ApplicationContext is only be created by singleton");
    
                }
            }
        }
    
        private static class ApplicationContextHolder{
    
            private static final ApplicationContext CONTEXT = new ApplicationContext();
        }
    
        public synchronized static ApplicationContext getApplicationContext() {
            if (!beanIsInitialized) {
                return ApplicationContextHolder.CONTEXT;
            }
            return null;
        }
    
        public Object getBean(String beanName) {
            Object o = beanFactory.get(beanName);
            if (o == null) {
                throw new RuntimeException("bean not found");
            }
            return o;
        }
    
        public <T> T getBean(String beanName, Class<T> cls) {
            return cls.cast(getBean(beanName));
        }
    
        public Map<String, Object> getBeanFactory (){
            return beanFactory;
        }
    
        private void dependencyInjection() {
    
            if (CollectionUtils.isEmpty(beanFactory.entrySet())) {
                logger.error("instance has not been over");
                return;
            }
            beanFactory.forEach((k, v) ->{
                Class cls = v.getClass();
                if (cls.isAnnotationPresent(Controller.class) || cls.isAnnotationPresent(Service.class)) {
                    Arrays.stream(cls.getDeclaredFields()).forEach(field -> {
                        if (field.isAnnotationPresent(Autowired.class) && classNames.contains(field.getType().getName())) {
                            field.setAccessible(true);
                            try {
                                field.set(v, beanFactory.get(camelNameSpell(field.getType().getSimpleName())));
                            } catch (IllegalAccessException e) {
                                logger.error("error dependencyInjection", e);
                            }
                        }
                    });
                    //beanFactoryAware applicationContextAware
                    if (BeanFactoryAware.class.isAssignableFrom(cls)) {
                        try {
                            Method method = cls.getMethod("setBeanFactory", Map.class);
                            method.invoke(v, beanFactory);
                        } catch (ReflectiveOperationException e) {
                            logger.error("beanFactoryAware error", e);
                        }
                    }
                }
            });
    
    
        }
    
        /**
         * 销毁方法,用于释放资源
         */
        public void close() {
            beanFactory.clear();
            beanFactory = null;
        }
    
        private void scanPackage(String basePackage) {
    
            URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
            File[] classFileList = getClassFileList(url.getPath());
            if (classFileList != null) {
                for (File file : classFileList) {
                    if (file.isDirectory()) {
                        scanPackage(basePackage + "." + file.getName());
                    }else {
                        String className = basePackage+"."+file.getName().replace(".class", "");
                        classNames.add(className);
                    }
                }
            }
        }
    
        //获取该路径下所遇的class文件和目录
        private static File[] getClassFileList(String filePath) {
            return new File(filePath).listFiles(
                    file -> file.isFile() && file.getName().endsWith(".class") || file.isDirectory());
        }
    
        private void instance() {
            if (CollectionUtils.isEmpty(classNames)) {
                logger.error("scan path error!");
                return;
            }
    
            for (String className : classNames) {
                try {
                    Class<?> clazz = Class.forName(className);
                    if (clazz.isInterface()) {
                        continue;
                    }
                    if (clazz.isAnnotationPresent(Controller.class)) {
                        Controller annotation = clazz.getAnnotation(Controller.class);
                        Object instance = clazz.newInstance();
                        //不指定beanName,采用驼峰命名
                        Object o = beanFactory.putIfAbsent(StringUtils.isEmpty(annotation.value()) ?
                                camelNameSpell(clazz.getSimpleName()) :
                                annotation.value(), instance);
                        if (o != null) {
                            throw new RuntimeException(String.format("duplicated beanName %s", clazz.getSimpleName()));
                        }
                    }
    
                    if (clazz.isAnnotationPresent(Service.class)) {
                        Service service = clazz.getAnnotation(Service.class);
                        Object instance = clazz.newInstance();
                        Object o = beanFactory.putIfAbsent(StringUtils.isEmpty(service.beanName()) ?
                                camelNameSpell(clazz.getSimpleName()) :
                                service.beanName(), instance);
                        if (o != null) {
                            throw new RuntimeException(String.format("duplicated beanName %s", clazz.getSimpleName()));
                        }
                    }
                } catch (ReflectiveOperationException e) {
                    logger.error("instance reflect error", e);
                }
            }
        }
    
        private static String camelNameSpell(String name) {
            if (StringUtils.isEmpty(name)) {
                return name;
            }
            return String.valueOf(name.charAt(0)).toLowerCase() + name.substring(1);
    
        }
    
    }
    

    Aware 用于获取Aware名称前面的对象

    public interface ApplicationContextAware{
    
        void setApplicationContext(ApplicationContext applicationContext);
    
    }
    
    public interface BeanFactoryAware {
    
        void setBeanFactory(Map<String, Object> beanFactory);
    }
    

    controller 和service

    @Controller("testController")
    @RequestMapping("/user")
    public class UserController {
    
        private final Logger logger = LoggerFactory.getLogger(getClass());
        
        @Autowired
        private UserService userService;
    
        @RequestMapping("/query.do")
        public void queryUserInfo(HttpServletRequest request, HttpServletResponse response, @RequestParam("userName") String userName) {
            logger.info("userName: {}", userName );
            try {
                PrintWriter pw = response.getWriter();
                String result = userService.query(userName);
                pw.write(result);
            }
            catch (IOException e) {
                logger.error("test", e);
            }
        }
    
        @RequestMapping("/insert.do")
        public String insert(String param) {
            return "success";
        }
    }
    
    public interface UserService {
        
        String query(String param);
        
    }
    
    @Service(beanName = "userService")
    public class UserServiceImpl implements UserService {
        
        @Override
        public String query(String param) {
           return "User:{name = nicky, age = 23}" ;
        }
    
    }
    
    

    接下来是核心类
    DispatcherServlet

    @NoArgsConstructor
    @Service
    public class DispatcherServlet extends FrameworkServlet {
    
        private static final long serialVersionUID = -6961498140472266321L;
    
        private static final Logger logger = LoggerFactory.getLogger(DispatcherServlet.class);
        
        private Map<String, HandlerMapping> handlerMappings = new ConcurrentHashMap<>();
        
        private static Properties prop;
    
        private final String handlerAdapter = "spring.bean.handlerAdapter";
    
        static {
    
            InputStream is = DispatcherServlet.class.getResourceAsStream("/application.properties");
            prop = new Properties();
            try {
                prop.load(is);
            } catch (IOException e) {
                logger.error("static block error", e);
            }
        }
    
    
        @Override
        public void init(ServletConfig config) {
            //处理映射
            handlerMapping();
            
        }
        
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) {
            processRequest(request, response);
            ServletRequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            doPost(requestAttributes.getRequest(), requestAttributes.getResponse());
        }
    
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response){
            String uri = request.getRequestURI();
            String context = request.getContextPath();
            String path = uri.replace(context, "");
            logger.warn("access path :{}", path);
    
            String className = prop.getProperty(handlerAdapter);
            Class<?> cls = null;
            try {
                cls = Class.forName(className);
            } catch (ClassNotFoundException e) {
                logger.error("adapter not found", e);
            }
            Service annotation = cls.getAnnotation(Service.class);
            HandlerAdapter adapter = applicationContext.getBean(annotation.beanName(), HandlerAdapter.class);
            HandlerMapping handlerMapping = handlerMappings.get(path);
            if (handlerMapping == null) {
                try {
                    PrintWriter pw = response.getWriter();
                    pw.write("404, page not found");
                    return;
                } catch (IOException e) {
                    e.getLocalizedMessage();
                }
            }
            adapter.handle(request, response, handlerMapping);
            
        }
        
        private void handlerMapping() {
    
            for (Map.Entry<String, Object> entry : applicationContext.getBeanFactory().entrySet()) {
                Object instance = entry.getValue();
                Class<?> clazz = instance.getClass();
    
                if (clazz.isAnnotationPresent(Controller.class)) {
                    Controller annotation = clazz.getAnnotation(Controller.class);
                    RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                    String rootPath = requestMapping.value();
    
                    for (Method method : clazz.getMethods()) {
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            RequestMapping subMethod = method.getAnnotation(RequestMapping.class);
                            String methodPath = subMethod.value();
                            handlerMappings.putIfAbsent(rootPath + methodPath, new HandlerMapping(annotation.value(), method));
                        }
                    }
                }
            }
        }
    
    }
    
    public abstract class FrameworkServlet extends HttpServlet {
    
        protected ApplicationContext applicationContext;
    
        public FrameworkServlet() {
            applicationContext = ApplicationContext.getApplicationContext();
        }
    
        /**
         * 保证单例servlet下的请求线程安全
         */
        protected final void processRequest(HttpServletRequest request, HttpServletResponse response) {
            RequestContextHolder.resetRequestAttributes();
            RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));
        }
    
    }
    

    首先 静态块中的内容会预加载,即获取resouce下application.properties文件的内容,存入对象中。并且创建DispatcherServlet对象的时候,会获取到applicationcontext。

    当http请求过来的时候,第一次会触发DispatcherServlet的init(ServletConfig config)方法,这里的handlerMapping()方法会在初始化的时候获取controller中的URL链接并保存。

    之后get请求会进入doGet方法,因为考虑到servlet是单例的,所以请求会有并发线程安全问题,所以我们通过ThreadLocal去解决,在processRequest(request, response);方法中,最后会调用RequestContextHolder这个类的方法,具体代码如下:

    public final class RequestContextHolder {
    
        private static final ThreadLocal<ServletRequestAttributes> requestHolder =
                new NamedThreadLocal<>("Request attributes");
    
    
        private static final ThreadLocal<ServletRequestAttributes> inheritableRequestHolder =
                new NamedInheritableThreadLocal<>("Request context");
    
    
        public static void resetRequestAttributes() {
            requestHolder.remove();
            inheritableRequestHolder.remove();
        }
    
        public static void setRequestAttributes(ServletRequestAttributes attributes) {
            setRequestAttributes(attributes, false);
        }
    
        public static void setRequestAttributes(ServletRequestAttributes attributes, boolean inheritable) {
            if (attributes == null) {
                resetRequestAttributes();
            }
            else {
                if (inheritable) {
                    inheritableRequestHolder.set(attributes);
                    requestHolder.remove();
                }
                else {
                    requestHolder.set(attributes);
                    inheritableRequestHolder.remove();
                }
            }
        }
    
    
        public static ServletRequestAttributes getRequestAttributes() {
            ServletRequestAttributes attributes = requestHolder.get();
            if (attributes == null) {
                attributes = inheritableRequestHolder.get();
            }
            return attributes;
        }
    
    }
    
    
    @Data
    public class ServletRequestAttributes {
    
        private final HttpServletRequest request;
    
        private final HttpServletResponse response;
    
    }
    
    public class NamedInheritableThreadLocal<T> extends InheritableThreadLocal<T> {
    
        private final String name;
    
    
        /**
         * Create a new NamedInheritableThreadLocal with the given name.
         * @param name a descriptive name for this ThreadLocal
         */
        public NamedInheritableThreadLocal(String name) {
            Assert.hasText(name, "Name must not be empty");
            this.name = name;
        }
    
        @Override
        public String toString() {
            return this.name;
        }
    
    }
    
    public class NamedThreadLocal<T> extends ThreadLocal<T> {
    
        private final String name;
    
    
        /**
         * Create a new NamedThreadLocal with the given name.
         * @param name a descriptive name for this ThreadLocal
         */
        public NamedThreadLocal(String name) {
            Assert.hasText(name, "Name must not be empty");
            this.name = name;
        }
    
        @Override
        public String toString() {
            return this.name;
        }
    
    }
    

    当进入doPost方法的时候,会通过HandlerAdapter对象来处理具体的请求,通过分析 HandlerMapping 查询,当前请求的地址是否可以分发到已有的controller中,如果没有则,直接向前端页面报错 404,如果有,则通过 HandlerAdapter继续处理

    @Service(beanName = "handlerAdapter")
    public class HttpRequestHandlerAdapter implements HandlerAdapter, BeanFactoryAware {
    
        private Map<String, Object> beanFactory;
        
        @Override
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, HandlerMapping handler) {
    
            Method method = handler.getMethod();
            Class<?>[] paramList = method.getParameterTypes();
            
            Object[] args = new Object[paramList.length];
            
            //获取接口的实现
            Map<String, Object> resolvers = getBeanInterfaceImpl(HandlerMethodArgumentResolver.class);
            //todo HandlerMethodArgumentResolver 可以排序
            int paramIndex = 0, i = 0;
            for (Class<?> paramClazz : paramList) {
                for (Map.Entry<String, Object> entry : resolvers.entrySet()) {
                    HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)entry.getValue();
                    MethodParameter warp = new MethodParameter(paramClazz, paramIndex, method);
                    if (resolver.supportsParameter(warp)) {
                        args[i++] = resolver.resolveArgument(request, response, warp);
                    }
                }
                paramIndex++;
            }
            Map<String, Object> model = new HashMap<>(1);
            ModelAndView view = new ModelAndView();
            Object obj;
            try {
                Object instance = beanFactory.get(handler.getControllerBeanName());
                obj = method.invoke(instance, args);
                //如果调用方法没有传入HttpServletResponse参数,则自己封装
                if (!Arrays.asList(paramList).contains(HttpServletResponse.class)) {
                    PrintWriter writer = response.getWriter();
                    writer.write(obj.toString());
                }
                //移除ThreadLocal副本
                RequestContextHolder.resetRequestAttributes();
            } catch (ReflectiveOperationException | IOException e) {
                view.setStatus(HttpStatus.EXPECTATION_FAILED);
                model.put("result", e.getLocalizedMessage());
                view.setModel(model);
                return view;
            }
            view.setStatus(HttpStatus.OK);
            model.put("result",obj);
            view.setModel(model);
            return view;
        }
        
        private Map<String, Object> getBeanInterfaceImpl(Class<?> interfaceType) {
            
            Map<String, Object> result = new HashMap<>();
            beanFactory.forEach((k, v) ->{
                if (interfaceType.isAssignableFrom(v.getClass())) {
                    result.put(k, v);
                }
            });
            return result;
        }
    
        @Override
        public void setBeanFactory(Map<String, Object> beanFactory) {
            this.beanFactory = beanFactory;
        }
    }
    
    public interface HandlerAdapter {
        
        ModelAndView handle(HttpServletRequest request, HttpServletResponse response, HandlerMapping handler);
    }
    
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public class HandlerMapping {
    
        private String controllerBeanName;
    
        private Method method;
    
    }
    
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class ModelAndView {
    
        private HttpStatus status;
    
        private Map<String, Object> model;
    
    }
    

    HttpRequestHandlerAdapter为HandlerAdapter的具体实现,它同时实现了BeanFactoryAware,主要是为了获取BeanFactory对象

    handle方法中首先会获取到需要请求的HandlerMapping,它包含了controller的信息,然后对 controller对应路由的方法中所持有的参数做拦截处理即 HandlerMethodArgumentResolver 对象:

    public interface HandlerMethodArgumentResolver {
        
        boolean supportsParameter(MethodParameter methodParameter);
        
        Object resolveArgument(HttpServletRequest request, HttpServletResponse response, MethodParameter methodParameter);
    
        default int getOrderAnnotationValue(){
            Order annotation = this.getClass().getAnnotation(Order.class);
            if (annotation == null) {
                return Integer.MAX_VALUE;
            }
            return annotation.value();
        }
    }
    
    public abstract class AbstractOrderResolver implements HandlerMethodArgumentResolver {
    
        protected int order = Integer.MAX_VALUE;
    
        public AbstractOrderResolver() {
            setOrder();
        }
    
        protected boolean paramConfirm(MethodParameter parameter, Class targetClass) {
    
            try {
                return ClassUtils.isAssignable(targetClass, parameter.getParamType());
            }
            catch (Exception ex) {
                return false;
            }
        }
    
        public void setOrder(){
            order = this.getOrderAnnotationValue();
        }
    
        public int getOrder() {
            return order;
        }
    }
    
    @Service(beanName = "handlerRequestArgumentResolver")
    @Order(10)
    public class HandlerRequestArgumentResolver extends AbstractOrderResolver {
        
        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            return paramConfirm(methodParameter, ServletRequest.class);
        }
        
        @Override
        public Object resolveArgument(HttpServletRequest request, HttpServletResponse response, MethodParameter methodParameter) {
            System.out.println("ServletRequest.class");
            return request;
        }
    
    }
    
    @Service(beanName = "handlerResponseArgumentResolver")
    @Order(1)
    public class HandlerResponseArgumentResolver extends AbstractOrderResolver {
        
        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            return paramConfirm(methodParameter, ServletResponse.class);
        }
        
        @Override
        public Object resolveArgument(HttpServletRequest request,
                HttpServletResponse response, MethodParameter methodParameter) {
            System.out.println("ServletResponse.class");
            return response;
        }
        
    }
    
    @Service
    public class RequestParamArgumentResolver extends AbstractOrderResolver {
        
        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            
            Annotation[][] an = methodParameter.getMethod().getParameterAnnotations();
            
            Annotation[] paramAns = an[methodParameter.getParamIndex()];
            
            for (Annotation paramAn : paramAns) {
                if (RequestParam.class.isAssignableFrom(paramAn.getClass())) {
                    return true;
                }
            }
            return false;
        }
        
        @Override
        public Object resolveArgument(HttpServletRequest request, HttpServletResponse response, MethodParameter methodParameter) {
    
            Annotation[][] an = methodParameter.getMethod().getParameterAnnotations();
            Annotation[] paramAns = an[methodParameter.getParamIndex()];
            
            for (Annotation paramAn : paramAns) {
                if (RequestParam.class.isAssignableFrom(paramAn.getClass())) {
                    RequestParam rp = (RequestParam)paramAn;
                    String value = rp.value();
                   return request.getParameter(value);
                }
            }
            return null;
        }
    
        @Override
        public void setOrder() {
            order = 100;
        }
    }
    
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public class MethodParameter {
    
        private Class<?> paramType;
    
        private int paramIndex;
    
        private Method method;
    
    }
    

    会筛选对应的方法中的参数,进行拦截处理。最后通过执行反射来获取具体的内容

    我们先借用springboot的IOC容器来构建我们的SpringMVC框架

    @Configuration
    public class ContainerConfig {
    
    
        @Bean
        public ServletRegistrationBean MyServlet1(){
            ServletRegistrationBean bean = new ServletRegistrationBean();
            bean.setServlet(new DispatcherServlet());
            bean.getUrlMappings().clear();
            bean.addUrlMappings("/user/*");
            return bean;
        }
    
    }
    

    启动项目,访问页面

    成功:


    success.PNG

    失败:


    error.PNG

    如果你不使用springboot,你可以使用web.xml来指定servlet来实现

    当然我们也可以通过springboot基于SPI来实现,通过Servlet3.0的规范来实现

    public class ServletConfig implements ServletContainerInitializer {
    
        @Override
        public void onStartup(Set<Class<?>> c, ServletContext container) throws ServletException {
    
            System.out.println("启动加载自定义的MyServletContainerInitializer");
            ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcherServlet", "com.nicky.servlet.DispatcherServlet");
            dispatcher.setLoadOnStartup(1);
            dispatcher.addMapping("/");
    
        }
    }
    
    spi.PNG

    在javax.servlet.ServletContainerInitializer加入spi.ServletConfig,然后打成jar包,最后向springmvc的项目打成war包,将spi的jar包放入WEB-INF\lib目录下重启服务即可,spi入门可参考我的文章框架基础之SPI机制

    Reference

    【SpringMVC】9大组件概览

    相关文章

      网友评论

      • 6a21a3b1a1d8:老师您好,我是北航出版社图书策划编辑,看到您有很多技术博文,想问下您有兴趣写书吗?望回复,谢谢。
        6a21a3b1a1d8:@南乡清水 好的 谢谢回复 您后续如果有想法的话可以随时联系我 也可以先加我qq 978581473方便以后联系 大神从写书开始:smile:
        南乡清水:暂时没有,写的都比较浅显
      • 就爱骚韦:老哥稳,群里过来的
        南乡清水:@就爱骚韦 谢谢支持

      本文标题:Spring之动手实现SpringMVC功能

      本文链接:https://www.haomeiwen.com/subject/fqypbftx.html