1. Write in front
I plan to manually simulate a spring MVC today, because I haven't simulated spring MVC before learning spring MVC. We only stop at the application level and really understand the source code, so it's not easy to remember. Today, I first introduce the use of spring MVC, and then I manually simulate a spring MVC.
2. Use of spring MVC
Of course, the best way to learn Spring MVC is the official website of Spring. Let's build a Spring MVC project based on xml according to the official website of Spring. First, let's take a look Spring's official website , in order to save everyone's time, go directly to the code! First POM xml
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies> <build> <plugins> <!-- to configure Tomcat plug-in unit --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>80</port> <path>/</path> <uriEncoding>UTF-8</uriEncoding> </configuration> </plugin> </plugins> </build>
It mainly imports a dependency of spring MVC, and then imports a Tomcat plug-in. The next step is to look at the web xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <servlet> <servlet-name>app</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:SpringMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
It mainly configures a dispatcher servlet class, then specifies the spring MVC configuration file, and then configures the interception path. The next step is spring MVC xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--Configure scan path--> <context:component-scan base-package="com.ys.controller"/> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".html"/> </bean> </beans>
Finally, we write another Controller class. The specific code is as follows:
package com.ys.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/test") public class Test { @RequestMapping("/test.do") public String test() { System.out.println("This method was called"); return "index"; } }
Finally, let's take a look at our default index HTML, as follows:
<!DOCTYPE html> <html lang="ch"> <head> <meta charset="UTF-8"> <title>test</title> </head> <body> <h1>test SpringMVC</h1> </body> </html>
The results of the final operation are as follows:
3. Manually simulate spring MVC
Phenomenon: the above is to write a method, then add the corresponding annotation, and then enter the corresponding address in the browser, which can call the corresponding method and jump to the corresponding page at the same time.
Idea: we should configure our own servlet, and then parse the web through the method of servlet life cycle Spring MVC. XML The contents of XML are configured, then the class with @Controller base note is scanned under the corresponding package, then all the @RequestMapping annotations are added to the class, then these methods are stored in the corresponding Map, where the key is the path of the request, and the value is the corresponding method, so we can input the corresponding address in the browser and then call the corresponding method. No more nonsense, just go to the code.
Let's take a look at our POM xml
<dependencies> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <!-- to configure Tomcat plug-in unit --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>80</port> <path>/</path> <uriEncoding>UTF-8</uriEncoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.0</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerArgs> <arg>-parameters</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>
Above, the author imported a dependency of dom4j to parse xml files, and then the dependency of servlet. Next, we will simulate the servlet class of the core of spring MVC. Before simulating this class, the author writes the web The specific contents of xml are as follows:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <servlet> <servlet-name>app</servlet-name> <servlet-class>com.ys.servlet.MyMVCServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>MyMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
Next, we write our configuration class, which is similar to spring MVC xml
<beans> <componentScan package="com"></componentScan> <view prefix = "/page/" suffix=".html"></view> </beans>
Then we define several annotation classes, namely Controller, RequestMapping and ResponseBody. The specific codes are as follows:
package com.ys.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Controller { }
package com.ys.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequestMapping { //URL blocked address String value() default ""; }
package com.ys.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ResponseBody { }
Then we define an entity class UserEntity to test the receiving parameters. The specific code is as follows:
package com.ys.entity; public class UserEntity { private String name; private String age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "UserEntity{" + "name='" + name + '\'' + ", age='" + age + '\'' + ", sex='" + sex + '\'' + '}'; } }
Then we define an HTML page to test the of jump page. The specific codes are as follows:
<!DOCTYPE html> <html lang="ch"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Here is index.html</h1> </body> </html>
Next, we will write our mymvc servlet class to simulate the core class of spring MVC. The specific codes are as follows:
package com.ys.servlet; import com.ys.annotation.Controller; import com.ys.annotation.RequestMapping; import com.ys.annotation.ResponseBody; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; 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.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.HashMap; import java.util.Map; import java.util.Objects; public class MyMVCServlet extends HttpServlet { private static String COMPONENT_SCAN_ELEMENT_PACKAGE_NAME = "package"; private static String COMPONENT_SCAN_ELEMENT_NAME = "componentScan"; private static String VIEW_ELEMENT_NAME = "view"; private static String VIEW_ELEMENT_PREFIX_NAME = "prefix"; private static String VIEW_ELEMENT_SUFFIX_NAME = "suffix"; private static String XML_PATH_LOCAL = "contextConfigLocation"; private static String prefixValue = ""; private static String suffixValue = ""; private static String packageValue = ""; private static String projectPath = MyMVCServlet.class.getResource("/").getPath(); //The method used to save the parsed data private static Map<String, Method> methodMap = new HashMap<>(); /** * init Main tasks: * Load configuration file web Loading spring mvc.xml xml * Scan the entire project according to the directory given by the configuration file * Scan all classes annotated with @ Controller * After scanning the class annotated with @ Controller, traverse all the methods in it * After getting the method object, analyze whether the @ RequestMapping annotation is added to the method * Define a Map collection. Bind the Value of @ RequstMapping with the method object * Map<String,Method> * * @param config web.xml * @throws ServletException abnormal */ @Override public void init(ServletConfig config) throws ServletException { //Get web Value of contextConfigLocation configuration in XML String initParameter = config.getInitParameter(XML_PATH_LOCAL); //Get something like spring MVC XML file File file = new File(projectPath + "//" + initParameter); Document parse = parse(file); //Root node Element rootElement = parse.getRootElement(); //componentScan node Element componentScanEle = rootElement.element(COMPONENT_SCAN_ELEMENT_NAME); //Attribute value of package in componentScan node packageValue = componentScanEle.attributeValue(COMPONENT_SCAN_ELEMENT_PACKAGE_NAME); //view node Element viewEle = rootElement.element(VIEW_ELEMENT_NAME); //prefix prefixValue = viewEle.attributeValue(VIEW_ELEMENT_PREFIX_NAME); //suffix suffixValue = viewEle.attributeValue(VIEW_ELEMENT_SUFFIX_NAME); //Scan items scanProjectByPath(projectPath + "\\" + packageValue); } private void scanProjectByPath(String projectPath) { File file = new File(projectPath); //Recursively parse all files of the project scanFile(file); } private void scanFile(File file) { //Folder, recursive if (file.isDirectory()) { for (File listFile : Objects.requireNonNull(file.listFiles())) { scanFile(listFile); } } else { // Not a folder to resolve directly String filePath = file.getPath(); String suffix = filePath.substring(filePath.lastIndexOf(".")); if (".class".equals(suffix)) { String classPath = filePath.replace(new File(projectPath).getPath() + "\\", ""); classPath = classPath.replaceAll("\\\\", "."); //Get class name String className = classPath.substring(0, classPath.lastIndexOf(".")); try { Class<?> clazz = Class.forName(className); if (clazz.isAnnotationPresent(Controller.class)) { RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class); String classRequestMappingUrl = ""; if (classRequestMapping != null) { //Get the value of RequestMapping above the class classRequestMappingUrl = classRequestMapping.value(); } //Traverse all methods for (Method method : clazz.getDeclaredMethods()) { if (!method.isSynthetic()) { //Get the annotation of RequestMapping on the method RequestMapping annotation = method.getAnnotation(RequestMapping.class); if (annotation != null) { String methodRequestMappingUrl = ""; methodRequestMappingUrl = annotation.value(); System.out.println("class:" + clazz.getName() + "of" + method.getName() + "Method mapped to" + classRequestMappingUrl + methodRequestMappingUrl + "above"); methodMap.put(classRequestMappingUrl + methodRequestMappingUrl, method); } } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } /** * @param file File to parse * @return Return the parsed Document */ public Document parse(File file) { SAXReader saxReader = new SAXReader(); try { return saxReader.read(file); } catch (DocumentException e) { e.printStackTrace(); } return null; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } /** * What to do when executing: * get the request URI in the map * Assign values to parameters and call methods * Get the return value of the method and do view jump and message return */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Get the requested URI String requestURI = request.getRequestURI(); Method method = methodMap.get(requestURI); if (method != null) { Parameter[] parameters = method.getParameters(); Object[] objects = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { Parameter parameter = parameters[i]; String name = parameter.getName(); Class<?> type = parameter.getType(); if (type.equals(String.class)) { objects[i] = request.getParameter(name); } else if (type.equals(HttpServletRequest.class)) { objects[i] = request; } else if (type.equals(HttpServletResponse.class)) { objects[i] = response; } else { try { Object o = type.newInstance(); for (Field field : type.getDeclaredFields()) { field.setAccessible(true); String fieldName = field.getName(); field.set(o, request.getParameter(fieldName)); } objects[i] = o; } catch (Exception e) { e.printStackTrace(); } } } Object o = null; try { o = method.getDeclaringClass().newInstance(); Object invoke = method.invoke(o, objects); //Judgment return value if (!method.getReturnType().equals(Void.class)) { ResponseBody annotation = method.getAnnotation(ResponseBody.class); if (annotation != null) { response.getWriter().write(String.valueOf(invoke)); } else { request.getRequestDispatcher(prefixValue + String.valueOf(invoke) + suffixValue).forward(request, response); } } } catch (Exception e) { e.printStackTrace(); } } else { response.setStatus(404); } } }
Finally, let's take a look at the Controller class we wrote. The specific code is as follows:
package com.ys.controller; import com.ys.annotation.Controller; import com.ys.annotation.RequestMapping; import com.ys.annotation.ResponseBody; import com.ys.entity.UserEntity; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Controller @RequestMapping("/test") public class TestController { @RequestMapping("/test.do") @ResponseBody public Object test(String name, HttpServletRequest request, HttpServletResponse response, UserEntity userEntity){ request.getParameter("name"); System.out.println(name); System.out.println(request); System.out.println(response); System.out.println(userEntity); return "test"; } @RequestMapping("/model.do") public Object model(){ return "index"; } }
- The results of the operation are as follows:
You can see that our results have been mapped and saved in the corresponding map. Then let's take a look at the running results in the browser. First, let's look at the jump of the page. The details are as follows:
You can see that there is no problem with the jump of our page. Finally, let's take a look at the encapsulation of our parameters. The specific operation results are as follows:
You can see that our parameters are also encapsulated. At this point, a simple spring MVC is over.
4. Write at the end
Through our simple manual simulation of spring MVC, we can have a deeper understanding of spring MVC. I will upload the source code. You can download it from my resources.