1.Spring
The operation process of MVC is as follows.
Let's assume this scenario:
DispatcherServlet is the boss in the MVC scene, and he does it himself. He has to review and approve everything. That day, he received a user request asking him to give a web page.
He immediately gave his deputy handler mapping and said, "Xiao Liu, look at this job. Who is suitable for it?" When Xiao Liu looked at the employee roster, a Controller named Xiao Zhang was competent. Xiao Liu said to the leader, "Xiao Zhang can do it.".
At this time, the leader DispatcherServlet cannot directly find Xiao Zhang, because Xiao Zhang is only responsible for realizing specific business, and the user's requirements are too abstract. Xiao Zhang can't understand them. Someone needs to help him sort out what to do in the first step and what to do in the second step. At this time, the project manager HandlerAdaper went online. The leader found the project manager and said, "help Xiao Zhang manage this job. What should I do specifically?".
After the project manager finished dividing two from three, the leader took the handled task and handed it to Li Xiaozhang. Our Xiaozhang was also very competitive and finished it. Moreover, His project not only has business models, but also beautiful components (View), but Xiao Zhang's aesthetic is not very good, so he can't combine them together. Therefore, the leader DispatcherServlet went to the art student viewRsolver and asked her to render it. viewRsolver has excellent painting skills, and only a few strokes render a page with both business information and good-looking.
So far, a project has been completed, and the dispatcher servlet shows the results (JSP and other front-end pages) to the users. The users are satisfied and paid generously, so everyone has money to take
After reading the MVC process of Rod Johnson's spring MVC, we found that the components have a clear division of labor, perform their respective duties, and can complete many complex businesses. However, we just started, we certainly can't complete so many. Therefore, today we build a simple version, Only the leader (dispatcher servlet) and various salesmen, etc. the salesmen are still only responsible for specific business, and the leaders do all other work.
2. The process of our framework
In our process, DispatcherServlet leader = front-end controller + mapping processor
3. Start building
1. New maven project
2. In POM Import dependency in XML
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency>
3. Project structure
4. Prepare configuration file
Write the configuration file in the resource directory:
applicationContext.properties: specify the scanning path package. Here we specify the package where the controller is located
package=com.yun.controller
5. Update web XML file
The skeleton is still version 2.0. We update it to version 4.0 here.
And register our leader mydispatcher servlet and specify the contextConfigLocation where the configuration file is located. Our leader does everything himself and asks him to intercept all requests here.
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- Configure our own front-end controller, MyDispatcherServlet Just one servlet,Intercept requests sent by the front end--> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>com.yun.servlet.MyDispatcherServlet</servlet-class> <!-- Configure scan package--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>application.properties</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <!-- Block all requests--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
6. User defined annotation
The function of annotation here is equivalent to adding a small tail to the class / Method. We identify different controllers and methods through different tails
We define two annotations
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @ fileName:MyController * @ description:Imitate the @ controller in spring and act on the class to identify that the class is a controller */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyController { /** * It's useless, but in order to imitate the @ Controller in spring, we add it * Our simple version uses the default id: a lowercase class name */ String value() default ""; } import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @ fileName:MyRequestMapping * @ description:Imitate @ RequestMapping, act on classes and methods, and specify the corresponding Controller and Method through url * @ createTime:2021/12/17 10:31 * @ version:1.0.0 */ @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyRequestMapping { /** * @Author: zyk * @Description: The domain name can only have one segment, which can only be / controllerName/methodName * @Date: 2021/12/17 15:15 */ String value() default ""; }
The above is some preparatory work. If copying spring MVC is regarded as forming a team, the above work is equivalent to finding a workplace for the team. The following is the characterization of the characters. First, let's welcome our leader mydispatcher servlet
Write front-end controller
Write the front-end controller (a Servlet) and rewrite the init and service methods
MyDispatcherServlet
Overview
The whole process revolves around two rewritten methods, in which init() is the focus.
What MyDispatcherServlet wants to do is to look at the access address of the front end in a sentence: then call the matching processor (Controller) corresponding method (method).
To complete this, we need to bind a certain string to the Controller and method through annotation, and then analyze the string in the Url passed from the front end to find the same one, so as to complete the matching. Reflection plays a great role in this process. Many actions, such as finding the annotation on the class header or finding the value in the annotation, need reflection.
Specific process
code
Create a dispatcher servlet that inherits the HTTP servlet and overrides the two methods. (init() and service())
import com.yun.annotation.MyController; import com.yun.annotation.MyRequestMapping; import javafx.scene.effect.Reflection; import org.reflections.Reflections; 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.IOException; import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; /** * @ fileName:MyDispatcherServlet * @ description:Front end controller, equivalent manager, handles distribution requests * Look at the access address of the front end, then call the matching processor (Controller) corresponding method (method). * @ author:zyk * @ createTime:2021/12/17 10:43 * @ version:1.0.0 */ public class MyDispatcherServlet extends HttpServlet { /** * 1.Configure scanned packages and put them into one In the properties file, it is read during initialization */ private Properties properties = new Properties(); /** * 2.You need a set to save all that can respond to the controller */ private Set<Class<?>> classSet = new HashSet<>(); /** * 3.springMVC container */ private Map<String, Object> springMVCContext = new HashMap<>(); /** * 4.The mapping processor stores all the methods */ private Map<String, Method> handlerMapping = new HashMap<>(); /** * 4.The mapping relationship of the back-end processor stores all controller s */ private Map<String, Object> controllerMap = new HashMap<>(); /** * @Author: zyk * 1.Load profile * 2.Scan controller package * 3.Initialize controller * 4.Initialize the Handler mapper (Handler = controller + method) * @Date: 2021/12/17 15:20 */ @Override public void init(ServletConfig config) throws ServletException { //1. Load the configuration file on the web Initialization parameter contextConfigLocation configured in XML String initParameter = config.getInitParameter("contextConfigLocation"); try { loadConfigFile(initParameter); } catch (Exception e) { e.printStackTrace(); } //2. Scan controller package scanPackage(properties.getProperty("package")); //3. Initialize the controller initController(); //4. Initialize the processor mapper initHandlerMapping(); } /** * @Author: zyk * @Description: Methods of performing business * @Date: 2021/12/17 15:46 * @Param: [req, resp] * @return: void */ @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //Processing requests if (handlerMapping.isEmpty()) { return; } //Get url String uri = req.getRequestURI(); String contextPath = req.getContextPath(); String url = uri.replace(contextPath, ""); if (!handlerMapping.containsKey(url)) { resp.getWriter().println("404"); } else { Method method = handlerMapping.get(url); Object controller = controllerMap.get(url); try { method.invoke(controller); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } /** * @ author: zyk * @ description:Load profile * @ date: 2021/12/17 10:51 * Tool class */ private void loadConfigFile(String fileName) { //Get resources by stream InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(fileName); try { properties.load(inputStream); } catch (IOException e) { e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * @ author: zyk * @ description:Scan all classes with MyController annotations and encapsulate them into the set collection * @ date: 2021/12/17 10:52 * Tool class */ private void scanPackage(String packageName) { Reflections retentions = new Reflections(packageName); classSet = retentions.getTypesAnnotatedWith(MyController.class); } /** * @ author: zyk * @ description:Initialize controller * @ date: 2021/12/17 10:53 * Tool class */ private void initController() { if (classSet.isEmpty()) { return; } for (Class<?> controller : classSet) { try { springMVCContext.put(lowerFirstWord(controller.getSimpleName()), controller.newInstance()); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } } /** * @ author: zyk * @ description:Initial to lowercase * @ date: 2021/12/17 * Tool class */ private String lowerFirstWord(String simpleName) { char[] array = simpleName.toCharArray(); array[0] += 32; return String.valueOf(array); } /** * @ author: zyk * @ description:Initialize the mapping processor, (Handler = controller + method) * @ date: 2021/12/17 10:58 */ private void initHandlerMapping() { if (springMVCContext.isEmpty()) { return; } for (Map.Entry<String, Object> entry : springMVCContext.entrySet()) { //Get class object Class<?> aClass = entry.getValue().getClass(); if (!aClass.isAnnotationPresent(MyController.class)) { continue; } else { String baseUrl = ""; if (aClass.isAnnotationPresent(MyRequestMapping.class)) { //If the class contains the annotation MyRequestMapping, get the annotation value MyRequestMapping annotation = aClass.getAnnotation(MyRequestMapping.class); baseUrl = annotation.value(); } //Get all methods Method[] methods = aClass.getMethods(); for (Method method : methods) { // The judgment method contains the MyRequestMapping annotation if (method.isAnnotationPresent(MyRequestMapping.class)) { MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class); String url = annotation.value(); url = baseUrl + url; //Put the method into the method set handlerMapping.put(url, method); try { //Put into controllerMap controllerMap.put(url, aClass.newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } } }
Finally, write a Controller to test
@MyRequestMapping(value = "/test") @MyController public class TestController { @MyRequestMapping(value = "/test1") public void test1() { System.out.println("test1 Called"); } @MyRequestMapping(value = "/test2") public void test2() { System.out.println("test2 Called"); } @MyRequestMapping(value = "/test3") public void test3() { System.out.println("test3 Called"); } }
Test screenshot:
Wrong address entered:
Enter the correct address:
The above is to completely rewrite the spring MVC framework.