天天看點

手撸代碼實作Spring基本功能,實作IOC,DI,AOP

緻謝:首先感謝Tom老師,看了Tom老師的視訊後,對Spring的實作思路有了更清晰的認識。

首先先總結一下實作Spring功能的一個整體思路。

實作Spring基本需要經過一下步驟:

配置階段–>初始化階段–>運作階段

手撸代碼實作Spring基本功能,實作IOC,DI,AOP

大緻流程了解之後我們再細化每個階段。

1、配置階段:

我們需要在web.xml中配置Servlet,init-param,url-pattern,以及建立自定義注解(實作Spring過程中,不需要引入Spring依賴,注解也需要自己自定義)

2、初始化階段:

配置階段完成之後,我們需要在初始畫階段做以下處理:

  • 讀取配置檔案
  • 初始化IOC容器
  • 掃描相關的類
  • 執行個體化相關的類,并儲存到IOC容器中
  • 實作依賴注入
  • 初始化HandlerMapping

3、運作階段

使用者請求後調用doGet/doPost方法,

拿到請求位址,取出requestMapping路徑,

拿resquestMapping路徑與HandlerMapping比對,

反射調用method.invoke(),

傳回前端

手撸代碼實作Spring基本功能,實作IOC,DI,AOP

代碼區:

配置階段:

1、配置application.properties

#要掃描的包#
scanPackage=com.nxw.demo
           

2、配置web.xml

<servlet>
        <servlet-name>MySpring</servlet-name>
        <servlet-class>com.nxw.spring.webmvc.NDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MySpring</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
           

3、自定義注解

/**
 * Cntroller注解
 * @author Silence
 */

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

}

           
/**
 * 業務邏輯注入注解
 * @author Silence
 */

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

           
/**
 * 請求url
 * @author Silence
 */

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

           
/**
 *  請求參數映射
 * @author Silence
 *
 */

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

           
/**
 * 自動注入注解
 * @author Silence
 */

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

           

初始化階段:

1、讀取配置檔案

private void doLoadConfig(String contextConfigLocation) {
        //classpath下找到application.properties配置檔案,并且讀取出來
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:",""));
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            System.err.println("沒有找到配置檔案");
            e.printStackTrace();

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

2、初始化IOC容器 就是建立一個map

private Map<String,Object> ioc = new HashMap<String, Object>();
           

3、掃描相關的類

private void doScanner(String scanPackage) {
        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 {
                //判斷是不是以.class結尾的檔案
                if(!file.getName().endsWith(".class")) {continue;}
                //拼接全類名,可以通過Class.forName()
                String className = (scanPackage + "." + file.getName().replace(".class", ""));
                //存入list
                classNames.add(className);
            }
        }
    }
           

4、執行個體化相關的類,并且緩存到IOC容器中

private void doInstance() {
        //掃描剛剛掃描的類
        if(classNames.isEmpty()){return;}
        for (String className : classNames) {
            try {
                Class clazz = Class.forName(className);

                if(clazz.isAnnotationPresent(NController.class)) {
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                }else if(clazz.isAnnotationPresent(NService.class)){
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    //自定義beanName,不同包下相同類名
                    NService service = (NService) clazz.getAnnotation(NService.class);
                    if(!"".equals(service.value())){
                        beanName = service.value();
                    }

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

                    //如果是接口,用它的實作類指派
                    for (Class i : clazz.getInterfaces()) {
                        if (ioc.containsKey(i.getName())){
                            throw new Exception("This beanName already exists!");
                        }
                        //比對接口類型
                        ioc.put(i.getName(),instance);
                    }

                }else{
                    continue;
                }

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

5、完成依賴注入

private void doAutowrited() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //把所有的private,public,default,prodected都拿到
            Field fields [] = entry.getValue().getClass().getDeclaredFields();

            for (Field field : fields) {
                if(!field.isAnnotationPresent(NAutowired.class)){
                    continue;
                }
                NAutowired autowired = field.getAnnotation(NAutowired.class);
                String beanName = autowired.value().trim();
                if("".equals(beanName.trim())){
                    beanName = field.getType().getName();
                }

                //暴力通路,可以通過反射拿到所有的private,public,default,prodected
                field.setAccessible(true);

                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
   }
           

6、初始化HandlerMapping

private void doHandlerMapper() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();

            if(!clazz.isAnnotationPresent(NController.class)){continue;}

            String baseUrl = null;
            if(clazz.isAnnotationPresent(NRequestMapping.class)){
                NRequestMapping requestMapping = clazz.getAnnotation(NRequestMapping.class);
                baseUrl = requestMapping.value();

            }

            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(NRequestMapping.class)){continue;}
                NRequestMapping requestMapping = method.getAnnotation(NRequestMapping.class);
                String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
                handleMapping.put(url,method);
                System.out.println("Mapping : " + url + " , "+method);
            }
        }
    }
           

運作階段:

doPost()

//7、調用具體的方法
        String url = req.getRequestURL().toString();
        String contextPath = req.getContextPath();
        String url1 = url.replaceAll("http://localhost:8080","");

        System.out.println("URL : "+url);
        if(!this.handleMapping.containsKey(url1)){
            resp.getWriter().write("404 Not Found!");
            return;
        }

        Map<String,String []> params = req.getParameterMap();
        Method method = this.handleMapping.get(url1);
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName),new Object[] {req,resp,params.get("name")[0],params.get("addr")[0]});
           

至此Spring基本功能已經實作。

源碼請到 https://download.csdn.net/download/nxw_tsp/12646030 下載下傳