手写自己的Spring Ioc

笔者从事开发一年,对spring仍然停留在使用阶段,近来打算深入了解spring ioc原理。下文自己实现的spring ioc是利用了servlet,但只是皮毛,真正的spring ioc有着对继承、设计模式的经典复杂的设计。但是在深入源码了解spring ioc原理之前,本文中有助于对ioc原理的理解

目录结构

 

1、首先引入依赖

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.3.0.RELEASE</version>
    </dependency>
</dependencies>

2、实现自己的注解 

package com.study.myspring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutoWried {
    String value() default  "";
}
package com.study.myspring.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
    String value() default "";
}
package com.study.myspring.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
    String value() default "";
}
package com.study.myspring.annotation;

import java.lang.annotation.*;

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

3、重写servlet

package com.study.myspring.servlet;

import com.study.myspring.annotation.MyAutoWried;
import com.study.myspring.annotation.MyController;
import com.study.myspring.annotation.MyRequestMapping;
import com.study.myspring.annotation.MyService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

//urlPatterns不能使用"/"
@WebServlet(name="myDispatchServlet",urlPatterns = {"/test/*"})
public class MyDispatchServlet extends HttpServlet {

    //属性配置文件
    private Properties contextConfig = new Properties();
    private List<String> classNameList = new ArrayList<String>();

    //ioc注册表
    Map<String,Object> iocMap = new HashMap<String,Object>();
    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 {

        //7、运行阶段
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception Detail:\n" + Arrays.toString(e.getStackTrace()));
        }

    }

    private void doDispatch(HttpServletRequest req,HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        System.out.println("[INFO-7] request url-->" + url);

        if(!this.handlerMapping.containsKey(url)){
            try {
                resp.getWriter().write("404 NOT FOUND!");
                return;
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        Method method = this.handlerMapping.get(url);
        System.out.println("[INFO-7] method-->" + method);

        String beanName = (method.getDeclaringClass().getSimpleName());

        System.out.println("[INFO-7] iocMap.get(beanName)->" + iocMap.get(beanName));

        // 第一个参数是获取方法,后面是参数,多个参数直接加,按顺序对应
        method.invoke(iocMap.get(beanName), req, resp);

        System.out.println("[INFO-7] method.invoke put {" + iocMap.get(beanName) + "}.");
    }

    @Override
    public void init(){
        //加载配置文件
        System.out.println("实例化");

        //扫描相关的类(偷懒了直接写死包名)
        doScanner("com.study");

        //3、初始化 IOC 容器,将所有相关的类实例保存到 IOC 容器中
        doInstane();

        //4、依赖注入
        doAutoWired();

        //5、初始化 HandlerMapping
        initHandlerMapping();

        System.out.println("XSpring FrameWork is init.");

        //6、打印数据
        doTestPrintData();
    }

    private void doLoadConfig(String contextConfigLocation){
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(inputStream);
            System.out.println("[INFO-1] property file has been saved in contextConfig.");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doScanner(String scanPackage){
        URL resourcePath = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.", "/"));
        if(resourcePath == null){
            return;
        }

        File classPath = new File(resourcePath.getFile());

        for(File file : classPath.listFiles()){
            if(file.isDirectory()){
                System.out.println("[INFO-2] {" + file.getName() + "} is a directory.");

                // 子目录递归
                doScanner(scanPackage + "." + file.getName());
            }else {

                if (!file.getName().endsWith(".class")) {
                    System.out.println("[INFO-2] {" + file.getName() + "} is not a class file.");
                    continue;
                }

                String className = (scanPackage + "." + file.getName()).replace(".class", "");

                // 保存在内容
                classNameList.add(className);

                System.out.println("[INFO-2] {" + className + "} has been saved in classNameList.");
            }
        }
    }

    private void doInstane(){
        if(classNameList.isEmpty()){
            return;
        }

        try{
            for(String className : classNameList){
                Class<?> aClass = Class.forName(className);
                if(aClass.isAnnotationPresent(MyController.class)){
                    String beanName = (aClass.getSimpleName());
                    Object object = aClass.newInstance();
                    System.out.println(beanName+object.getClass().isAnnotationPresent(MyController.class));
                    iocMap.put(beanName,object);
                    System.out.println(beanName+iocMap.get(beanName).getClass().isAnnotationPresent(MyController.class));
                    System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap.");
                }else if(aClass.isAnnotationPresent(MyService.class)){
                    MyService annotation = aClass.getAnnotation(MyService.class);
                    String beanName = (aClass.getSimpleName());
                    if(!annotation.value().equals("")){
                        beanName = annotation.value();
                    }
                    Object object = aClass.newInstance();
                    iocMap.put(beanName,object);
                    for(Class i : aClass.getInterfaces()){
                        if(iocMap.containsKey(i.getName())){
                            throw new Exception("The bean is exit");
                        }
                        iocMap.put(i.getName(),object);
                        System.out.println("[INFO-3] {" + i.getName() + "} has been saved in iocMap.");
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void doAutoWired(){
        if(iocMap.isEmpty()){
            return;
        }
        for(Map.Entry<String,Object> entry : iocMap.entrySet()){
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for(Field field :fields){
                //field.getAnnotation(MyAutoWried.class);
                if(field.isAnnotationPresent(MyAutoWried.class)){
                    MyAutoWried annotation = field.getAnnotation(MyAutoWried.class);
                    String beanName = annotation.value().trim();
                    if(beanName.equals("")){
                        beanName = field.getType().getName();
                    }
                    field.setAccessible(true);
                    try {
                        field.set(entry.getValue(),iocMap.get(beanName));
                        System.out.println("[INFO-4] field set {" + entry.getValue() + "} - {" + iocMap.get(beanName) + "}.");
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }

    private void initHandlerMapping(){
        if(iocMap.isEmpty()){
            return;
        }
        for(Map.Entry<String,Object> entry : iocMap.entrySet()){
            //MyService annotation1 = entry.getClass().getAnnotation(MyService.class);
            System.out.println(entry.getKey()+entry.getClass().isAnnotationPresent(MyController.class));
            if(!entry.getValue().getClass().isAnnotationPresent(MyController.class)){
                continue;
            }
            String baseUrl = "";
            if(entry.getValue().getClass().isAnnotationPresent(MyRequestMapping.class)){
                MyRequestMapping annotation = entry.getValue().getClass().getAnnotation(MyRequestMapping.class);
                baseUrl = annotation.value();
            }

            for(Method method : entry.getValue().getClass().getDeclaredMethods()){
                if (method.isAnnotationPresent(MyRequestMapping.class)){
                    MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                    baseUrl=baseUrl+annotation.value();
                }
                handlerMapping.put(baseUrl, method);
                System.out.println("[INFO-5] handlerMapping put {" + baseUrl + "} - {" + method + "}.");
            }
        }
    }


    private void doTestPrintData() {

        System.out.println("[INFO-6]----data------------------------");

        System.out.println("contextConfig.propertyNames()-->" + contextConfig.propertyNames());

        System.out.println("[classNameList]-->");
        for (String str : classNameList) {
            System.out.println(str);
        }

        System.out.println("[iocMap]-->");
        for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
            System.out.println(entry);
        }

        System.out.println("[handlerMapping]-->");
        for (Map.Entry<String, Method> entry : handlerMapping.entrySet()) {
            System.out.println(entry);
        }

        System.out.println("[INFO-6]----done-----------------------");

        System.out.println("====启动成功====");
        System.out.println("测试地址:http://localhost:8080/test/listClassName");
    }


}

4、定义Service接口类

package com.study.service;

public interface ITestMyService {
    String listClassName();
}

5、实现Service接口类

package com.study.service.impl;

import com.study.myspring.annotation.MyService;
import com.study.service.ITestMyService;

@MyService
public class TestMyServiceImpl implements ITestMyService {
    public String listClassName() {
        return "1234";
    }
}

6、实现Controller类

package com.study.controller;

import com.study.myspring.annotation.MyAutoWried;
import com.study.myspring.annotation.MyController;
import com.study.myspring.annotation.MyRequestMapping;
import com.study.service.ITestMyService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@MyController
@MyRequestMapping("/test")
public class TestMyController {
    @MyAutoWried
    ITestMyService testMyService;

    @MyRequestMapping("/listClassName")
    public void listClassName(HttpServletRequest req, HttpServletResponse resp) {
        String str = testMyService.listClassName();
        System.out.println("testXService----------=-=-=>" + str);
        try {
            resp.getWriter().write(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

7、使用spring boot启动我们的servlet

package com.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@EnableAutoConfiguration
@SpringBootApplication
//@ServletComponentScan会扫描包下我们定义的servlet
@ServletComponentScan("com.study.myspring.servlet")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

8、运行结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值