Spring source code exploration III. preliminary test - refining core functions of Spring+MVC [1]

Refining the core functions of Spring+MVC

1, foreword

Before learning the source code of a framework, we can think about the configuration items provided by the framework for developers, the functions of the framework in the project, and the presentation form of the framework in the project. We can think about how to write the framework by ourselves, how to realize it, and what is the internal thinking. In this way, we sort out a set of corresponding implementation methods, and then take our own set of things to see the source code to see how the source code elegantly realizes the functions of its framework, so as to polish our coding and thinking through comparison and pondering.

2, comb

  • Configuration items required by developers
  • Presentation form of framework in the project
  • Functions of framework in the project

2.1 configuration items required by developers

Let's take spring + springMVC as an example and not mention spring boot for now.
First, we need to introduce the dependency package of spring, and then configure web.xml.

2.1.1 configure the start of Spring Web

Readers with Web development experience know that you can configure a self starting Servlet or define a Web container listener in web.xml. With either of these two, we can start the Spring Web application context.
Spring provides Servlet and Web container listener for starting WebApplicationContext respectively:

org.springframework.web.context.ContextLoaderServlet
org.springframework.web.context.ContextLoaderListener

Both of them implement the logic of starting the WebApplicationContext instance. We only need to select one of them according to the specific situation of the Web container and complete the configuration in web.xml
For example, if we choose the Web container listener, we can configure it in web.xml as follows:

<listener>
	<description>start-up spring container</description>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

2.1.2 configure scan path of Spring Web configuration file

ContextLoaderListener obtains the location of the Spring configuration file through the Web container context parameter contextConfigLocation. Users can specify multiple profiles separated by commas, spaces, or colons. For configuration file paths without resource type prefix, WebApplicationContext defaults to the deployment root path of the Web. Of course, we can use path configuration with resource type prefix, such as "classpath*:/spring-*.xml", which is equivalent to the above configuration.

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:spring-context.xml</param-value>
</context-param>

2.1.3 spring MVC distributor and matching rule configuration

To use spring MVC, we need to introduce dispatcher servlet in web.mxl, and indicate the path of configuration file and matching rules.

<servlet>
	<servlet-name>spring-mvc</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- Specified path -->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc.xml</param-value>
	</init-param>
</servlet>

<servlet-mapping>
	<servlet-name>spring-mvc</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

The above configuration indicates that a dispatcher servlet (the dispatcher servlet is the front-end controller, the core function is to distribute requests to the corresponding java classes) is declared. The path address of the configuration file is resources / spring mvc.xml, and the matching rule is. do at the end of the request url is matched and distributed once in the dispatcher servlet.

2.1.4 xml configuration file of spring

We create a spring-context.xml under the resources folder of the project to configure spring related configuration information, and a spring-mvc.xml to configure spring MVC related configuration information.
The configuration information in spring-mvc.xml is as follows:

<?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:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=
            "
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx.xsd 
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd
            "
            > 
   
    <context:component-scan base-package="com.spring.test.aop" />
    
    <mvc:annotation-driven />
</beans>  

The most important one is < context: component scan base package = "com.spring.test.aop" / >, which specifies the bean scanning path for configuring spring. The bean with annotation declaration (@ Controller, @ Service, @ Component) under the path will be registered in the bean container.

2.2 presentation form of framework in the project

When we complete the above configuration, we can use some annotations provided by the framework to complete the corresponding functional implementation.

  • Common annotations declared on classes include @ Controller, @ Service, @ Component
  • The annotations declared on the attribute are @ Autowired, @ Resource
  • The annotations declared on the method are @ RequestMapping, @ PostMapping, @ GetMapping
  • The annotations on the method parameters are @ RequestBody, @ RequestParam.

For example:

@Controller
@RequestMapping("/test")
public class TestController {
    
    @Autowired
    private Config Config;
    
    @RequestMapping("/testMethod.do")
    public void testMethod(@RequestBody Test test) {
        
    }
    
    @RequestMapping("/testMethodParam.do")
    public void testMethodParam(@RequestParam("param") String param) {
        
    }
}

2.3 functions of framework in the project

The core functions of spring in the project are IOC, DI and AOP. As an adhesive, spring can integrate some external frameworks.
Through the core functions of spring, we can easily handle the dependency relationship between beans. We don't need to pay attention to creating beans and managing beans. We declare the beans to spring management and use them directly.

3. Simulation

Simulate the core functions of spring and spring MVC. The overall idea is as follows:

3.1 configure application.properties

To facilitate parsing, we can use application.properties to replace the configuration of spring-content.xml and spring-mvc.xml. The specific configuration content is as follows:

scanPackage=com.peng.demo

Define the path to scan.

3.2 user defined annotation class

According to the annotation classes presented by spring, we simulate to build our own annotation classes
@PDController

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PDController {
    String value() default "";
}

@PDService

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PDService {
    String value() default "";
}

@PDAutowired

import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PDAutowired {
    String value() default "";
}

@PDRequestMapping

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PDRequestMapping {
    String value() default "";
}

@PDRequestParam

import java.lang.annotation.*;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PDRequestParam {
    String value() default "";
}

3.3 simulate the creation of dispatcher Servlet

Create PDDispatcherServlet, inherit HttpServlet, override init(), doGet(), doPost().

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PDDispatcherServlet extends HttpServlet{

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

3.4 configure web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">
	<servlet>
		<servlet-name>pdmvc</servlet-name>
		<servlet-class>com.peng.mvcframework.servlet.PDDispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>application.properties</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>pdmvc</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

We use our own DispatchServlet and application.properties.

3.5 configure annotation and create controller/service class

controller layer

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.peng.demo.service.IDemoService;
import com.peng.mvcframework.annotation.PDAutowired;
import com.peng.mvcframework.annotation.PDController;
import com.peng.mvcframework.annotation.PDRequestMapping;
import com.peng.mvcframework.annotation.PDRequestParam;

@PDController
@PDRequestMapping("/demo")
public class DemoController {

  	@PDAutowired private IDemoService demoService;

	@PDRequestMapping("/query")
	public void query(HttpServletRequest req, HttpServletResponse resp,
					  @PDRequestParam("name") String name){
		String result = demoService.get(name);
		try {
			resp.getWriter().write(result);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@PDRequestMapping("/add")
	public void add(HttpServletRequest req, HttpServletResponse resp,
					@PDRequestParam("a") Integer a, @PDRequestParam("b") Integer b){
		try {
			resp.getWriter().write(a + "+" + b + "=" + (a + b));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

service level

public interface IDemoService {
	
	String get(String name);
}
import com.peng.demo.service.IDemoService;
import com.peng.mvcframework.annotation.PDService;

/**
 * Core business logic
 */
@PDService
public class DemoServiceImpl implements IDemoService{

	public String get(String name) {
		return "My name is " + name;
	}
}

At this point, the configuration phase is complete.

3.6 container initialization - write DispatchServlet


We follow this process to write the DispatchServlet.
First, write the following initialization process in the init() method.

public class PDDispatcherServlet extends HttpServlet{

    //Store the configuration content of application.properties
    private Properties contextConfig = new Properties();
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        //1. Load profile
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2. Scan related classes
        doScanner(contextConfig.getProperty("scanPackage"));
        //3. Initializes instances of all related classes and places them in IOC containers
        doInstance();
        //4. Complete dependency injection
        doAutowired();
        //5. Initialize HandlerMapping
        initHandlerMapping();

        System.out.println("PD Spring framework is init.");
    }
    
    private void initHandlerMapping() {
        
    }

    private void doAutowired() {
        
    }

    private void doInstance() {
        
    }

    private void doScanner(String property) {
        
    }

    private void doLoadConfig(String initParameter) {
        
    }

3.6.1 loading configuration files

    private void doLoadConfig(String contextConfigLocation) {
        InputStream fis = null;
        try {
            //Get the input stream of application.properties from resource
            fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
            //Using the Properties object to read the configuration file
            contextConfig.load(fis);
        }catch(Exception e){
            System.out.println("When loading a profile,Appear error,Abnormal information:" + Arrays.toString(e.getStackTrace()));
        }finally{
            try {
                if(null != fis){
                    fis.close();
                }
            } catch (IOException e) {
                System.out.println("When loading a profile,Appear error,Abnormal information:" + Arrays.toString(e.getStackTrace()));
            }
        }
    }

3.6.2 scanning related classes

    //Store all scanned classes
    private List<String> classNames = new ArrayList<String>();

	private void doScanner(String scanPackage) {
        //scanPackage = com.peng.demo
        //The path is passed. All the classes under the path are scanned in
        URL url = this.getClass().getClassLoader()
                .getResource("/" + scanPackage.replaceAll("\\.","/"));
        //A package corresponds to a folder at the system level. The path is converted to a File folder to represent and operate.
        File classPath = new File(url.getFile());

        //Traverse all folders and files under this folder
        for (File file : classPath.listFiles()) {
            //If the file object is a folder, recursively continue scanning
            if(file.isDirectory()){
                doScanner(scanPackage + "." + file.getName());
            }else {
                //Determine whether the file object is a class file. If it is not a class file, do not scan.
                if(!file.getName().endsWith(".class")){ continue; }
                //If it is a class file, add the class file and its path address to the List
                //For example: com.peng.demo.mvc.controller.DemoController
                String className = (scanPackage + "." + file.getName()).replace(".class","");
                classNames.add(className);
            }
        }
    }

4, conception

Due to the space problem, we will continue to refine and simulate the writing of init() method of DispatchServlet and how to complete the request forwarding through DispatcherServlet in the runtime.
The following general idea:

  • Initialize the scanned class, use reflection, and instantiate its class. And define a map < string, Object > map to save the instantiated object. The key value is the name of the class, and the value is the instantiated object
  • Dependency injection also uses reflection to get whether there is a custom annotation @ Autowired on the class's attributes. If there is one, look for the corresponding instantiated class from the above map by modifying the attribute type, and use reflection injection to inject the attribute value.
Published 21 original articles, won praise 2, visited 7477
Private letter follow

Tags: Spring xml Java Attribute

Posted on Mon, 13 Jan 2020 03:25:03 -0500 by blear