Handwritten Spring MVC

The above four articles have introduced and written the life cycle of spring bean. Now let's start writing Spring MVC

In Spring MVC, we only write one C layer, that is, the Controller control layer (there is no parent-child container)

As we all know, Spring MVC has a CPU called dispatcher Servlet, which implements HttpServlet as a Servlet

Since it is a Servlet, it must follow the Servlet specification.

The servers we usually know are tomcat and jetty, and these two containers also implement the Servlet specification.

When the container runs, javax. X under META-INF/services will be called servlet. The ServletContainerInitializer file contains object methods. This object must implement the interface ServletContainerInitializer, and the things done in this interface can realize zero configuration and start a spring web project

Let's take a look at the process of handwritten spring MVC. We need to package and introduce the spring project we wrote

What needs to be done to start tomcat? We start tomcat in the way of Spring boot

@XxSpringBootApplication
public class SimpleMVCApplication {

    public static void main(String[] args) throws Exception{
        XxSpringApplication.run(SimpleMVCApplication.class);
    }
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@XxConfiguration
public @interface XxSpringBootApplication {
}

When the run method is executed, the current class will be passed in, and then our spring container will be initialized. Put it in the cache

public static void run(Class clazz){
    try {
        XxAnnotationConfigApplicationContext springContext = new XxAnnotationConfigApplicationContext(clazz);
        XxLocalCache.CONTEXT_CACHE.put("springContext",springContext);
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(80);
        Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
        context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());
        tomcat.start();
        tomcat.getServer().await();
    }catch (Exception e){

    }
}

After starting tomcat, it will execute the interface of Servlet specification, get the xxdispatcher Servlet and put it into the container

public class XxWebApplicationInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) {
        XxAnnotationConfigApplicationContext springContext = (XxAnnotationConfigApplicationContext) XxLocalCache.CONTEXT_CACHE.get("springContext");
        XxDispatcherServlet dispatcherServlet = springContext.getBean(XxDispatcherServlet.class);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", dispatcherServlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/*");
    }
}

When initializing the Spring container, the xxdispatcher servlet will initialize, scan and fill the mapped map collection. The XxDispatcherServlet implements a Aware interface of Spring to obtain the context, scan and register beanDefinition, and an XxInitializingBean interface to fill the result set of map mapping after the initialization of XxDispatcherServlet

@XxComponent
public class XxDispatcherServlet extends HttpServlet implements XxInitializingBean, XxApplicationContextAware {

    public XxAnnotationConfigApplicationContext context;

    public Map<String, Method> handlerMapping = new ConcurrentHashMap<>(256);

    public Map<String,String[]> pathVariableMapping = new ConcurrentHashMap<>(256);

    public List<XxHandlerInterceptor> handlerInterceptors = new ArrayList<>();

    public static List<AnnotationTypeParameterParser> parameterParsers = new ArrayList<>();

    static {
        parameterParsers.add(new XxRequestParamParser());
        parameterParsers.add(new XxPathVariableParser());
        parameterParsers.add(new XxRequestBodyParser());
    }

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

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

    @Override
    public void afterPropertiesSet() {
        List<Class<?>> loadClass = context.getXxDefaultListableBeanFactory().loadRootResources();
        for (Class<?> aClass : loadClass) {
            if(aClass.isAnnotationPresent(XxController.class)){
                XxRequestMapping aClassAnnotation = aClass.getAnnotation(XxRequestMapping.class);
                String aClassValue = handleUrl(aClassAnnotation.value());
                Method[] methods = aClass.getDeclaredMethods();
                for (Method method : methods) {
                    if(method.isAnnotationPresent(XxRequestMapping.class)){
                        XxRequestMapping aMethodAnnotation = method.getAnnotation(XxRequestMapping.class);
                        String requestUrl = handleUrl(aClassValue + aMethodAnnotation.value());
                        handlerMapping.put(requestUrl,method);
                    }
                }
            }
        }
    }

    @Override
    public void setXxApplicationContext(XxAnnotationConfigApplicationContext context) {
        this.context = context;
    }

    private String handleUrl(String value){
        if(!value.startsWith("/")){
            value += "/";
        }
        if(value.endsWith("/")){
            value = value.substring(0,value.length()-1);
        }
        if(value.contains("/{") && value.contains("}")){
            String key = value.substring(0,value.indexOf("/{"));
            String values = value.substring(value.indexOf("/{")+1);
            pathVariableMapping.put(key,values.split("/"));
            value = key;
        }
        return value;
    }
    //doDispatcher......
}

handlerMapping: the result set class level XxRequestMapping value of the mapping plus the method level XxRequestMapping value

/user + /get01

pathVariableMapping: stores the mapping result set containing values in the processing path

/user + /get01/001/waf , 001 stands for id , waf = name, which is understood as the query result through id and name

handlerInterceptors is a collection of interceptors. A collection will be filled in during Spring scanning. The function of the collection is to obtain all the implementation classes of the interface through an interface

parameterParsers , parameter parser this project implements the request parameter parsing of three annotations , xxrequestparam, xxpathvariable and xxrequestbody

Specifically, in the logic of the execution method, the response parameters are directly returned using json packages because they are uniformly encapsulated. If there is no need for unified return encapsulation, they can be directly serialized and automatically parsed into json strings.

For code details, please refer to gitee address: https://gitee.com/wanganfen/xx-spring/tree/master/simple-mvc

Keywords: Java Spring Spring MVC

Added by srboj on Tue, 18 Jan 2022 00:40:14 +0200