DispatchServlet principle of Spring principle

Posted by tmed on Tue, 08 Mar 2022 02:44:24 +0100

DispatchServlet parsing of Spring MVC

Spring MVC sequence diagram

The parent class of DispatchServlet is FrameworkServlet, and the parent class of FrameworkServlet is HttpServletBean

Core process:

  • Find the init() method of DispatchServlet and locate the init() method of the parent HttpServletBean of its parent class

    • Locate the location of the configuration file and load the configuration information
    • initServletBean call
  • The parent FrameworkServlet of DispatchServlet implements the initServletBean method,

    • Complete the initialization context of initWebApplicationContext(), including binding the location of configuration file and binding the relationship between parent and child containers
    • After initialization, onRefresh(wac) is called. method
  • The onRefresh() method is implemented by the subclass DispatchServlet method

    • Complete the initialization strategy and mainly realize the nine components of spring MVC
    • The nine components are obtained from the ioc container by name or class

1. It is known from the above that the init method of DispatchServlet is started, but there is no init method. However, according to the source code, the init method is the init method of HttpServletBean, the parent class of DispatchServlet

public final void init() throws ServletException {
    PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            // Locate resources
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            // Load configuration information
            ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
            this.initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        } catch (BeansException var4) {
            if (this.logger.isErrorEnabled()) {
                this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
            }
            throw var4;
        }
    }

    this.initServletBean();
}

protected void initServletBean() throws ServletException {
}

2. The initServletBean method that really completes the action of initializing the container is implemented by the parent FrameworkServlet

protected final void initServletBean() throws ServletException {
    this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        // Initialization information of webapplicationcontext method
        this.webApplicationContext = this.initWebApplicationContext();
        this.initFrameworkServlet();
    } catch (ServletException var5) {
        this.logger.error("Context initialization failed", var5);
        throw var5;
    } catch (RuntimeException var6) {
        this.logger.error("Context initialization failed", var6);
        throw var6;
    }
    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
    }
}

// Initialize context information
protected WebApplicationContext initWebApplicationContext() {
    	// Get the parent container WebApplicationContext from ServletContext
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
		// Declare child containers
        WebApplicationContext wac = null;
    	// Establish an association between parent and child containers
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }

                    this.configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
		// Find out whether the reference of the web container exists from the ServletContext, and create a default empty ioc container
        if (wac == null) {
            wac = this.findWebApplicationContext();
        }
	   // Assign a value to the ioc container created in the previous step
        if (wac == null) {
            wac = this.createWebApplicationContext(rootContext);
        }
		// Trigger onRefresh method
        if (!this.refreshEventReceived) {
            synchronized(this.onRefreshMonitor) {
                this.onRefresh(wac);
            }
        }
	
        if (this.publishContext) {
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
        }

        return wac;
    }
	// Find whether the reference of the web container exists from the ServletContext
    @Nullable
    protected WebApplicationContext findWebApplicationContext() {
        String attrName = this.getContextAttribute();
        if (attrName == null) {
            return null;
        } else {
            WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName);
            if (wac == null) {
                throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
            } else {
                return wac;
            }
        }
    }
	//Assign a value to the created ioc container
    protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
        Class<?> contextClass = this.getContextClass();
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
        } else {
            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
            wac.setEnvironment(this.getEnvironment());
            wac.setParent(parent);
            // Binding profile
            String configLocation = this.getContextConfigLocation();
            if (configLocation != null) {
                wac.setConfigLocation(configLocation);
            }
            // Refresh context information
            this.configureAndRefreshWebApplicationContext(wac);
            return wac;
        }
    }

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            if (this.contextId != null) {
                wac.setId(this.contextId);
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName());
            }
        }
        wac.setServletContext(this.getServletContext());
        wac.setServletConfig(this.getServletConfig());
        wac.setNamespace(this.getNamespace());
        wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener()));
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
        }
		// Empty method
        this.postProcessWebApplicationContext(wac);
        // Implementation of some implementers
        this.applyInitializers(wac);
        // Refresh
        wac.refresh();
    }
	
	// Implemented by subclasses
 	protected void onRefresh(ApplicationContext context) {
    }

The onRefresh() method is implemented by the subclass DispatchServlet method

protected void onRefresh(ApplicationContext context) {
	// Initialization policy
    this.initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
    //1. Multi file upload component
    this.initMultipartResolver(context);
    
     private void initMultipartResolver(ApplicationContext context) {
        // It saves a lot of code for printing exception logs
        try {
            // Get the uploaded component of the configuration file from the configuration file
            this.multipartResolver = (MultipartResolver)context.getBean("multipartResolver", MultipartResolver.class);
        } catch (NoSuchBeanDefinitionException var3) {
            this.multipartResolver = null;
        }
    }
    
    // 2. Initialize locales
    this.initLocaleResolver(context);
    
    private void initLocaleResolver(ApplicationContext context) {
        try {
            // It is also obtained from the ioc container
            this.localeResolver = (LocaleResolver)context.getBean("localeResolver", LocaleResolver.class);
        } catch (NoSuchBeanDefinitionException var3) {
            this.localeResolver = (LocaleResolver)this.getDefaultStrategy(context, LocaleResolver.class);
        }

    }
    
    // 3. Initialize template processor
    this.initThemeResolver(context);
    
     private void initThemeResolver(ApplicationContext context) {
        try {
             // It is also obtained from the ioc container
            this.themeResolver = (ThemeResolver)context.getBean("themeResolver", ThemeResolver.class);
        } catch (NoSuchBeanDefinitionException var3) {
            this.themeResolver = (ThemeResolver)this.getDefaultStrategy(context, ThemeResolver.class);
            }
        }

    }
    
    // 4. Initialize handlerMapping
    this.initHandlerMappings(context);
    
     private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
         // Whether to load all HandlerMapping. The default is true. When loading all, get the class of HandlerMapping type from the bean container
        if (this.detectAllHandlerMappings) {
            Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList(matchingBeans.values());
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        } else {
            try {
                // If you only need to load one, you can get it by name
                HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            } catch (NoSuchBeanDefinitionException var3) {
            }
        }
		// If there are no handlerMappings, try to get from the default policy
        if (this.handlerMappings == null) {
            this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
        }
    }
    
    // 5. Initialize parameter adapter
    this.initHandlerAdapters(context);

 private void initHandlerAdapters(ApplicationContext context) {
        this.handlerAdapters = null;
     // Whether to load all HandlerAdapters. The default is true. When loading all, get the class of HandlerMapping type from the bean container
        if (this.detectAllHandlerAdapters) {
            Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerAdapters = new ArrayList(matchingBeans.values());
                AnnotationAwareOrderComparator.sort(this.handlerAdapters);
            }
        } else {
            try {
                // If you only need to load one, you can get it by name
                HandlerAdapter ha = (HandlerAdapter)context.getBean("handlerAdapter", HandlerAdapter.class);
                this.handlerAdapters = Collections.singletonList(ha);
            } catch (NoSuchBeanDefinitionException var3) {
            }
        }
		// If there are no handlerAdapters, try to get from the default policy
        if (this.handlerAdapters == null) {
            this.handlerAdapters = this.getDefaultStrategies(context, HandlerAdapter.class);
        }
    }

    // 6. Initialize the exception interceptor. The specific implementation is the same logic as the initialization parameter adapter above
    this.initHandlerExceptionResolvers(context);
    // 7. Initialize the view preprocessor. The specific implementation is the same as the initialization parameter adapter above
    this.initRequestToViewNameTranslator(context);
    // 8. Initialize the view converter. The specific implementation is the same logic as the initialization parameter adapter above
    this.initViewResolvers(context);
    // 9. Initialize FlashMap Manager
    this.initFlashMapManager(context);

 	private void initFlashMapManager(ApplicationContext context) {
        try {
            //It is also obtained from the ioc container
            this.flashMapManager = (FlashMapManager)context.getBean("flashMapManager", FlashMapManager.class);
        } catch (NoSuchBeanDefinitionException var3) {
            this.flashMapManager = (FlashMapManager)this.getDefaultStrategy(context, FlashMapManager.class);
        }
    }
}

Custom DispatchServlet

1. Core principles of Spring

Dispatcher servlet is the core function class implemented by Spring

   <servlet>
        <servlet-name>service_dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/services/service_dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

(1) Version 1.0

This side implements a simple gpdispatcher servlet to simulate the core function classes implemented by Spring

Rewriting the init() method of the HttpServlet of the parent class to initialize the container mainly does the following things:

  • Obtain the configuration file and load the configuration file
  • Get the package name of the configured scan, and then put all classes in the package into the map of the ioc container
  • Traverse the map and process different types of classes. Put the methods in the Controller into the class, and directly instantiate the services into the class
  • Traverse the map to determine whether there are fields marked with autored. If there are, assign values to the fields through reflection (mainly from the ioc container)
public class GPDispatcherServlet extends HttpServlet {
    //ioc container
    private Map<String,Object> mapping = new HashMap<String, Object>();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req,resp);}
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            // Call the doDispatch method. After the request comes, the method will be called to implement the core logic
            doDispatch(req,resp);
        } catch (Exception e) {
            resp.getWriter().write("500 Exception " + Arrays.toString(e.getStackTrace()));
        }
    }
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        if(!this.mapping.containsKey(url)){resp.getWriter().write("404 Not Found!!");return;}
        Method method = (Method) this.mapping.get(url);
        Map<String,String[]> params = req.getParameterMap();
        method.invoke(this.mapping.get(method.getDeclaringClass().getName()),new Object[]{req,resp,params.get("name")[0]});
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        InputStream is = null;
        try{
            Properties configContext = new Properties();
            // 1. Get the configuration file and load it
            is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
            configContext.load(is);
            // Get scanned package
            String scanPackage = configContext.getProperty("scanPackage");
            // 2. Get the package name of the configured scan, and then put all classes in the package into the map of the ioc container
            doScanner(scanPackage);
            for (String className : mapping.keySet()) {
                if(!className.contains(".")){continue;}
                Class<?> clazz = Class.forName(className);
                if(clazz.isAnnotationPresent(GPController.class)){
                    mapping.put(className,clazz.newInstance());
                    String baseUrl = "";
                    if (clazz.isAnnotationPresent(GPRequestMapping.class)) {
                        GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
                        baseUrl = requestMapping.value();
                    }
                    Method[] methods = clazz.getMethods();
                    for (Method method : methods) {
                        if (!method.isAnnotationPresent(GPRequestMapping.class)) {  continue; }
                        GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
                        String url = (baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
                        mapping.put(url, method);
                        System.out.println("Mapped " + url + "," + method);
                    }
                }else if(clazz.isAnnotationPresent(GPService.class)){
                        GPService service = clazz.getAnnotation(GPService.class);
                        String beanName = service.value();
                        if("".equals(beanName)){beanName = clazz.getName();}
                        Object instance = clazz.newInstance();
                        mapping.put(beanName,instance);
                        for (Class<?> i : clazz.getInterfaces()) {
                            mapping.put(i.getName(),instance);
                        }
                }else {continue;}
            }
            for (Object object : mapping.values()) {
                if(object == null){continue;}
                Class clazz = object.getClass();
                if(clazz.isAnnotationPresent(GPController.class)){
                    Field [] fields = clazz.getDeclaredFields();
                    for (Field field : fields) {
                        if(!field.isAnnotationPresent(GPAutowired.class)){continue; }
                        GPAutowired autowired = field.getAnnotation(GPAutowired.class);
                        String beanName = autowired.value();
                        if("".equals(beanName)){beanName = field.getType().getName();}
                        field.setAccessible(true);
                        try {
                            field.set(mapping.get(clazz.getName()),mapping.get(beanName));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        } catch (Exception e) {
        }finally {
            if(is != null){
                try {is.close();} catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.print("GP MVC Framework is init");
    }
    // Scan the package and put the classes in the package in mapping
    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.","/"));
        File classDir = new File(url.getFile());
        for (File file : classDir.listFiles()) {
            if(file.isDirectory()){ doScanner(scanPackage + "." +  file.getName());}else {
                if(!file.getName().endsWith(".class")){continue;}
                String clazzName = (scanPackage + "." + file.getName().replace(".class",""));
                mapping.put(clazzName,null);
            }
        }
    }
}

(2) Version 2.0

  • Load the configuration file doLoadConfig(config.getInitParameter("contextConfigLocation");
  • Scan related class doScanner(contextConfig.getProperty("scanPackage");
  • Initialize the instances of all related classes and put them into the IOC container doInstance();
  • Complete dependency injection doAutowired();
  • Initialize handlermapping();
package com.gupaoedu.mvcframework.v2.servlet;

import com.gupaoedu.mvcframework.annotation.*;

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.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;


public class GPDispatcherServlet extends HttpServlet {

    //Store application Configuration contents of properties
    private Properties contextConfig = new Properties();
    //Store all scanned classes
    private List<String> classNames = new ArrayList<String>();
    //IOC container that holds all instantiated objects
    //Registered singleton mode
    private Map<String,Object> ioc = new HashMap<String,Object>();
    //Save the correspondence of all Mapping in Contrller

    private Map<String,Method> handlerMapping = new HashMap<String,Method>();

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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //Dispatch and distribute tasks
        try {
            //Delegation mode
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Excetion Detail:" +Arrays.toString(e.getStackTrace()));
        }
    }

//    private void doDispatch(HttpServletRequest req, HttpServletResponse resp)throws Exception {
//
//        String url = req.getRequestURI();
//        String contextPath = req.getContextPath();
//        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
//
//        if(!this.handlerMapping.containsKey(url)){
//            resp.getWriter().write("404 Not Found!!");
//            return;
//        }
//
//        Method method = this.handlerMapping.get(url);
//        //The first parameter: the instance where the method is located
//        //The second parameter: the argument required when calling
//
//        Map<String,String[]> params = req.getParameterMap();
//
//        //Opportunistic way
//        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
//        method.invoke(ioc.get(beanName),new Object[]{req,resp,params.get("name")[0]});
//        //System.out.println(method);
//    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp)throws Exception {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        if(!this.handlerMapping.containsKey(url)){
            resp.getWriter().write("404 Not Found!!");
            return;
        }

        Method method = this.handlerMapping.get(url);
        //The first parameter: the instance where the method is located
        //The second parameter: the argument required when calling
        Map<String,String[]> params = req.getParameterMap();
        //Gets the formal parameter list of the method
        Class<?> [] parameterTypes = method.getParameterTypes();
        //Save the url parameter list of the request
        Map<String,String[]> parameterMap = req.getParameterMap();
        //The location where the assignment parameters are saved
        Object [] paramValues = new Object[parameterTypes.length];
        //Dynamic assignment according to parameter position
        for (int i = 0; i < parameterTypes.length; i ++){
            Class parameterType = parameterTypes[i];
            if(parameterType == HttpServletRequest.class){
                paramValues[i] = req;
                continue;
            }else if(parameterType == HttpServletResponse.class){
                paramValues[i] = resp;
                continue;
            }else if(parameterType == String.class){

                //Annotated parameters in the extraction method
                Annotation[] [] pa = method.getParameterAnnotations();
                for (int j = 0; j < pa.length ; j ++) {
                    for(Annotation a : pa[i]){
                        if(a instanceof GPRequestParam){
                            String paramName = ((GPRequestParam) a).value();
                            if(!"".equals(paramName.trim())){
                                String value = Arrays.toString(parameterMap.get(paramName))
                                        .replaceAll("\\[|\\]","")
                                        .replaceAll("\\s",",");
                                paramValues[i] = value;
                            }
                        }
                    }
                }

            }
        }
        //Opportunistic way
        //Get the class where the method is located through reflection. After getting the class, you still get the name of the class
        //Then call toLowerFirstCase to get beanName
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName),new Object[]{req,resp,params.get("name")[0]});
    }

    @Override
    public void init(ServletConfig config) throws ServletException {

        //Template mode

        //1. Load profile
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2. Scan related classes
        doScanner(contextConfig.getProperty("scanPackage"));
        //3. Initialize the instances of all related classes and put them into the IOC container
        doInstance();
        //4. Complete dependency injection
        doAutowired();
        //5. Initialize HandlerMapping
        initHandlerMapping();

        System.out.println("GP Spring framework is init.");
    }

    private void initHandlerMapping() {
        if(ioc.isEmpty()){ return; }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if(!clazz.isAnnotationPresent(GPController.class)){ continue; }

            String baseUrl = "";
            //Get url configuration of Controller
            if(clazz.isAnnotationPresent(GPRequestMapping.class)){
                GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
                baseUrl = requestMapping.value();
            }

            //Get url configuration of Method
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {

                //Those without RequestMapping annotation are ignored directly
                if(!method.isAnnotationPresent(GPRequestMapping.class)){ continue; }

                //Mapping URL
                GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
               ///demo/query
                //(//demo//query)
                String url = ("/" + baseUrl + "/" + requestMapping.value())
                        .replaceAll("/+", "/");
                handlerMapping.put(url,method);
                System.out.println("Mapped " + url + "," + method);
            }
        }


    }

    private void doAutowired() {
        if(ioc.isEmpty()){ return; }
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //Get all the properties in the instance object
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if(!field.isAnnotationPresent(GPAutowired.class)){ continue; }
                GPAutowired autowired = field.getAnnotation(GPAutowired.class);
                String beanName = autowired.value().trim();
                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }
                //Whether you like it or not, kiss hard
                field.setAccessible(true); //Set access to private properties
                try {
                    //Execute injection action
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                    continue ;
                }
            }
        }
    }

    //Control reversal process
    //Factory mode
    private void doInstance() {
        if(classNames.isEmpty()){return;}

        try {
            for (String className : classNames) {
                Class<?> clazz = Class.forName(className);

                if(clazz.isAnnotationPresent(GPController.class)) {
                    Object instance = clazz.newInstance();
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName, instance);
                }else if(clazz.isAnnotationPresent(GPService.class)){
                    //1. The default class name is lowercase

                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    //2. Custom naming
                    GPService service = clazz.getAnnotation(GPService.class);
                    if(!"".equals(service.value())){
                        beanName = service.value();
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                    //3. Inject implementation classes according to types in an opportunistic way
                    for (Class<?> i : clazz.getInterfaces()) {
                        if(ioc.containsKey(i.getName())){
                            throw new Exception("The beanName is exists!!");
                        }
                        ioc.put(i.getName(),instance);
                    }
                }else {
                    continue;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private String toLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
        chars[0] += 32;
        return  String.valueOf(chars);
    }

    private void doScanner(String scanPackage) {
        //All classes under the package are scanned in
        URL url = this.getClass().getClassLoader()
                .getResource("/" + scanPackage.replaceAll("\\.","/"));
        File classPath = new File(url.getFile());

        for (File file : classPath.listFiles()) {
            if(file.isDirectory()){
                doScanner(scanPackage + "." + file.getName());
            }else {
                if(!file.getName().endsWith(".class")){ continue; }
                String className = (scanPackage + "." + file.getName()).replace(".class","");
                classNames.add(className);
            }
        }

    }

    private void doLoadConfig(String contextConfigLocation) {
        InputStream fis = null;
        try {
            fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
            //1. Read configuration file
            contextConfig.load(fis);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try {
                if(null != fis){fis.close();}
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Topics: Spring Spring Boot