天天看點

手撕Spring----Spring源碼解讀-IOC、DI

一、架構解讀:

手撕Spring----Spring源碼解讀-IOC、DI

二、源碼:

手撕Spring----Spring源碼解讀-IOC、DI
#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">
  <display-name>Jason Web Application</display-name>
  <servlet>
    <servlet-name>jasonmvc</servlet-name>
    <servlet-class>com.jason.spring.framework.webmvc.JasonDispatchServlet</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>jasonmvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>
           
package com.jason.spring.framework.webmvc;

import com.jason.spring.framework.annoation.*;

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.*;

/**
 * @program: Jason-spring-1.0-ioc
 * @description
 * @author: 大齡程式猿
 * @create: 2020-04-10 20:47
 **/
public class JasonDispatchServlet  extends HttpServlet {
    private Properties  contextConfig=new Properties();
    public List<String> classNameList=new ArrayList<>();
    public Map<String,Object> ioc=new HashMap<>();
    public Map<String,Method> handleMapping=new HashMap<>();
    @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 {
        //6、委派  根據URL隐射對應的handleMapping
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception,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(!handleMapping.containsKey(url)){
                  resp.getWriter().write("404 Not Found!!!");
                  return;
           }
           Map<String,String[]> params=req.getParameterMap();
           Method method=handleMapping.get(url);

        //擷取形參清單
        Class<?> [] parameterTypes = method.getParameterTypes();
        Object [] paramValues = new Object[parameterTypes.length];

        for (int i = 0; i < parameterTypes.length; i++) {
            Class paramterType = parameterTypes[i];
            if(paramterType == HttpServletRequest.class){
                paramValues[i] = req;
            }else if(paramterType == HttpServletResponse.class){
                paramValues[i] = resp;
            }else if(paramterType == String.class){
                //通過運作時的狀态去拿到你
                Annotation[] [] pa = method.getParameterAnnotations();
                for (int j = 0; j < pa.length ; j ++) {
                    for(Annotation a : pa[i]){
                        if(a instanceof JasonRequestParam){
                            String paramName = ((JasonRequestParam) a).value();
                            if(!"".equals(paramName.trim())){
                                String value = Arrays.toString(params.get(paramName))
                                        .replaceAll("\\[|\\]","")
                                        .replaceAll("\\s+",",");
                                paramValues[i] = value;
                            }
                        }
                    }
                }

            }
        }


        //暫時寫死
        String beanName = toLowerFirsCase(method.getDeclaringClass().getSimpleName());
        //指派實參清單
        System.out.println("ioc.get("+beanName+")="+ioc.get(beanName));
        System.out.println("paramValues="+paramValues);

        method.invoke(ioc.get(beanName),paramValues);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("Jason Spring framework init start!");

        //1.加載配置檔案
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        //2、掃描相關的類
        doScanner(contextConfig.getProperty("scanPackage"));

        /**********************IOC*******************************/
        //3、初始化IOC容器,将掃描到的類相關執行個體,儲存到IOC容器
        doInstance();
        /**********************AOP*******************************/
        //新生成的代理對象,需要在DI之前

        /**********************DI*******************************/
        //4、完成依賴注入
        doAutoWired();

        /**********************MVC*******************************/
        //5、初始化handlerMapping
        doInitHandlerMapping();

        System.out.println("Jason Spring framework init finished!");
    }

    private void doInitHandlerMapping() {
        if(ioc.isEmpty())
        {
            return;
        }
        //隻取public方法
        for(Map.Entry entry:ioc.entrySet())
        {
            Class<?> clazz=entry.getValue().getClass();
            if(!clazz.isAnnotationPresent(JasonController.class))
            {
                continue;
            }
            String baseUrl="";
            if(clazz.isAnnotationPresent(JasonRequestMapping.class))
            {
                baseUrl=clazz.getAnnotation(JasonRequestMapping.class).value().trim();
            }
            for(Method method:clazz.getMethods())
            {
               if(!method.isAnnotationPresent(JasonRequestMapping.class))
               {
                   continue;
               }
               JasonRequestMapping  jasonRequestMapping=method.getAnnotation(JasonRequestMapping.class);
                String url = ("/" + baseUrl + "/" + jasonRequestMapping.value()).replaceAll("/+","/");
                handleMapping.put(url,method);
                System.out.println("初始化handleMapping方法隐射:"+url+"---"+method);
            }
        }
    }

    private void doAutoWired() {
        if(ioc.isEmpty())
        {
            return;
        }
        for(Map.Entry<String,Object> entry:ioc.entrySet())
        {
            //擷取private,public,default所有的屬性
            for(Field field:entry.getValue().getClass().getDeclaredFields())
            {
                if(!field.isAnnotationPresent(JasonAutowired.class))
                {
                    continue;
                }
                String beanName=field.getAnnotation(JasonAutowired.class).value();

                if("".equals(beanName.trim()))
                {
                    beanName=field.getType().getName();
                }
                System.out.println("doAutoWired="+beanName);
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                    System.out.println("ioc.get("+beanName+")="+ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doInstance() {
        if(classNameList.isEmpty()){
            return;
        }
        for(String className:classNameList)
        {
            try
            {
                Class<?>  clazz=Class.forName(className);

                if(clazz.isAnnotationPresent(JasonController.class))
                {
                    Object  obj=clazz.newInstance();
                    String beanName=toLowerFirsCase(clazz.getSimpleName());
                    ioc.put(beanName,obj);
                }else if(clazz.isAnnotationPresent(JasonService.class))
                {
                    //1.在多個包路徑下,同名service,隻能自己起一個全局唯一的名稱@JasonService("AService")  @JasonService("BService")
                    String  beanName=clazz.getAnnotation(JasonService.class).value();
                    if("".equals(beanName.trim()))
                    {
                        //2.預設的類首字母小寫
                        beanName=toLowerFirsCase(clazz.getSimpleName());
                    }

                    Object  obj=clazz.newInstance();
                    ioc.put(beanName,obj);

                    //3.如果是接口,判斷有多少個實作類,如果有多個實作類,則抛出異常。如果隻有一個,則預設就是實作類。
                    for(Class<?> i:clazz.getInterfaces())
                    {
                        if(ioc.containsKey(i.getName()))
                        {
                            throw  new Exception("The "+i.getName()+" is Exists!!");
                        }
                        ioc.put(i.getName(),obj);
                    }

                }else
                {
                    continue;
                }

            }catch (Exception e)
            {
                e.printStackTrace();
            }

        }
        System.out.println("IOC容器初始化結果:"+ioc);


    }

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

    private void doScanner(String scanPackage) {
        URL  url=this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.","/"));
        System.out.println("url.getFile="+url.getFile());
        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;}
                classNameList.add(scanPackage+"."+file.getName().replace(".class",""));
            }
        }


    }

    private void doLoadConfig(String contextConfigLocation) {
        System.out.println("contextConfigLocation="+contextConfigLocation);
        InputStream inputStream=this.getClass().getClassLoader().getResourceAsStream("application.properties");
        System.out.println("inputStream="+inputStream);
        try {
            contextConfig.load(inputStream);
            System.out.println("初始化,讀取配置檔案:--------------"+contextConfig.getProperty("scanPackage"));

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(inputStream!=null)
                    inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}
           

三、測試類:

package com.jason.demo.action;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jason.demo.service.IDemoService;
import com.jason.spring.framework.annoation.JasonAutowired;
import com.jason.spring.framework.annoation.JasonController;
import com.jason.spring.framework.annoation.JasonRequestMapping;
import com.jason.spring.framework.annoation.JasonRequestParam;


//雖然,用法一樣,但是沒有功能
@JasonController
@JasonRequestMapping("/demo")
public class DemoAction {

  	@JasonAutowired
	private IDemoService demoService;

	@JasonRequestMapping("/query")
	public void query(HttpServletRequest req, HttpServletResponse resp,
					  @JasonRequestParam("name") String name){
		System.out.println("---------------------"+demoService);
		String result = demoService.get(name);
		System.out.println(result);
		try {
			resp.getWriter().write(result);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

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

	@JasonRequestMapping("/sub")
	public void add(HttpServletRequest req, HttpServletResponse resp,
					@JasonRequestParam("a") Double a, @JasonRequestParam("b") Double b){
		try {
			resp.getWriter().write(a + "-" + b + "=" + (a - b));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@JasonRequestMapping("/remove")
	public String  remove(@JasonRequestParam("id") Integer id){
		return "" + id;
	}

}
           

四、浏覽器測試:

手撕Spring----Spring源碼解讀-IOC、DI